32. 訊息傳遞
Spring框架為與訊息傳遞系統整合提供了廣泛的支援,從使用JmsTemplate
簡化的JMS API到使用完整的基礎設施非同步接收訊息,Spring AMQP為高階訊息佇列協議提供了類似的特性集。Spring Boot還為RabbitTemplate
和RabbitMQ提供自動配置選項,Spring WebSocket原生包括對STOMP訊息的支援,Spring Boot通過啟動器和少量的自動配置支援這一點,Spring Boot還支援Apache Kafka。
32.1 JMS
javax.jms.ConnectionFactory
介面提供了建立javax.jms.Connection
與JMS代理互動的標準方法,儘管Spring需要一個ConnectionFactory
來處理JMS,你通常不需要自己直接使用它,而是可以依賴於更高階別的訊息傳遞抽象。(詳見Spring Framework參考文件的相關部分。)Spring Boot還自動配置傳送和接收訊息所需的基礎設施。
32.1.1 ActiveMQ支援
當ActiveMQ在類路徑上可用時,Spring Boot還可以配置ConnectionFactory
,如果代理存在,則會自動啟動和配置嵌入式代理(只要沒有通過配置指定代理URL)。
如果你使用
spring-boot-starter-activemq
,那麼將提供連線或嵌入ActiveMQ例項所需的依賴項,與JMS整合的Spring基礎設施也是一樣。
ActiveMQ配置由在spring.activemq.*
中的外部配置屬性控制,例如,你可以在application.properties
中宣告以下部分:
spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret
你還可以通過向org.apache.activemq:activemq-pool
新增一個依賴項來共享JMS資源並相應地配置PooledConnectionFactory
,如下例所示:
spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50
有關更多支援的選項,請參見ActiveMQProperties,你還可以註冊一個任意數量的實現
activemqconnectionfactorycustomzer
的bean進行更高階的自定義。
預設情況下,ActiveMQ建立一個目的地,如果它還不存在,那麼目的地將根據它們提供的名稱解析。
32.1.2 Artemis支援
Spring Boot可以自動配置ConnectionFactory
,當它檢測到在類路徑上可用的Artemis時,如果代理存在,則自動啟動和配置嵌入式代理(除非模式屬性已顯式設定),所支援的模式是embedded
(要明確地說明需要一個嵌入式代理,如果代理在類路徑上不可用,就會出現錯誤)和native
(使用netty
傳輸協議連線到代理),在配置後者時,Spring Boot使用預設設定配置連線到執行在本地機器上的代理的ConnectionFactory
。
如果你使用
spring-boot-starter-artemis
,則提供了連線到現有的Artemis例項的必要依賴項,以及與JMS整合的Spring基礎設施,新增org.apache.activemq:artemis-jms-server
到你的應用程式以允許你使用嵌入式模式。
Artemis配置由spring.artemis.*
的外部配置屬性控制,例如,你可以在application.properties
中宣告以下部分:
spring.artemis.mode=native
spring.artemis.host=192.168.1.210
spring.artemis.port=9876
spring.artemis.user=admin
spring.artemis.password=secret
在嵌入代理時,你可以選擇是否啟用永續性,並列出應該可用的目的地,可以將它們指定為逗號分隔的列表,以使用預設選項建立它們,或者可以定義型別為org.apache.activemq.artemis.jms.server.config.JMSQueueConfiguration
或org.apache.activemq.artemis.jms.server.config.TopicConfiguration
的bean,分別用於高階佇列和主題配置。
有關更多受支援的選項,請參閱ArtemisProperties。
不涉及JNDI查詢,目的地根據它們的名稱進行解析,使用Artemis配置中的name
屬性或通過配置提供的名稱。
32.1.3 使用JNDI ConnectionFactory
如果你正在應用伺服器中執行應用程式,Spring Boot試圖使用JNDI定位JMS ConnectionFactory
,預設情況下,檢查java:/JmsXA
和java:/XAConnectionFactory
位置,如果需要指定替代位置,可以使用spring.jms.jndi-name
屬性,如下例所示:
spring.jms.jndi-name=java:/MyConnectionFactory
32.1.4 傳送訊息
Spring的JmsTemplate是自動配置的,你可以將其自動連線到你自己的bean中,如下面的示例所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final JmsTemplate jmsTemplate;
@Autowired
public MyBean(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
// ...
}
JmsMessagingTemplate可以以類似的方式注入,如果定義了
DestinationResolver
或MessageConverter
bean,則它將自動關聯到自動配置的JmsTemplate
。
32.1.5 接收訊息
當出現JMS基礎設施時,可以使用@JmsListener
註解任何bean,以建立監聽器端點,如果沒有定義JmsListenerContainerFactory
,則會自動配置預設工廠,如果定義了DestinationResolver
或MessageConverter
bean,則它將自動關聯到預設工廠。
預設情況下,預設工廠是事務性的,如果你執行的基礎設施中存在JtaTransactionManager
,那麼它預設與偵聽器容器相關聯,如果沒有,則啟用sessionTransacted
標誌。在後一個場景中,你可以通過在監聽器方法(或委託)上新增@Transactional
,將本地資料儲存事務與接收訊息的處理相關聯,這確保在本地事務完成之後,傳入訊息得到確認,這還包括髮送在相同JMS會話上執行的響應訊息。
以下元件在someQueue
目的地上建立監聽器端點:
@Component
public class MyBean {
@JmsListener(destination = "someQueue")
public void processMessage(String content) {
// ...
}
}
有關更多細節,請參見@EnableJms的Javadoc。
如果你需要建立更多的JmsListenerContainerFactory
例項,或者希望重寫預設的例項,Spring Boot提供了一個DefaultJmsListenerContainerFactoryConfigurer
,你可以使用它來初始化一個DefaultJmsListenerContainerFactory
,其設定與自動配置的工廠相同。
例如,下面的示例公開了另一個使用特定MessageConverter
的工廠:
@Configuration
static class JmsConfiguration {
@Bean
public DefaultJmsListenerContainerFactory myFactory(
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory());
factory.setMessageConverter(myMessageConverter());
return factory;
}
}
然後你可以在任何@JmsListener
註解的方法中使用工廠,如下所示:
32.2 AMQP
高階訊息佇列協議(AMQP)是面向訊息的中介軟體的一種平臺無關的、有線級別的協議。Spring AMQP專案將核心Spring概念應用於基於AMQP的訊息傳遞解決方案的開發,Spring Boot為通過RabbitMQ使用AMQP提供了一些方便,包括spring-boot-starter-amqp
“啟動器”。
32.2.1 RabbitMQ支援
RabbitMQ是一個輕量級的、可靠的、可伸縮的、可移植的訊息代理,基於AMQP協議,Spring使用RabbitMQ
通過AMQP協議進行通訊。
RabbitMQ配置由spring.rabbitmq.*
的外部配置屬性控制,例如,你可以在application.properties
中宣告以下部分:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=secret
如果上下文中存在ConnectionNameStrategy
bean,那麼它將自動用於命名由自動配置的ConnectionFactory
建立的連線。有關更多受支援的選項,請參閱RabbitProperties。
有關詳細資訊,請參閱RabbitMQ使用的協議AMQP
32.2.2 傳送訊息
Spring的AmqpTemplate
和AmqpAdmin
是自動配置的,你可以將它們自動連線到你自己的bean中,如下面的示例所示:
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final AmqpAdmin amqpAdmin;
private final AmqpTemplate amqpTemplate;
@Autowired
public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) {
this.amqpAdmin = amqpAdmin;
this.amqpTemplate = amqpTemplate;
}
// ...
}
RabbitMessagingTemplate可以以類似的方式注入,如果定義了
MessageConverter
bean,它會自動關聯到自動配置的AmqpTemplate
。
如果有必要,任何定義為bean的org.springframework.amqp.core.Queue
自動用於在RabbitMQ例項上宣告相應的佇列。
重試操作,可以對AmqpTemplate
啟用重試(例如,如果代理連線丟失了),預設情況下禁用重試。
32.2.3 接收訊息
當Rabbit基礎設施存在時,可以使用@RabbitListener
對任何bean進行註解,以建立監聽器端點,如果沒有定義RabbitListenerContainerFactory
,則會自動配置預設的SimpleRabbitListenerContainerFactory
,你可以使用spring.rabbitmq.listener.type
屬性切換到直接容器。如果定義了MessageConverter
或MessageRecoverer
bean,則它將自動與預設工廠相關聯。
以下示例元件在someQueue
佇列上建立監聽器端點:
@Component
public class MyBean {
@RabbitListener(queues = "someQueue")
public void processMessage(String content) {
// ...
}
}
有關更多細節,請參見@EnableRabbit的Javadoc。
如果你需要建立更多的RabbitListenerContainerFactory
例項,或者你想要覆蓋預設值,Spring Boot提供了一個SimpleRabbitListenerContainerFactoryConfigurer
和一個DirectRabbitListenerContainerFactoryConfigurer
,你可以使用它們初始化一個SimpleRabbitListenerContainerFactory
,以及一個DirectRabbitListenerContainerFactory
,其設定與自動配置使用的工廠相同。
選擇哪種容器型別並不重要,這兩個bean通過自動配置公開。
例如,下面的configuration類公開另一個使用特定MessageConverter
的工廠:
@Configuration
static class RabbitConfiguration {
@Bean
public SimpleRabbitListenerContainerFactory myFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory =
new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setMessageConverter(myMessageConverter());
return factory;
}
}
然後你可以在任何@RabbitListener
註解的方法中使用工廠,如下所示:
@Component
public class MyBean {
@RabbitListener(queues = "someQueue", containerFactory="myFactory")
public void processMessage(String content) {
// ...
}
}
你可以啟用重試來處理監聽器丟擲異常的情況,預設情況下,使用RejectAndDontRequeueRecoverer
,但是可以定義自己的MessageRecoverer
,當重試結束時,訊息將被拒絕,如果將代理配置為這樣做,則訊息將被刪除或路由到死信交換,預設情況下,重試被禁用。
重要
預設情況下,如果重試被禁用並且監聽器丟擲異常,該遞送被無限期地重試,你可以用兩種方式修改此行為:將defaultRequeueRejected
屬性設定為false
,以便嘗試零重複傳送,或者丟擲AmqpRejectAndDontRequeueException
來通知訊息應該被拒絕,後者是在啟用重試並達到最大提交嘗試次數時使用的機制。
32.3 Apache Kafka支援
通過提供spring-kafka
專案的自動配置來支援Apache Kafka。
Kafka配置由spring.kafka.*
中的外部配置屬性控制,例如,你可以在application.properties
中宣告以下部分:
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup
要在啟動時建立主題,請新增一個型別
NewTopic
的bean,如果主題已經存在,則忽略bean。
有關更多受支援的選項,請參閱KafkaProperties。
32.3.1 傳送訊息
Spring的KafkaTemplate
是自動配置的,你可以直接在你自己的bean中自動連線它,如下面的示例所示:
@Component
public class MyBean {
private final KafkaTemplate kafkaTemplate;
@Autowired
public MyBean(KafkaTemplate kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
// ...
}
如果定義了一個
RecordMessageConverter
bean,它將自動關聯到自動配置的KafkaTemplate
32.3.2 接收訊息
當存在Apache Kafka基礎設施時,任何bean都可以使用@KafkaListener
進行註解,以建立監聽器端點,如果沒有定義KafkaListenerContainerFactory
,預設設定為使用spring.kafka.listener.*
中定義的鍵,此外,如果定義了一個RecordMessageConverter
bean,它將自動關聯到預設的工廠。
@Component
public class MyBean {
@KafkaListener(topics = "someTopic")
public void processMessage(String content) {
// ...
}
}
32.3.3 附加的Kafka屬性
自動配置所支援的屬性顯示在附錄A中,通用的應用程式屬性。注意,在大多數情況下,這些屬性(連字元或駝峰式大小寫)直接對映到Apache Kafka *屬性,有關詳細資訊,請參閱Apache Kafka文件。
前幾個屬性同時適用於生產者和消費者,但是如果你希望對每個屬性使用不同的值,可以在生產者或消費者級別指定,Apache Kafka設計具有高、中或低重要性的屬性,Spring Boot自動配置支援所有重要屬性、一些選定的中屬性和低屬性,以及任何沒有預設值的屬性。
Kafka支援的屬性中只有一部分是可以通過KafkaProperties
類獲得的,如果你希望為生產者或消費者配置不受直接支援的其他屬性,請使用以下屬性:
spring.kafka.properties.prop.one=first
spring.kafka.admin.properties.prop.two=second
spring.kafka.consumer.properties.prop.three=third
spring.kafka.producer.properties.prop.four=fourth
這將設定通用的prop.one
Kafka屬性為first
(適用於生產者、消費者和管理員),prop.two
管理員屬性為second
,prop.three
消費者屬性為third
並且prop.four
生產者屬性為fourth
。
你還可以配置Spring Kafka JsonDeserializer
,如下所示:
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.value.default.type=com.example.Invoice
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example,org.acme
同樣,可以禁用JsonSerializer
在header中傳送型別資訊的預設行為:
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.properties.spring.json.add.type.headers=false
以這種方式設定的屬性將覆蓋Spring Boot顯式支援的任何配置項。