RabbitMQ交換器分類:
- direct交換器(默認交換器)非常的簡單,如果路由鍵匹配的話,消息就投遞到相應的隊列
- fanout交換器——發佈/訂閱模式,一個發佈者,多個消費者,交換器會把一個消息發佈到綁定到此交換器的所有隊列
- topic交換器——匹配訂閱模式,和fanout類似,但是可以更靈活的匹配隊列,使用* 或者#進行規則匹配
- headers交換器——匹配AMQP消息的header而非路由鍵,用到較少,可以稍加了解
在瞭解RabbitMQ交換器之後,我們來根據官方文檔把所有的交換器實現一次
一、創建SpringBoot項目,整合RabbitMQ
引入pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、在配置文件中寫入RabbitMQ交換器配置信息
#--------------------MQ配置--------------------
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#消息發送確認回調
spring.rabbitmq.publisher-confirms=true
mq.env=local
#--------------------併發量的配置--------------------
# 併發消費者的初始化值
spring.rabbitmq.listener.concurrency=10
# 併發消費者的最大值
spring.rabbitmq.listener.max-concurrency=20
# 每個消費者每次監聽時可拉取處理的消息數量
spring.rabbitmq.listener.prefetch=5
#--------------------用戶操作日誌消息模型配置 direct模式--------------------
log.user.direct.queue=${mq.env}.log.user.direct.queue
log.user.direct.exchange=${mq.env}.log.user.direct.exchange
log.user.direct.routing.key=${mq.env}.log.user.direct.routing.key
#--------------------用戶操作日誌消息模型配置 fanout模式 不需要routing.key--------------------
log.user.fanout.queue1=${mq.env}.log.user.fanout.queue1
log.user.fanout.queue2=${mq.env}.log.user.fanout.queue2
log.user.fanout.exchange=${mq.env}.log.user.fanout.exchange
#--------------------用戶操作日誌消息模型配置 topic模式 --------------------
log.user.topic.exchange=${mq.env}.log.user.topic.exchange
#隊列名
log.user.topic.queue=${mq.env}.log.user.topic.queue
#隊列匹配規則
log.user.topic.queue.routing.key=#
#測試key值
#不可以綁定到隊列
log.user.topic.routing.key1=${mq.env}.log.file.topic.routing.key
#可綁定到隊列
log.user.topic.routing.key2=${mq.env}.log.user.topic.routing.key
#--------------------用戶操作日誌消息模型配置 headers模式 --------------------
log.user.headers.exchange=${mq.env}.log.user.headers.exchange
log.user.headers.queue=${mq.env}.log.user.headers.queue
三、創建配置類,在配置類中寫入所有的RabbitMQ交換器,供測試接口使用
@Configuration
public class MQConfig {
private static final Logger log = LoggerFactory.getLogger(MQConfig.class);
@Autowired
private Environment env;
@Autowired
private CachingConnectionFactory connectionFactory;
@Autowired
private SimpleRabbitListenerContainerFactoryConfigurer factoryConfigurer;
/**
* 單一消費者
*
* @return
*/
@Bean(name = "singleListenerContainer")
public SimpleRabbitListenerContainerFactory listenerContainer() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setConcurrentConsumers(1);
factory.setMaxConcurrentConsumers(1);
factory.setPrefetchCount(1);
factory.setTxSize(1);
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
return factory;
}
/**
* 多個消費者
*
* @return
*/
@Bean(name = "multiListenerContainer")
public SimpleRabbitListenerContainerFactory multiListenerContainer() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factoryConfigurer.configure(factory, connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setAcknowledgeMode(AcknowledgeMode.NONE);
factory.setConcurrentConsumers(env.getProperty("spring.rabbitmq.listener.concurrency", int.class));
factory.setMaxConcurrentConsumers(env.getProperty("spring.rabbitmq.listener.max-concurrency", int.class));
factory.setPrefetchCount(env.getProperty("spring.rabbitmq.listener.prefetch", int.class));
return factory;
}
@Bean
@Scope("prototype")
public RabbitTemplate rabbitTemplate() {
connectionFactory.setPublisherConfirms(true);
connectionFactory.setPublisherReturns(true);
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
//confirm此方法會在消息發送MQ之後自動調用
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
log.info("消息發送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause);
}
});
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
//returnedMessage此方法會在消息發送MQ失敗之後之後自動調用
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
String routingKey) {
log.info("消息丟失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey,
replyCode, replyText, message);
}
});
return rabbitTemplate;
}
//TODO 用戶操作日誌消息模型 direct模式
/**
* 定義隊列 並做持久化處理
* @return
*/
@Bean(name = "logUserQueue")
public Queue logUserQueue() {
return new Queue(env.getProperty("log.user.direct.queue"),true);
}
/**
* 定義交換器
* true:持久化處理
* false:不進行自動刪除
* @return
*/
@Bean
public DirectExchange logUserExchange() {
return new DirectExchange(env.getProperty("log.user.direct.exchange"),true,false);
}
//路由
@Bean
public Binding logUserBinding() {
//鏈式寫法: 用指定的路由鍵將隊列綁定到交換機
return BindingBuilder.bind(logUserQueue())
.to(logUserExchange())
.with(env.getProperty("log.user.direct.routing.key"));
}
//TODO 用戶操作日誌消息模型 Fanout模式
/**
* 定義交換器
* @return
*/
@Bean
public FanoutExchange fanoutExchange() {
log.info("創建FanoutExchange>>>>>>>>>>>>>>>>>>>>>>>>>>>成功");
return new FanoutExchange(env.getProperty("log.user.fanout.exchange"),true,false);
}
/**
* 測試隊列一
*
* @return 隊列實例
*/
@Bean
public Queue queue1() {
log.info("【【【測試隊列一實例創建成功】】】");
return new Queue(env.getProperty("log.user.fanout.queue1"));
}
/**
* 測試隊列二
*
* @return 隊列實例
*/
@Bean
public Queue queue2() {
log.info("【【【測試隊列二實例創建成功】】】");
return new Queue(env.getProperty("log.user.fanout.queue2"));
}
/**
* 綁定隊列一到交換機
*
* @return 綁定對象
*/
@Bean
public Binding bingQueue1ToExchange() {
log.info("【【【綁定隊列一到交換機成功】】】");
return BindingBuilder.bind(queue1()).to(fanoutExchange());
}
/**
* 綁定隊列二到交換機
*
* @return 綁定對象
*/
@Bean
public Binding bingQueue2ToExchange() {
log.info("【【【綁定隊列二到交換機成功】】】");
return BindingBuilder.bind(queue2()).to(fanoutExchange());
}
//TODO 用戶操作日誌消息模型 Topic模式
@Bean
public TopicExchange topicExchange () {
log.info("【<<<<<<<<<<<--Topic交換器創建成功-->>>>>>>>>>>>>>>>>>>>】");
return new TopicExchange(env.getProperty("log.user.topic.exchange"),true,false);
}
/**
* 測試隊列
* @return 隊列實例
*/
@Bean
public Queue topicQueue() {
log.info("【<<<<<<<<<<<--測試topic隊列創建成功-->>>>>>>>>>>>>>>>>>>>】");
return new Queue(env.getProperty("log.user.topic.queue"));
}
@Bean
public Binding bingQueue2ToTopic() {
Binding binding = BindingBuilder.bind(topicQueue()).to(topicExchange()).with("*.user.*");
log.info("【<<<<<<<<<<<--topic隊列綁定成功--匹配規則:"+env.getProperty("log.user.topic.queue.routing.key")+">>>>>>>>>>>>>>>>>>>>】");
return binding;
}
//TODO 用戶操作日誌消息模型 Headers模式
@Bean
public HeadersExchange headersExchange () {
log.info("【<<<<<<<<<<<--Headers交換器創建成功-->>>>>>>>>>>>>>>>>>>>】");
return new HeadersExchange(env.getProperty("log.user.headers.exchange"),true,false);
}
@Bean
public Queue headers_queue() {
return new Queue(env.getProperty("log.user.headers.queue"),true);
}
@Bean
Binding bindingExchangeTopicQueue2() {
Map<String, Object> map = new HashMap<>();
map.put("user", "101");
map.put("pwd", "202");
//whereAll表示全部匹配
//return BindingBuilder.bind(queue).to(headersExchange).whereAll(map).match();
//whereAny表示部分匹配
//all: 默認值。一個傳送消息的header裏的鍵值對和交換機的header鍵值對全部匹配,纔可以路由到對應交換機
//any: 一個傳送消息的header裏的鍵值對和交換機的header鍵值對任意一個匹配,就可以路由到對應交換機
return BindingBuilder.bind(headers_queue()).
to(headersExchange()).
whereAny(map).match();
}
}
四、創建發送消息服務
@Service
public class ProdServer {
private static final Logger log = LoggerFactory.getLogger(ProdServer.class);
@Autowired
private ObjectMapper objectMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private Environment env;
/**
* direct模式下發送消息
* @param obj
* @throws JsonProcessingException
*/
public void convertAndSend(Object obj) throws JsonProcessingException {
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setExchange(env.getProperty("log.user.direct.exchange"));
rabbitTemplate.setRoutingKey(env.getProperty("log.user.direct.routing.key"));
Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(obj))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
MessageProperties.CONTENT_TYPE_JSON);
rabbitTemplate.convertAndSend(message);
log.info("--------------------消息傳入MQ成功-----------------");
}
/**
* fanout模式下發送消息
* @param obj
*/
public void fanoutSendMsg(Object obj) throws JsonProcessingException {
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setExchange(env.getProperty("log.user.fanout.exchange"));
rabbitTemplate.setRoutingKey("");
Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(obj))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
MessageProperties.CONTENT_TYPE_JSON);
log.info("【消息發送者】發送消息到fanout交換機,消息內容爲: {}", message);
rabbitTemplate.convertAndSend(message);
log.info("--------------------消息傳入MQ成功-----------------");
}
/**
* topic模式下發送消息
* @param obj
*/
public void topicSendMsg1(Object obj) throws JsonProcessingException {
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setExchange(env.getProperty("log.user.topic.exchange"));
rabbitTemplate.setRoutingKey(env.getProperty("log.user.topic.routing.key1"));
Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(obj))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
MessageProperties.CONTENT_TYPE_JSON);
log.info("【消息發送者1】發送消息到fanout交換機,消息內容爲: {}", message);
rabbitTemplate.convertAndSend(message);
}
/**
* topic模式下發送消息
* @param obj
*/
public void topicSendMsg2(Object obj) throws JsonProcessingException {
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setExchange(env.getProperty("log.user.topic.exchange"));
rabbitTemplate.setRoutingKey(env.getProperty("log.user.topic.routing.key2"));
Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(obj))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
MessageProperties.CONTENT_TYPE_JSON);
log.info("【消息發送者2】發送消息到fanout交換機,消息內容爲: {}", message);
rabbitTemplate.convertAndSend(message);
}
/**
* headers模式下發送消息
* @param obj
*/
public void headersSendMsg1(Object obj) throws JsonProcessingException {
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setExchange(env.getProperty("log.user.headers.exchange"));
Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(obj))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
message.getMessageProperties().setHeader("user","102");
log.info("【消息發送者1】發送消息到headers交換機,消息內容爲: {}", message);
rabbitTemplate.convertAndSend(message);
}
/**
* headers模式下發送消息
* @param obj
*/
public void headersSendMsg2(Object obj) throws JsonProcessingException {
Map<String, Object> map_all = new HashMap<>();
map_all.put("user", "101");
map_all.put("pwd", "202");
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setExchange(env.getProperty("log.user.headers.exchange"));
Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(obj))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
message.getMessageProperties().getHeaders().putAll(map_all);
log.info("【消息發送者2】發送消息到headers交換機,消息內容爲: {}", message);
rabbitTemplate.convertAndSend(message);
}
}
五、創建接收消息的監聽
@Component
public class LogMqListener {
private static final Logger log= LoggerFactory.getLogger(LogMqListener.class);
@Autowired
private ObjectMapper objectMapper;
/**
* 監聽消費用戶日誌
* @param message
*/
@RabbitListener(queues = "${log.user.direct.queue}",containerFactory = "singleListenerContainer")
public void consumeUserLogQueue(@Payload byte[] message){
try {
String userLog=objectMapper.readValue(message, String.class);
log.info("監聽消費用戶日誌 監聽到消息: {} ",userLog);
//userLogMapper.insertSelective(userLog);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 監聽消費用戶日誌 fanout模式
* @param message
*/
@RabbitListener(queues = "${log.user.fanout.queue1}",containerFactory = "singleListenerContainer")
public void fanoutUserLogQueue1(@Payload byte[] message){
try {
String userLog=objectMapper.readValue(message, String.class);
log.info("監聽消費用戶日誌【隊列1】 監聽到消息: {} ",userLog);
//userLogMapper.insertSelective(userLog);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 監聽消費用戶日誌 fanout模式
* @param message
*/
@RabbitListener(queues = "${log.user.fanout.queue2}",containerFactory = "singleListenerContainer")
public void fanoutUserLogQueue2(@Payload byte[] message){
try {
String userLog=objectMapper.readValue(message, String.class);
log.info("監聽消費用戶日誌 【隊列2】監聽到消息: {} ",userLog);
//userLogMapper.insertSelective(userLog);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 監聽消費用戶日誌 topic模式
* @param message
*/
@RabbitListener(queues = "${log.user.topic.queue}",containerFactory = "singleListenerContainer")
public void topicUserLogQueue(@Payload byte[] message){
try {
String userLog=objectMapper.readValue(message, String.class);
log.info("監聽消費用戶日誌監聽到消息: {} ",userLog);
//userLogMapper.insertSelective(userLog);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 監聽消費用戶日誌 headers模式
* @param message
*/
@RabbitListener(queues = "${log.user.headers.queue}",containerFactory = "singleListenerContainer")
public void headersUserLogQueue(@Payload byte[] message){
try {
String userLog=objectMapper.readValue(message, String.class);
log.info("監聽消費用戶日誌監聽到消息: {} ",userLog);
//userLogMapper.insertSelective(userLog);
}catch (Exception e){
e.printStackTrace();
}
}
}
六、創建測試類
@RestController
@RequestMapping("/mq")
public class UserController extends BaseController{
@Autowired
private ProdServer prodServer;
@RequestMapping(value="/direct/add", method = {RequestMethod.GET, RequestMethod.POST})
public String direct(String eventIds) {
String msg = IdUtils.randomUUID();
try {
prodServer.convertAndSend(msg);
} catch (Exception e) {
e.printStackTrace();
logger.error("-----------------mq添加出錯------------");
}
return msg;
}
@RequestMapping(value="/fanout/add", method = {RequestMethod.GET, RequestMethod.POST})
public String fanout(String eventIds) {
String msg = IdUtils.randomUUID();
try {
prodServer.fanoutSendMsg(msg);
} catch (Exception e) {
e.printStackTrace();
logger.error("-----------------mq添加出錯------------");
}
return msg;
}
@RequestMapping(value="/topic/add", method = {RequestMethod.GET, RequestMethod.POST})
public String topic(String eventIds) {
String msg = IdUtils.randomUUID();
try {
prodServer.topicSendMsg1(msg);
prodServer.topicSendMsg2(msg);
} catch (Exception e) {
e.printStackTrace();
logger.error("-----------------mq添加出錯------------");
}
return msg;
}
@RequestMapping(value="/headers/add", method = {RequestMethod.GET, RequestMethod.POST})
public String headers(String eventIds) {
String msg = IdUtils.randomUUID();
try {
prodServer.headersSendMsg1(msg);
prodServer.headersSendMsg2(msg);
} catch (Exception e) {
e.printStackTrace();
logger.error("-----------------mq添加出錯------------");
}
return msg;
}
}
所有的代碼實現都已經寫完了,接下來就可以把項目跑起來,調用接口看一下實現效果
啓動服務
測試hesders交換器結果
小結:
這裏只是寫了每個交換器基本的使用方法,具體的項目使用還是需要更加深入的學習