Guida per sviluppatori di JMS 2.0 del bus di servizio di Azure

Questa guida fornisce informazioni dettagliate che consentono di comunicare con bus di servizio di Azure usando l'API JMS (Java Message Service) 2.0.

In qualità di sviluppatore Java, se sei nuovo al bus di servizio di Azure, considera la lettura degli articoli seguenti.

Come iniziare Concetti

Modello di programmazione JMS (Message Service) Java

Il modello di programmazione api del servizio messaggi Java è descritto nelle sezioni seguenti:

Annotazioni

Il livello Premium del bus di servizio di Azure supporta JMS 1.1 e JMS 2.0.

Bus di servizio di Azure: il livello Standard supporta funzionalità JMS 1.1 limitate. Per altri dettagli, vedere questa documentazione.

JMS - Blocchi predefiniti

Usare i blocchi predefiniti seguenti per comunicare con l'applicazione JMS.

Annotazioni

Questa guida è adattata dal tutorial di Oracle Java EE 6 per Java Message Service (JMS).

Per una migliore comprensione dell'Java Message Service (JMS), vedere questa esercitazione.

Factory di connessione

Annotazioni

La azure-servicebus-jms libreria è disponibile in due varianti: com.azure:azure-servicebus-jms (versione 2.0.0+) per Jakarta EE () e jakarta.jms.* (com.microsoft.azure:azure-servicebus-jmsversione 1.0.x) per Java EE (javax.jms.*). Per ottenere indicazioni sulla scelta del corretto artefatto, vedere Supporto di Jakarta EE e javax.

Il client usa l'oggetto connection factory per connettersi al provider JMS. La fabbrica di connessione incapsula un insieme di parametri configurativi della connessione definiti dall'amministratore.

Ogni factory di connessione è un'istanza dell'interfaccia ConnectionFactory, QueueConnectionFactory o TopicConnectionFactory.

Per semplificare la connessione a bus di servizio di Azure, queste interfacce vengono implementate rispettivamente tramite ServiceBusJmsConnectionFactory, ServiceBusJmsQueueConnectionFactory o ServiceBusJmsTopicConnectionFactory.

Importante

Le applicazioni Java che usano l'API JMS 2.0 possono connettersi a bus di servizio di Azure utilizzando la stringa di connessione o un TokenCredential per sfruttare l'autenticazione supportata da Microsoft Entra. Quando si usa l'autenticazione con supporto di Microsoft Entra, assicurarsi di assegnare ruoli e permessi all'identità in base alle esigenze.

Creare un'identità gestita assegnata dal sistema in Azure e usare questa identità per creare un oggetto TokenCredential.

TokenCredential tokenCredential = new DefaultAzureCredentialBuilder().build();

È possibile creare un'istanza della connection factory con i parametri seguenti:

  • Credenziale token: rappresenta una credenziale in grado di fornire un token OAuth.
  • Host: il nome host dello spazio dei nomi del livello Premium di bus di servizio di Azure.
  • Contenitore delle proprietà ServiceBusJmsConnectionFactorySettings, che contiene:
    • connectionIdleTimeoutMS: timeout di connessione inattiva in millisecondi.
    • traceFrames: flag booleano per raccogliere frame di traccia AMQP per il debug.
    • Altri parametri di configurazione.

Creare la fabbrica come illustrato nell'esempio seguente. Le credenziali del token e l'host sono parametri obbligatori, ma le altre proprietà sono facoltative.

String host = "<YourNamespaceName>.servicebus.windows.net";
ConnectionFactory factory = new ServiceBusJmsConnectionFactory(tokenCredential, host, null); 

Destinazione JMS

Una destinazione è l'oggetto utilizzato da un client per specificare la destinazione dei messaggi prodotti e l'origine dei messaggi utilizzati.

Le destinazioni eseguono il mapping alle entità nel bus di servizio di Azure: code (in scenari da punto a punto) e argomenti (in scenari pub-sub).

Connessioni

Una connessione incapsula una connessione virtuale con un provider JMS. Utilizzando bus di servizio di Azure, rappresenta una connessione di stato tra l'applicazione e bus di servizio di Azure tramite AMQP.

Creare una connessione dalla "fabbrica di connessione", come illustrato nell'esempio seguente:

Connection connection = factory.createConnection();

Sessioni

