rabbitmq是當下非常流行的消息隊列,本文主要介紹springboot中如何配置使用rabbitmq。
文中代碼基於springboot2.1.6,源代碼見文末地址。
1.爲了自己玩方便,可以用docker安裝rabbitmq,見專欄內文章
《docker安裝rabbitmq》
2.相關配置
spring.rabbitmq.host=192.168.59.128
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
#這個如果不配置,就會默認找"/"
spring.rabbitmq.virtual-host=my_vhost
#指定心跳超時,單位秒,0爲不指定;默認60s
spring.rabbitmq.requested-heartbeat=20
#是否啓用【發佈確認】
spring.rabbitmq.publisher-confirms=true
#是否啓用【發佈返回】
spring.rabbitmq.publisher-returns=true
#連接超時,單位毫秒,0表示無窮大,不超時
spring.rabbitmq.connection-timeout=10
3.pom依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
4.rabbitmq有4種exchange
Exchange type | Default pre-declared names |
---|---|
Direct exchange | (Empty string) and amq.direct |
Fanout exchange | amq.fanout |
Topic exchange | amq.topic |
Headers exchange | amq.match (and amq.headers in RabbitMQ) |
a.direct exchange使用routing key進行消息傳輸,如下圖,routing key其實就是queue和exchange的綁定。適用於多工作者協同工作的場景。
綁定代碼如下:代碼中queue名稱和routing key名稱都是"direct"
@Configuration
public class DirectRabbitConfig {
@Bean
public Queue direct() {
return new Queue("direct");
}
@Bean
public DirectExchange directExchange() {
return new DirectExchange("directExchange");
}
@Bean
public Binding directBindingExchange(Queue direct, DirectExchange directExchange) {
return BindingBuilder.bind(direct).to(directExchange).with("direct");
}
}
sender如下:
@Service
public class DirectSenderService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private AmqpTemplate rabbitTemplate;
public void sendString(String message) {
logger.info("direct sender : " + message);
rabbitTemplate.convertAndSend("directExchange", "direct", message);
}
public void sendObject(Object message) {
String messageStr = JSONObject.toJSONString(message);
logger.info(messageStr);
rabbitTemplate.convertAndSend("directExchange", "direct", messageStr);
}
}
receiver:
@RabbitHandler
@RabbitListener(queues = {"direct"})
public void processDirect(Message message) {
logger.info("Receiver direct: {}", new String(message.getBody()));
}
b.fanout exchange就是廣播模式,把消息路有給所有的綁定隊列,可以適用於羣聊天的場景。
配置代碼如下:其中有3個隊列綁定一個fanout exchange
@Configuration
public class FanoutRabbitConfig {
@Bean
public Queue queueA(){
return new Queue("fanout.a");
}
@Bean
public Queue queueB(){
return new Queue("fanout.b");
}
@Bean
public Queue queueC(){
return new Queue("fanout.c");
}
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");
}
@Bean
public Binding bindingExchangeA(Queue queueA, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queueA).to(fanoutExchange);
}
@Bean
public Binding bindingExchangeB(Queue queueB, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queueB).to(fanoutExchange);
}
@Bean
public Binding bindingExchangeC(Queue queueC, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queueC).to(fanoutExchange);
}
}
sender:
@Service
public class FanoutSenderService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AmqpTemplate rabbitTemplate;
public void send(String message) {
logger.info("fanout sender : {}", message);
rabbitTemplate.convertAndSend("fanoutExchange","", message);
}
}
receiver:
@RabbitHandler
@RabbitListener(queues = {"fanout.a", "fanout.b", "fanout.c"})
public void processFanout1(Message message) {
logger.info("Receiver fanout: {}", new String(message.getBody()));
}
c.topic exchange通過routing key和通配符來路由消息,適用於發佈訂閱場景。
配置代碼:
@Configuration
public class TopicRabbitConfig {
@Bean
public Queue queueMessage() {
return new Queue("topic.message");
}
@Bean
public Queue queueMessage2() {
return new Queue("topic.message2");
}
/**
* 將隊列綁定到Topic交換器
* @return
*/
@Bean
public TopicExchange exchange() {
return new TopicExchange("topicExchange");
}
/**
* 將隊列綁定到Topic交換器
* @param queueMessage
* @param exchange
* @return
*/
@Bean
public Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
}
/**
* 將隊列綁定到Topic交換器 採用#的方式
* @param exchange
* @param queueMessage2
* @return
*/
@Bean
Binding bindingExchangeMessage2(TopicExchange exchange, Queue queueMessage2) {
return BindingBuilder.bind(queueMessage2).to(exchange).with("topic.#");
}
}
sender:
@Service
public class TopicSenderService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private AmqpTemplate rabbitTemplate;
public void send1(String message) {
logger.info("topic sender1 : " + message);
rabbitTemplate.convertAndSend("topicExchange", "topic.message", message);
}
public void send2(String message) {
logger.info("topic sender2 : " + message);
rabbitTemplate.convertAndSend("topicExchange", "topic.message2", message);
}
}
接受
@RabbitHandler
@RabbitListener(queues = {"topic.message"})
public void processTopic(Message message) {
logger.info("Receiver topic: {}", new String(message.getBody()));
}
@RabbitHandler
@RabbitListener(queues = {"topic.message2"})
public void processTopic2(Message message) {
logger.info("Receiver topic2: {}", new String(message.getBody()));
}
d.header exchange忽略routing key參數,用header來取代
配置
@Configuration
public class HeadersRabbitConfig {
@Bean
public Queue headerQueue() {
return new Queue("headerQueue");
}
@Bean
public Queue headerQueue2() {
return new Queue("headerQueue2");
}
@Bean
public HeadersExchange headerExchange() {
return new HeadersExchange("headerExchange");
}
@Bean
public HeadersExchange headerExchange2() {
return new HeadersExchange("headerExchange2");
}
@Bean
public Binding bindingExchange(Queue headerQueue, HeadersExchange headerExchange) {
Map<String,Object> headerValues = new HashMap<>(3);
headerValues.put("param1", "value1");
headerValues.put("param2", "value2");
return BindingBuilder.bind(headerQueue).to(headerExchange).whereAll(headerValues).match();
}
@Bean
public Binding bindingExchange2(Queue headerQueue2, HeadersExchange headerExchange2) {
Map<String,Object> header = new HashMap<>(3);
header.put("param1", "value1");
header.put("param2", "value2");
return BindingBuilder.bind(headerQueue2).to(headerExchange2).whereAny(header).match();
}
}
發送:
@Service
public class HeadersSenderService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private AmqpTemplate rabbitTemplate;
public void headerSend(Map<String, Object> head, String msg){
logger.info("header send message: "+msg);
rabbitTemplate.convertAndSend("headerExchange", "headerQueue", getMessage(head, msg));
}
public void headerSend2(Map<String, Object> head, String msg){
logger.info("header1 send message: "+msg);
rabbitTemplate.convertAndSend("headerExchange2", "headerQueue2", getMessage(head, msg));
}
private Message getMessage(Map<String, Object> head, Object msg){
MessageProperties messageProperties = new MessageProperties();
for (Map.Entry<String, Object> entry : head.entrySet()) {
messageProperties.setHeader(entry.getKey(), entry.getValue());
}
MessageConverter messageConverter = new SimpleMessageConverter();
return messageConverter.toMessage(msg, messageProperties);
}
}
接收:
@RabbitHandler
@RabbitListener(queues = {"headerQueue"})
public void processHeaders(Message message) {
logger.info("Receiver header: {}", new String(message.getBody()));
}
@RabbitHandler
@RabbitListener(queues = {"headerQueue2"})
public void processHeaders1(Message message) {
logger.info("Receiver header2: {}", new String(message.getBody()));
}
5.測試,測試代碼寫在RabbitMqController中,啓動Application即可進行url測試。見源碼。
說明:
a.topic exchange,瀏覽器輸入http://localhost:8082/mq/topic後,topic.#的routing key收到了2條消息,topic.message的routing key收到了1條,可以看出通配符的作用
b.headers exchange:瀏覽器輸入http://localhost:8082/mq/headers,發送了4條消息,但是第1條沒有收到。因爲headerExchange綁定時使用了whereAll,headerExchange2綁定時使用了whereAny。
參考:https://www.jianshu.com/p/de0c00359771
源代碼github地址:
https://github.com/jinjunzhu/springboot-rabbitmq
歡迎關注個人公衆號:jinjunzhu