項目構建
引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
springBoot引入rabbitMq需要添加依賴
配置參數
spring.application.name=cluster-1
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5673
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#必須配置這個纔會確認回調
spring.rabbitmq.publisher-confirm-type=correlated
#消息發送到交換機確認機制,是否返回回饋
spring.rabbitmq.publisher-returns=true
# 對 rabbitmqTemplate 進行監聽,當消息由於server的原因無法到達queue時,就會被監聽到,以便執行ReturnCallback方法
# 默認爲false,Server端會自動刪除不可達消息
spring.rabbitmq.template.mandatory=true
# 消費端手動確認
spring.rabbitmq.listener.type=simple
#manual 手動確認
spring.rabbitmq.listener.simple.acknowledge-mode=manual
# 併發消費 同一個隊列啓動幾個消費者
spring.rabbitmq.listener.simple.concurrency=3
# 啓動消費者最大數量
spring.rabbitmq.listener.simple.max-concurrency=3
#是否支持重試 true 支持
spring.rabbitmq.listener.simple.retry.enabled=true
#最大重試次數
spring.rabbitmq.listener.simple.retry.max-attempts=5
#重試是無狀態的還是有狀態的
spring.rabbitmq.listener.simple.retry.stateless=false
#時間策略乘數因子
spring.rabbitmq.listener.simple.retry.multiplier = 1.0
#第一次和第二次嘗試發佈或傳遞消息之間的間隔
spring.rabbitmq.listener.direct.retry.initial-interval=1000ms
#最大重試時間間隔
spring.rabbitmq.listener.direct.retry.max-interval = 10000m
#重試次數超過上面的設置之後是否丟棄(false不丟棄時需要寫相應代碼將該消息加入死信隊列)
spring.rabbitmq.listener.direct.default-requeue-rejected = true
添加生產者
創建隊列
package com.notification.rabbitcluster.normal;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import java.util.HashMap;
import java.util.Map;
/**
* @author wang
*/
@Configuration
public class RabbitMQConfig {
@Autowired
private CachingConnectionFactory connectionFactory;
/**
* 目標轉換器,需要哪種類型的轉換器就創建哪種類型的轉換器
*
* @return
*/
@Bean
public DirectExchange exchangeHello() {
Map<String, Object> eArguments = new HashMap<>();
//備份交換器參數
eArguments.put("alternate-exchange", "exchange.ae");
return new DirectExchange("exchange.hello", true, false, eArguments);
}
/**
* 備份轉換器
*
* @return
*/
@Bean
public FanoutExchange exchangeAE() {
return new FanoutExchange("exchange.ae", true, false, null);
}
/**
* 死信轉換器
*
* @return
*/
@Bean
public TopicExchange exchangeDLX() {
return new TopicExchange("exchange.dlx", true, false, null);
}
/**
* 目標對列
*
* @return 隊列
*/
@Bean
public Queue queueHello() {
Map<String, Object> args = new HashMap<>();
//聲明死信交換器
args.put("x-dead-letter-exchange", "exchange.dlx");
//聲明死信路由鍵
args.put("x-dead-letter-routing-key", "dlx.test");
//聲明隊列消息過期時間 5000ms
args.put("x-message-ttl", 5000);
return new Queue("queue.hello", true, false, false, args);
}
/**
* 備份對列
*
* @return 隊列
*/
@Bean
public Queue queueAE() {
return new Queue("queue.ae", true, false, false, null);
}
/**
* 死信對列
*
* @return 隊列
*/
@Bean
public Queue queueDLX() {
return new Queue("queue.dlx", true, false, false, null);
}
/**
* 綁定目標對列
*
* @param queueHello
* @param exchangeHello
* @return
*/
@Bean
public Binding bindingExchangeDirect(@Qualifier("queueHello") Queue queueHello, @Qualifier("exchangeHello") DirectExchange exchangeHello) {
return BindingBuilder.bind(queueHello).to(exchangeHello).with("helloKey");
}
/**
* 綁定備份對列
*
* @param queueAE
* @param exchangeAE
* @return
*/
@Bean
public Binding bindingExchangeAE(@Qualifier("queueAE") Queue queueAE, @Qualifier("exchangeAE") FanoutExchange exchangeAE) {
return BindingBuilder.bind(queueAE).to(exchangeAE);
}
/**
* 綁定死信對列
*
* @param queueAE
* @param exchangeDLX
* @return
*/
@Bean
public Binding bindingExchangeDLX(@Qualifier("queueDLX") Queue queueAE, @Qualifier("exchangeDLX") TopicExchange exchangeDLX) {
return BindingBuilder.bind(queueAE).to(exchangeDLX).with("dlx.*");
}
/**
* 如果需要在生產者需要消息發送後的回調,
* 需要對rabbitTemplate設置ConfirmCallback對象,
* 由於不同的生產者需要對應不同的ConfirmCallback,
* 如果rabbitTemplate設置爲單例bean,
* 則所有的rabbitTemplate實際的ConfirmCallback爲最後一次申明的ConfirmCallback。
*
* @return
*/
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory);
}
}
創建消息發送類
package com.notification.rabbitcluster.queue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.UUID;
/**
* @author wang
*/
@RestController
public class Sender implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
private static Logger log = LoggerFactory.getLogger(Sender.class);
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 構造方法注入
*/
@Autowired
public Sender(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
//這是是設置回調能收到發送到響應
rabbitTemplate.setConfirmCallback(this);
//如果設置備份隊列則不起作用
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(this);
}
@GetMapping("/send")
public void send() {
String sendMsg = "hello1 " + new Date();
//convertAndSend(exchange:交換機名稱,routingKey:路由關鍵字,object:發送的消息內容,correlationData:消息ID)
CorrelationData cd = new CorrelationData();
// 消息唯一標識
String replace = UUID.randomUUID().toString().replace("-", "");
System.out.println("Sender : " + sendMsg+" ID :"+replace);
cd.setId(replace);
rabbitTemplate.convertAndSend("exchange.hello", "helloKey", sendMsg, cd);
}
/**
* 回調確認
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
log.info("消息發送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause);
} else {
log.info("消息發送失敗:correlationData({}),ack({}),cause({})", correlationData, ack, cause);
}
}
/**
* 消息發送到轉換器的時候沒有對列,配置了備份對列該回調則不生效
* @param message
* @param replyCode
* @param replyText
* @param exchange
* @param routingKey
*/
@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);
}
}
添加消費者
新建一個項目,項目的jar包引入與rabbitMq配置與生產者相同
package com.notification.rabbitcustomer;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.io.IOException;
/**
* @author wang
*/
@Component
public class Consumer {
private static Logger log = LoggerFactory.getLogger(Consumer.class);
@RabbitHandler
@RabbitListener(queues = "queue.hello")
public void process(Message message, Channel channel) throws IOException {
log.info(String.format("receive:%s線程名:%s線程id:%d",
new String(message.getBody()),
Thread.currentThread().getName(),
Thread.currentThread().getId()));
/*
* 手工ACK,不批量ack
* 取值爲 false 時,表示通知 RabbitMQ 當前消息被確認
* 如果爲 true,則額外將比第一個參數指定的 delivery tag 小的消息一併確認
*/
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try{
Assert.isTrue(false);
//發送消費成功消息
channel.basicAck(deliveryTag, false);
}
catch (Exception e){
//手動發送消費失敗消息
channel.basicReject(deliveryTag, true);
}
}
}
添加重試機制
以上配置中的配置爲消息重試配置
#是否支持重試 true 支持
spring.rabbitmq.listener.simple.retry.enabled=true
#最大重試次數
spring.rabbitmq.listener.simple.retry.max-attempts=5
#重試是無狀態的還是有狀態的
spring.rabbitmq.listener.simple.retry.stateless=false
#時間策略乘數因子
spring.rabbitmq.listener.simple.retry.multiplier = 1.0
#第一次和第二次嘗試發佈或傳遞消息之間的間隔
spring.rabbitmq.listener.direct.retry.initial-interval=1000ms
#最大重試時間間隔
spring.rabbitmq.listener.direct.retry.max-interval = 10000m
#重試次數超過上面的設置之後是否丟棄(false不丟棄時需要寫相應代碼將該消息加入死信隊列)
spring.rabbitmq.listener.direct.default-requeue-rejected = true
添加消息失敗死信隊列
創建隊列時綁定死信交換器
/**
* 目標對列
*
* @return 隊列
*/
@Bean
public Queue queueHello() {
Map<String, Object> args = new HashMap<>();
//聲明死信交換器
args.put("x-dead-letter-exchange", "exchange.dlx");
//聲明死信路由鍵
args.put("x-dead-letter-routing-key", "dlx.test");
//聲明隊列消息過期時間 5000ms
args.put("x-message-ttl", 5000);
return new Queue("queue.hello", true, false, false, args);
}
處理死信隊列
創建新的監聽方法,監聽死信隊列,對死信隊列中的數據進行處理。