Una sessione è un contesto a thread singolo per la produzione e l'utilizzo di messaggi. Usalo per creare messaggi, produttori di messaggi e consumatori. Fornisce anche un contesto transazionale per raggruppare gli invii e le ricevute in un'unità atomica di lavoro.

Creare una sessione dall'oggetto connessione come illustrato nell'esempio seguente:

Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);

Annotazioni

L'API JMS non supporta la ricezione di messaggi da code o argomenti del bus di servizio con sessioni di messaggistica abilitate.

Modalità di sessione

Creare una sessione con una delle modalità seguenti.

Modalità di sessione Comportamento
Session.AUTO_ACKNOWLEDGE La sessione riconosce automaticamente la ricezione di un messaggio da parte di un client quando la sessione torna con successo da una chiamata di 'receive' o quando il message listener che la sessione chiama per elaborare il messaggio restituisce con successo.
Session.CLIENT_ACKNOWLEDGE Il client riconosce un messaggio utilizzato chiamando il metodo di conferma del messaggio.
Session.DUPS_OK_ACKNOWLEDGE Questa modalità di riconoscimento indica alla sessione di riconoscere pigramente la consegna dei messaggi.
Session.SESSION_TRANSACTED Passare questo valore come argomento al metodo createSession(int sessionMode) sull'oggetto Connection per specificare che la sessione deve utilizzare una transazione locale.

Se non si specifica la modalità sessione, il valore predefinito è Session.AUTO_ACKNOWLEDGE.

JMSContext

Annotazioni

JMSContext viene definito come parte della specifica JMS 2.0.

JMSContext combina le funzionalità fornite dagli oggetti di connessione e di sessione. Viene creato dall'oggetto connection factory.

JMSContext context = connectionFactory.createContext();

Modalità JMSContext

Analogamente all'oggetto Session , è possibile creare JMSContext con le stesse modalità di riconoscimento indicate nelle modalità sessione.

JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE);

Se non si specifica una modalità, il valore predefinito è JMSContext.AUTO_ACKNOWLEDGE.

Produttori di messaggi JMS

Un producer di messaggi è un oggetto creato tramite JMSContext o una sessione. Usarlo per inviare messaggi a una destinazione.

È possibile crearlo come oggetto autonomo, come illustrato nell'esempio seguente:

JMSProducer producer = context.createProducer();

In alternativa, è possibile crearlo in fase di esecuzione quando è necessario inviare un messaggio.

context.createProducer().send(destination, message);

Consumer di messaggi JMS

Un consumer di messaggi è un oggetto creato da un JMSContext o da una sessione. Usarlo per ricevere messaggi inviati a una destinazione. Crearlo come illustrato nell'esempio seguente:

JMSConsumer consumer = context.createConsumer(dest);

Ricezione sincrona tramite metodo receive()

Il consumer di messaggi fornisce un modo sincrono per ricevere messaggi dalla destinazione tramite il metodo receive().

Se non si specificano argomenti o timeout, o se si specifica un timeout di 0, il consumer si blocca indefinitamente a meno che il messaggio non arrivi o la connessione venga interrotta (qualunque delle due avvenga per prima).

Message m = consumer.receive();
Message m = consumer.receive(0);

Quando si specifica un argomento positivo diverso da zero, il consumer si blocca fino alla scadenza del timer.

Message m = consumer.receive(1000); // time out after one second.

Ricezione asincrona con listener di messaggi JMS

Un listener di messaggi è un oggetto usato per la gestione asincrona dei messaggi in una destinazione. Implementa l'interfaccia MessageListener, che contiene il metodo onMessage in cui deve risiedere la logica di business specifica.

È necessario creare un'istanza di un oggetto listener di messaggi e registrarlo per un consumer di messaggi specifico usando il metodo setMessageListener.

Listener myListener = new Listener();
consumer.setMessageListener(myListener);

Utilizzo da argomenti

Creare consumer di messaggi JMS su una destinazione, che può essere una coda o un argomento.

I consumer nelle code sono semplicemente oggetti lato client che risiedono nel contesto della sessione (e connessione) tra l'applicazione client e il bus di servizio di Azure.

I consumer negli argomenti, tuttavia, hanno due parti:

  • Oggetto lato client che risiede nel contesto della sessione (o JMSContext) e
  • Una sottoscrizione che è un'entità nell'bus di servizio di Azure.

