前言
本文結合springboot學習rabbitmq(springboot版本2.2.2)
demo地址:https://github.com/foxiaotao/springboot-rabbitmq-demo
rabbitmq就是生產者把消息放到Exchange,Exchange根據規則把消息路由到Queue,消費者從Queue消費消息。
一、RabbitMq 構成
1、Exchange(交換器)
生產者將消息發佈出去,消息首先就得Exchange來接受,發送消息時,不用考慮Queue,消息一經生產者發送,那麼消息就在Exchange中(Exchange並不具備儲存消息,這裏便於理解才這樣說)。
1)、Exchange主要類型介紹
direct(默認)
headers
fanout
topic
其中headers交換器允許你匹配AMQP消息的header而非路由鍵,除此之外headers交換器和direct交換器完全一致,但性能卻很差,幾乎用不到,所以我們本文也不做講解。
注意:fanout、topic交換器是沒有歷史數據的,也就是說對於中途創建的隊列,獲取不到之前的消息。
2)、direct 模式
Exchange 和 Queue 是1:1關聯,關聯就叫做路由鍵(RoutingKey),需要申明Exchange和Queue,以及Exchange和Queue的關聯RoutingKey。申明綁定關係。
@Bean("dlxExchangeBean")
public DirectExchange dlxExchangeBean() {
return new DirectExchange(dlxExchange);
}
@Bean("dlxQueueBean")
public Queue dlxQueueBean() {
return new Queue(dlxQueue);
}
@Bean
public Binding binding(@Qualifier("dlxExchangeBean") DirectExchange dlxExchange, @Qualifier("dlxQueueBean") Queue dlxQueue) {
return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
}
這種方式只是定義在java 代碼中,如果在rabbitmq上沒有創建過,還需要通過後臺手動創建Exchange和Queue,並通過RoutingKey綁定。
每次發送消息需要指定Exchange 和 RoutingKey
例如:圖1:中發送消息指定Exchange 和 RoutingKey=rk1,那麼就只有Queue1收到消息,Queue2和Queue3不會有消息。只有Consumer1 消費到了消息。
3)、fanout模式
這種模式(可以參考廣播模式),只需要將Exchange與Queue申明綁定,Exchange會把消息發佈到所有Queue,這種不需要路由鍵。
@Bean(name="userExchange")
public FanoutExchange userFanoutExchange() {
return new FanoutExchange(userRegisterExchange, true, false);
}
@Bean(name = "userRegisterQueue")
public Queue userRegisterQueue() { //還款的queue
return new Queue(userRegisterQueue);
}
首次需要在rabbitmq後臺創建並綁定,綁定不需要RoutingKey。如圖2,消息只有發送Exchange,所有Queue都會用相同消息。
4)、topic模式
簡單的說是前置匹配模式。路由鍵必須必須包含xx.* 或者xx.#
*表示一個字符
#表示任意個字符
如圖3關係所示:
如果發送RoutingKey=odd.a,那麼收到消息的有Queue1、Queue2;
如果發送RoutingKey=odd.age,那麼收到消息的有Queue1;
2、Queue(隊列)
隊列就是消息的載體,車行駛在公路上,那麼消息就放在隊列中。通過Exchange的介紹,已經引入Queue,Queue是消息存放和通行的地方。Queue和Exchange的需要通過綁定建立關係。這種關係是長久存在的,不會因消息的改變而變化。就是定義Exchange和Queue的路由規則,消息攜帶的規則在總的Exchange中匹配,找到匹配的就將消息路由到Queue。
消費者監聽指定隊列,只要隊列中有消息,監聽程序獲取到消息。供消費者使用。
3、broker(消息體)
就是Exchange 和Queue 全部的集合
二、消息的發送與接收
1、消息發送
消息發送到mq,在springboot中引入import org.springframework.amqp.rabbit.core.RabbitTemplate類,
rabbitTemplate.convertAndSend完成發送消息。
定義RabbitTemplate:
@Bean("delayCachingConnectionFactory")
public CachingConnectionFactory delayCachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
connectionFactory.setChannelCacheSize(1024);
connectionFactory.setConnectionCacheSize(1024);
connectionFactory.setUsername(user);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(kaLoanVirtualHost);
connectionFactory.setPublisherReturns(false);
connectionFactory.setPublisherConfirms(false);
return connectionFactory;
}
@Bean(name = "delayRabbitTemplate")
public RabbitTemplate delayRabbitTemplate(@Qualifier("delayCachingConnectionFactory") ConnectionFactory connectionFactory) {
//RabbitTemplate rabbitTemplate = techRabbitBuilder.createRabbitTemplate(connectionFactory);
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 指定默認exchange
rabbitTemplate.setExchange(delayExchange);
// 指定默認路由鍵
rabbitTemplate.setRoutingKey(delayRoutingKey);
return rabbitTemplate;
}
/**
* 申明exchange
* @return
*/
@Bean("delayExchange")
public DirectExchange delayExchangeBean() {
return new DirectExchange(delayExchange);
}
@Bean("delayQueue")
public Queue orderQueueBean() {
return new Queue(delayQueue, true, false, false, null);
}
@Bean
public Binding orderBinding(@Qualifier("delayExchange") DirectExchange delayExchange, @Qualifier("delayQueue") Queue delayQueue) {
return BindingBuilder.bind(delayQueue).to(delayExchange).with(delayRoutingKey);
}
在config中申明RabbitTemplate delayRabbitTemplate,指定Bean.name,要是在程序中沒有多個RabbitTemplate可以不用指定。@Bean的方法名稱也同樣是Bean的默認name。
定義完RabbitTemplate,就可以發送消息到mq 了。
package cn.quantgroup.clf.api.delayretry.service.senddelay;
import cn.quantgroup.clf.api.delayretry.model.DelayEventBody;
import cn.quantgroup.clf.util.JSONTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @description: 消息發送
* @author:tao
* @create: 2019-12-18 14:20
*/
@Slf4j
@Service
public class SendDelayEventMqServiceImpl implements SendDelayEventService {
@Resource(name = "delayRabbitTemplate")
private RabbitTemplate rabbitTemplate;
@Override
public void sendDelayEvent(DelayEventBody delayEventBody) {
rabbitTemplate.convertAndSend(JSONTools.serialize(delayEventBody));
}
}
顯然可以看出Exchange類型是Direct,這裏發送消息的時候並沒有指定exchange和routingKey,是因爲在RabbitTemplate定義的時候設置了默認的exchange和routingKey,當然也可以在每次發送消息的時候指定Exchange和routingKey,指定方法爲,對應的exchange和routingKey的name。那麼這樣消息就發送到mq中了。
如圖所示,在隊列ka.dlx.queue中, 有5條消息。Features:D消息持久化,DLX、DLK死信;State:idle空閒、running運行。Ready、Unacked是消息的狀態,Ready準備好的消息數量,Unacked:消費者還沒有ack確認的消息數量。
當消息還在隊列中的時候,可以通過Get Message 查詢消息。
消息的發送只與Exchange和routingKey相關,不需要定義Queue。
2、消息接受
消息的接受,如果只是純粹的接受消息,與Exchange和routingKey無關,消費者監聽Queue即可。
package cn.quantgroup.cashloanflow.config.mq;
import cn.quantgroup.clf.api.delayretry.service.consumer.ConsumeDelayEventService;
import cn.quantgroup.tech.brave.service.ITechRabbitBuilder;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerEndpoint;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @author: amen
* @date: 2019-01-08
*/
@Configuration
public class DelayMqConfig {
@Value("${ka.rabbitmq.connection.host}")
private String host;
@Value("${ka.rabbitmq.connection.port}")
private Integer port;
@Value("${ka.rabbitmq.connection.user}")
private String user;
@Value("${ka.rabbitmq.connection.password}")
private String password;
@Value("${ka.rabbitmq.connection.virtual-host}")
private String kaLoanVirtualHost;
@Value("${ka.rabbitmq.delay.queue:ka.delay.queue}")
private String delayQueue;
@Value("${ka.rabbitmq.delay.exchange:ka.delay.exchange}")
private String delayExchange;
@Value("${ka.rabbitmq.delay.routingKey:ka.delay.routingKey}")
private String delayRoutingKey;
@Value("${ka.rabbitmq.dlx.queue:ka.dlx.queue}")
private String dlxQueue;
@Value("${ka.rabbitmq.dlx.exchange:ka.dlx.exchange}")
private String dlxExchange;
@Value("${ka.rabbitmq.dlx.routingKey:ka.dlx.routingKey}")
private String dlxRoutingKey;
@Autowired
@Qualifier( "techRabbitBuilder" )
private ITechRabbitBuilder techRabbitBuilder;
@Bean("delayCachingConnectionFactory")
public CachingConnectionFactory delayCachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
connectionFactory.setChannelCacheSize(1024);
connectionFactory.setConnectionCacheSize(1024);
connectionFactory.setUsername(user);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(kaLoanVirtualHost);
connectionFactory.setPublisherReturns(false);
connectionFactory.setPublisherConfirms(false);
return connectionFactory;
}
/**
* 申明死信隊列
* @return
*/
@Bean("dlxExchangeBean")
public DirectExchange dlxExchangeBean() {
return new DirectExchange(dlxExchange);
}
@Bean("dlxQueueBean")
public Queue dlxQueueBean() {
return new Queue(dlxQueue);
}
@Bean
public Binding binding(@Qualifier("dlxExchangeBean") DirectExchange dlxExchange, @Qualifier("dlxQueueBean") Queue dlxQueue) {
return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
}
/**
* 申明延時隊列
* @return
*/
@Bean("delayExchange")
public DirectExchange delayExchangeBean() {
return new DirectExchange(delayExchange);
}
@Bean("delayQueue")
public Queue orderQueueBean() {
Map<String, Object> arguments = new HashMap<>(4);
// 綁定該隊列到死信交換機
arguments.put("x-dead-letter-exchange", dlxExchange);
arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
return new Queue(delayQueue, true, false, false, arguments);
}
@Bean
public Binding orderBinding(@Qualifier("delayExchange") DirectExchange delayExchange, @Qualifier("delayQueue") Queue delayQueue) {
return BindingBuilder.bind(delayQueue).to(delayExchange).with(delayRoutingKey);
}
/**
* 監聽死信隊列
* @param consumeDelayEventService
* @param cachingConnectionFactory
* @param dlxQueue
* @return
*/
@Bean("delaySimpleMessageListenerContainer")
public SimpleMessageListenerContainer delaySimpleMessageListenerContainer(@Qualifier("consumeMqDelayEventService") ConsumeDelayEventService consumeDelayEventService,
@Qualifier("delayCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory,
@Qualifier("dlxQueueBean") Queue dlxQueue) {
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = techRabbitBuilder
.createSimpleRabbitListenerContainerFactory(cachingConnectionFactory);
SimpleRabbitListenerEndpoint simpleRabbitListenerEndpoint = new SimpleRabbitListenerEndpoint();
simpleRabbitListenerEndpoint.setQueues(dlxQueue);
simpleRabbitListenerEndpoint.setMessageListener(new MessageListenerAdapter(consumeDelayEventService, "consumeMessage"));
SimpleMessageListenerContainer container = simpleRabbitListenerContainerFactory.createListenerContainer(simpleRabbitListenerEndpoint);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setConcurrentConsumers(10);
container.start();
return container;
}
@Bean(name = "delayRabbitTemplate")
public RabbitTemplate delayRabbitTemplate(@Qualifier("delayCachingConnectionFactory") ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = techRabbitBuilder.createRabbitTemplate(connectionFactory);
rabbitTemplate.setExchange(delayExchange);
rabbitTemplate.setRoutingKey(delayRoutingKey);
return rabbitTemplate;
}
}
package cn.quantgroup.clf.api.delayretry.service.consumer;
/**
* @description: 消費延遲隊列消息
* @author:tao
* @create: 2019-12-23 11:49
*/
public interface ConsumeDelayEventService {
void consumeMessage(String message);
}
實現ConsumeDelayEventService接口,即可收到消息。