Le sottoscrizioni sono documentate qui e possono essere uno dei tipi seguenti:

  • Sottoscrizioni durevoli condivise
  • Sottoscrizioni non durevoli condivise
  • Sottoscrizioni durevoli non condivise
  • Sottoscrizioni non durevoli non condivise

Browser di accodamento JMS

L'API JMS fornisce un QueueBrowser oggetto che l'applicazione può usare per esplorare i messaggi nella coda e visualizzare i valori di intestazione per ogni messaggio.

È possibile creare un browser di coda usando JMSContext, come illustrato nell'esempio seguente.

QueueBrowser browser = context.createBrowser(queue);

Annotazioni

L'API JMS non fornisce un'API per esplorare un argomento.

Questa limitazione esiste perché l'argomento stesso non archivia i messaggi. Non appena il messaggio viene inviato al topic, viene inoltrato alle sottoscrizioni appropriate.

Selettori di messaggi JMS

Le applicazioni riceventi possono usare selettori di messaggi per filtrare i messaggi che ricevono. Usando i selettori di messaggio, l'applicazione ricevente scarica il lavoro di filtro dei messaggi al provider JMS (in questo caso, bus di servizio di Azure) invece di assumerne la responsabilità stessa.

È possibile usare i selettori durante la creazione di uno dei consumer seguenti:

  • Sottoscrizione durevole condivisa
  • Sottoscrizione durevole non condivisa
  • Sottoscrizione non durevole condivisa
  • Sottoscrizione non condivisa non durevole
  • Consumer della coda
  • Browser di accodamento

Annotazioni

I selettori del bus di servizio non supportano le parole chiave SQL LIKE e BETWEEN.

Messaggi pianificati (ritardo di recapito)

JMS 2.0 supporta la pianificazione di un messaggio per il recapito futuro usando il metodo setDeliveryDelay su MessageProducer o JMSProducer. Quando si imposta questa proprietà, bus di servizio accetta il messaggio, ma lo rende visibile solo ai consumer dopo il periodo di ritardo trascorso.

MessageProducer producer = session.createProducer(queue);

// Schedule a message for delivery 30 seconds from now
producer.setDeliveryDelay(30000);
producer.send(session.createTextMessage("Scheduled message"));

Per un esempio funzionante completo, vedere QueueScheduledSend.java nel repository azure-servicebus-jms-samples.

Selezione e resilienza del gestore di connessione

Quando si usa ServiceBusJmsConnectionFactory in Spring Boot o in altri framework che gestiscono connessioni JMS, scegliere il wrapper di factory di connessione corretto per sender e listener per assicurare il funzionamento affidabile.

Role Factory di connessione Perché
Mittenti (JmsTemplate) Wrapping di CachingConnectionFactory per ServiceBusJmsConnectionFactory JmsTemplate crea e chiude una connessione per ogni invio per impostazione predefinita. CachingConnectionFactory gestisce una singola connessione AMQP e memorizza nella cache le sessioni, evitando la varianza della connessione che può esaurire le risorse broker sotto carico.
Listener (@JmsListener, DefaultMessageListenerContainer) ServiceBusJmsConnectionFactory raw (unwrapped) Ogni contenitore del listener ottiene la propria connessione AMQP con ciclo di vita indipendente. Se una connessione non riesce (scadenza del token, aggiornamento del gateway, blip di rete), viene interessato solo il listener e Spring ricrea automaticamente la connessione.

Cosa evitare per gli ascoltatori

Avvertimento

Non usare mai SingleConnectionFactory con i contenitori del listener. Forza tutti i listener a condividere una singola connessione JMS. Se la connessione viene interrotta per qualsiasi motivo, tutti i listener perdono la connettività contemporaneamente e non possono essere ripristinati in modo indipendente. Usare il codice non elaborato ServiceBusJmsConnectionFactory in modo che ogni contenitore del listener gestisca la propria connessione.

CachingConnectionFactory nei contenitori listener possono anche causare problemi perché le sessioni memorizzate nella cache potrebbero fare riferimento a una connessione sottostante non aggiornata. Per i listener, la factory raw assicura che ogni contenitore possa creare una nuova connessione in modo indipendente.

Impostazioni predefinite di Spring Cloud Azure

Se si usa spring-cloud-azure-starter-servicebus-jms (versione 6.2.0+), lo starter applica questa separazione di factory per impostazione predefinita:

spring.jms.servicebus.pool.enabled spring.jms.cache.enabled Fabbrica del mittente Factory di listener
(non impostato) (non impostato) CachingConnectionFactory ServiceBusJmsConnectionFactory
(non impostato) true CachingConnectionFactory CachingConnectionFactory
(non impostato) false ServiceBusJmsConnectionFactory ServiceBusJmsConnectionFactory
true (non impostato) JmsPoolConnectionFactory JmsPoolConnectionFactory

Nelle versioni precedenti (pre-6.2.0), sia i mittenti che i listener usano ServiceBusJmsConnectionFactory per impostazione predefinita, in modo che i mittenti creino una nuova connessione per invio.

Aggiunta di un listener di eccezioni

Senza un listener di eccezioni, le interruzioni della connessione sono completamente invisibili all'utente. Aggiungere jakarta.jms.ExceptionListener alle factory di sender e listener per l'osservabilità:

connection.setExceptionListener(exception -> {
    log.error("JMS connection error: {}", exception.getMessage(), exception);
});

In Spring Boot, configurare il listener di eccezioni in CachingConnectionFactory (per i sender) e in DefaultJmsListenerContainerFactory (per i listener).

Per un esempio funzionante completo che mostra tutti questi modelli, vedere l'esempio Spring Boot JMS Resilience nel repository azure-servicebus-jms-samples.

Code di messaggi non recapitabili

Ogni sottoscrizione dell'argomento e della coda nel bus di servizio di Azure ha una coda di messaggi non recapitabili (DLQ) associata. Il sistema sposta automaticamente i messaggi che non è in grado di recapitare o elaborare nella DLQ. Ad esempio, il sistema sposta un messaggio nella DLQ quando il messaggio supera il numero massimo di recapiti o la durata (TTL).

Importante

Per spostare i messaggi TTL scaduti nella DLQ, abilitare l'invio di messaggi non recapitabili alla scadenza del messaggio per la coda o la sottoscrizione. Senza questa impostazione, il sistema elimina automaticamente i messaggi scaduti. Per i passaggi di configurazione, vedere Abilitare l'invio di messaggi non recapitabili per una coda o una sottoscrizione.

In JMS si accede alla DLQ come destinazione separata costruendo il percorso completo e creando un oggetto JmsQueue con esso. Non è necessaria alcuna API speciale.

Formato del percorso DLQ della coda:

<queue-name>/$deadletterqueue

Formato del percorso DLQ della sottoscrizione dell'argomento:

<topic-name>/Subscriptions/<subscription-name>/$deadletterqueue

Esempio: utilizzo dalla coda di messaggi non recapitabili di una coda:

import org.apache.qpid.jms.JmsQueue;

// Construct the DLQ path for a queue named "orders"
String dlqPath = "orders/$deadletterqueue";
JmsQueue dlqDestination = new JmsQueue(dlqPath);

// Create a consumer on the DLQ and receive messages
MessageConsumer dlqConsumer = session.createConsumer(dlqDestination);
Message message = dlqConsumer.receive(5000);

I messaggi non recapitabili includono proprietà di metadati che descrivono perché i messaggi non sono recapitabili.

Proprietà Description
DeadLetterReason Motivo per cui il messaggio è finito nella lista dei messaggi non recapitabili (ad esempio, TTLExpiredException o MaxDeliveryCountExceeded).
DeadLetterErrorDescription Descrizione leggibile del motivo dei messaggi non recapitabili.

Leggere queste proprietà usando message.getStringProperty():

String reason = message.getStringProperty("DeadLetterReason");
String description = message.getStringProperty("DeadLetterErrorDescription");

Per un esempio di lavoro completo, vedere QueueDeadLetterReceive.java nel repository azure-servicebus-jms-samples.

Disposizione AMQP e mappatura delle operazioni del bus di servizio

Ecco come si traduce una disposizione AMQP in un'operazione del bus di servizio:

ACCEPTED = 1; -> Complete()
REJECTED = 2; -> DeadLetter()
RELEASED = 3; (just unlock the message in service bus, will then get redelivered)
MODIFIED_FAILED = 4; -> Abandon() which increases delivery count
MODIFIED_FAILED_UNDELIVERABLE = 5; -> Defer()

Riassunto

Questa guida per sviluppatori illustra come Java applicazioni client che usano Java Message Service (JMS) possono connettersi a bus di servizio di Azure.

Passaggi successivi

Per altre informazioni su bus di servizio di Azure e informazioni dettagliate sulle entità di Java Message Service (JMS), vedere gli articoli seguenti: