文章目录
RabbitMQ
一、什么是MQ
MQ(message queue),字面意思就是一个队列,FIFO先入先出,只不过队列中存放的内容是message,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用MQ之后,消息发送上游只需要依赖MQ,不用依赖其他服务
二、MQ的优点
- 流量消峰
- 应用解耦
- 异步处理
三、rabbitMQ
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
四、docker安装
- –name :指定容器名称
- -p :将mq端口号映射到本地
- RABBITMQ_DEFAULT_USER 设置用户名
docker run -di --name myrabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
启动
访问
五、rabbitmq的工作模式
官网:https://www.rabbitmq.com/getstarted.html
- 5.1、简单模式
一个生产者对应一个消费者!!
- 5.2、工作模式
一个生产者对应多个消费者,但是一条消息只能有一个消费者获得消息!!!
轮询分发就是将消息队列中的消息,依次发送给所有消费者。一个消息只能被一个消费者获取。公平分发:根据消费者能力进行分发,处理快的分配的多
- 5.3、订阅模式
一个消费者将消息首先发送到交换器,交换器绑定到多个队列,然后被监听该队列的消费者所接收并消费。
ps:X表示交换器,在RabbitMQ中,交换器主要有四种类型:direct、fanout、topic、headers,这里的交换器是 fanout。下面我们会详细介绍这几种交换器。
==两个消费者获得了同一条消息。==即就是,一个消息从交换机同时发送给了两个队列中,监听这两个队列的消费者消费了这个消息;
如果没有队列绑定交换机,则消息将丢失。因为交换机没有存储能力,消息只能存储在队列中。
- 5.4、路由模式
生产者将消息发送到direct交换器,在绑定队列和交换器的时候有一个路由key,生产者发送的消息会指定一个路由key,那么消息只会发送到相应key相同的队列,接着监听该队列的消费者消费消息。
也就是让消费者有选择性的接收消息。
路由模式,是以路由规则为导向,引导消息存入符合规则的队列中。再由队列的消费者进行消费的。
- 5.5、主题模式
上面的路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配。
符号“#”表示匹配一个或多个词,符号“*”表示匹配一个词。
与路由模式相似,但是,主题模式是一种模糊的匹配方式。
六、四种交换机
- 1、direct 如果路由键完全匹配的话,消息才会被投放到相应的队列。
- 2、fanout 当发送一条消息到fanout交换器上时,它会把消息投放到所有附加在此交换器上的队列。
- 3、topic 设置模糊的绑定方式,“*”操作符将“.”视为分隔符,匹配单个字符;“#”操作符没有分块的概念,它将任意“.”均视为关键字的匹配部分,能够匹配多个字符。
- 4、header headers 交换器允许匹配 AMQP 消息的 header 而非路由键,除此之外,header 交换器和 direct 交换器完全一致,但是性能却差很多,因此基本上不会用到该交换器
七、简单模式案例
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.2.0</version>
</dependency>
- 生产者
public class Producer {
public static void main(String[] args) {
//所有中间件技术都是基于tcp/ip协议基础上构建新型的协议规范,只不过rabbitmq遵循的是amqp协议
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接
connection = connectionFactory.newConnection("生产者1号");
//3.通过连接创建通道channel
channel = connection.createChannel();
//4.通过创建交换机声明队列
/**
* queueDeclare(String queue, 队列名称
* boolean durable, 是否持久化 非持久化也会存盘,但是rabbit服务重启则就没有了
* boolean exclusive, 排他性,是否独占
* boolean autoDelete, 是否自动删除,随着最后一个消费者吧消息消费后时候把队列自动删除
* Map<String, Object> arguments 携带附属参数
* )
*/
String queName = "queue1";
channel.queueDeclare(queName, false, false, false, null);
//5.准备消息
String message = "hello";
//6.发送消息给队列queue
//@params1:交换机 @@params2:队列,路由key @params3:消息的状态控制 @params4:消息
channel.basicPublish("", queName, null, message.getBytes());
System.out.println("消息发送成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//8.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 消费者
public class Consumer {
public static void main(String[] args) {
//所有中间件技术都是基于tcp/ip协议基础上构建新型的协议规范,只不过rabbitmq遵循的是amqp协议
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection =null;
Channel channel = null;
try {
//2.创建连接
connection = connectionFactory.newConnection();
//3.通过连接创建通道channel
channel = connection.createChannel();
//4.消费者消费消息
/**
*basicConsume(String queue, 消费哪个队列
* boolean autoAck, 消费成功之后是否要自动应答 true 代表自动应答,false代表收到应答
* DeliverCallback deliverCallback, 收到消息后用来处理消息的回调对象
* CancelCallback cancelCallback 消费者取消时的回调对象
* )
*/
channel.basicConsume("queue1", true, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println("消费消息是" + new String(message.getBody(), "UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
System.out.println("取消消费的");
}
});
System.out.println("开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//6.关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
八、rabbitmq的核心组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tpUryA7-1652768042828)(…/AppData/Roaming/Typora/typora-user-images/1652081313901.png)]
核心概念:
Seerver(Broker):接受客户端的连接,实现AMQP实体服务
Connection:连接,应用程序与Broker的网络连接TCP/IP 三次握手和四次挥手
Channel:网络信道,几乎所有的操作都在channel中进行,Channel是进行消息读写的通道,客户端可以建立对各Channel,每个Channel代表一个会话任务
Message:消息,服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级,延迟等高级特性,Body则就是消息内容
Virtual Host:虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exchange和Queue,同一个虚拟主机里面不能有相同名字的Exchange
Exchange:交换机,接受消息,根据路由键发送消息到绑定的队列 不具备消息存储能力
Bindings :Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key
Routing key:路由规则,虚拟机可以用它来确定如何路由一个特定消息
Queue:队列,消息队列,保存消息并将它们转发给消费者
九、fanout(订阅)模式案例
- 消费者,用多线程模拟两个消费则
public class Consumer{
public static void main(String[] args) {
new Thread(runnable,"线程一").start();
new Thread(runnable,"线程二").start();
}
private static Runnable runnable = new Runnable() {
private static final String EXCHANGE_NAME ="aaa";
@SneakyThrows
@Override
public void run() {
//创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//声明交换机 durable设为true 表示交换机持久化
channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true);
//声明临时队列 当消费者断开与队列的连接时,队列就自动删除 队列名称随机
String queueName = channel.queueDeclare().getQueue();
//绑定交换机与队列
channel.queueBind(queueName, EXCHANGE_NAME, "");
//接收消息
System.out.println(Thread.currentThread().getName()+"开始接收消息");
//接收消息
DeliverCallback deliverCallback =(consumerTag,message) ->{
System.out.println(Thread.currentThread().getName()+"接收到的消息是"+new String(message.getBody(),"UTF-8"));
};
//消费者取消消息
CancelCallback cancelCallback =(consumerTag)->{
System.out.println("消费者取消消费"+"::"+consumerTag);
};
channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
System.in.read();
channel.close();
connection.close();
}
};
}
- 生产者
public class Producer {
private static final String EXCHANGE_NAME ="aaa";
public static void main(String[] args) throws Exception {
//创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//声明交换机 durable设为true 表示交换机持久化
channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true);
//发送消息
String message = "急急急急";
channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes("UTF-8"));
channel.close();
connection.close();
}
}
十、direct(路由)模式
路由模式只是在订阅模式基础上,交换机改为“direct”,绑定队列指定路由key,模仿订阅案例即可
十一、topic(主题)模式
交换机改为 topic 路由规则改为模糊匹配
十二、工作模式
轮询分发工作模式
- 生产者
public class Producer {
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接
connection = connectionFactory.newConnection("生产者1号");
//3.通过连接创建通道channel
channel = connection.createChannel();
//4.通过创建交换机声明队列
/**
* queueDeclare(String queue, 队列名称
* boolean durable, 是否持久化 非持久化也会存盘,但是rabbit服务重启则就没有了
* boolean exclusive, 排他性,是否独占
* boolean autoDelete, 是否自动删除,随着最后一个消费者吧消息消费后时候把队列自动删除
* Map<String, Object> arguments 携带附属参数
* )
*/
String queName = "queue1";
channel.queueDeclare(queName, false, false, false, null);
//5.发送消息给队列queue
for (int i = 0; i < 20; i++) {
String message = "你好" + i;
channel.basicPublish("",queName,null,message.getBytes("UTF-8"));
}
System.out.println("消息发送成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//8.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }}
- 消费者
public class Worker1 {
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection =null;
Channel channel = null;
try {
//2.创建连接
connection = connectionFactory.newConnection();
//3.通过连接创建通道channel
channel = connection.createChannel();
String queueName = "queue1";
//4.声明队列
/**
* queueDeclare(String queue, 队列名称
* boolean durable, 是否持久化 非持久化也会存盘,但是rabbit服务重启则就没有了
* boolean exclusive, 排他性,是否独占
* boolean autoDelete, 是否自动删除,随着最后一个消费者吧消息消费后时候把队列自动删除
* Map<String, Object> arguments 携带附属参数
* )
*/
channel.queueDeclare(queueName, false, false, false, null);
//5.消费者消费消息
/**
*basicConsume(String queue, 消费哪个队列
* boolean autoAck, 消费成功之后是否要自动应答 true 代表自动应答,false代表收到应答
* DeliverCallback deliverCallback, 收到消息后用来处理消息的回调对象
* CancelCallback cancelCallback 消费者取消时的回调对象
* )
*/
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println("Worker1消费消息是" + new String(message.getBody(), "UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
System.out.println("消费者取消消费");
}
});
System.out.println("Worker1开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//7.关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// worker2
public class Worker2 {
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("139.196.90.55");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection =null;
Channel channel = null;
try {
//2.创建连接
connection = connectionFactory.newConnection();
//3.通过连接创建通道channel
channel = connection.createChannel();
String queueName = "queue1";
//4.声明队列
channel.queueDeclare(queueName, false, false, false, null);
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println("Worker2消费消息是" + new String(message.getBody(), "UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
System.out.println("消费者取消消费");
}
});
System.out.println("Worker2开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//7.关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
公平分发工作模式
- 区别
- 消费者开启手动应答
- 开启QOS qos数根据服务器性能设置, qos限制一次消费的消息数量
Channel finalChannel = channel;
channel.basicQos(1); //设置qos
channel.basicConsume(queueName, false, new DeliverCallback() { //开启手动应答
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println("Worker1消费消息是" + new String(message.getBody(), "UTF-8"));
//开启手动应答 参数1:消息标记 参数2:fasle单个消息应答,true:批量应答
finalChannel.basicAck(message.getEnvelope().getDeliveryTag(),false);
}
}, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
System.out.println("消费者取消消费");
}
});
十三、springboot整合rabbitmq --fanout案例
- 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- ymi配置
server:
port: 8080
# rabbitmq 配置
spring:
rabbitmq:
host: 139.196.90.55
username: admin
password: admin
virtual-host: /
- 配置交换机与队列绑定
@Configuration
public class RabbitMqConfig {
public static final String EXCHANGE_NAME = "fanout_exchange";
public static final String WEIXIN_QUEUE = "weixin_queue";
public static final String EMAIL_QUEUE = "email_queue";
//声明交换机 fanout类型
@Bean
public FanoutExchange fanoutExchange(){
/**
* FanoutExchange(String name, 交换机名称
* boolean durable, 是否持久化
* boolean autoDelete 是否自动删除
* )
*/
return new FanoutExchange(EXCHANGE_NAME,false,false);
}
//声明队列
@Bean
public Queue weiXinQueue(){
return new Queue(WEIXIN_QUEUE, false);
}
@Bean
public Queue emailQueue(){
return new Queue(EMAIL_QUEUE, false);
}
//完成交换机与队列的绑定
@Bean
public Binding weiXinBinding(){
return BindingBuilder.bind(weiXinQueue()).to(fanoutExchange());
}
@Bean
public Binding emailBinding(){
return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
}
}
- 生产者
@Component
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(){
String routingKey = "";
for (int i = 0; i < 10; i++) {
String message = "真的好" + i;
rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE_NAME,routingKey,message);
}
}
}
- 消费者
//微信
@Component
@RabbitListener(queues = {RabbitMqConfig.WEIXIN_QUEUE}) //指定监听的队列
public class WeiXin {
@RabbitHandler
public void receiveMessage(String message){
System.out.println(RabbitMqConfig.WEIXIN_QUEUE+"---接收到的信息是---->"+message);
}
}
//email
@Component
@RabbitListener(queues = {RabbitMqConfig.EMAIL_QUEUE}) //指定监听的队列
public class Email {
@RabbitHandler
public void receiveMessage(String message){
System.out.println(RabbitMqConfig.EMAIL_QUEUE+"---接收到的信息是---->"+message);
}
}
- 测试
@Test
void test1(){
producer.sendMessage();
}
十四、direct模式–springboot
- 只是在绑定交换机和队列时加个路由key
@Bean
public DirectExchange directExchange(){
return new DirectExchange(EXCHANGE_NAME_DIRECT, false, false);
}
@Bean
public Binding emailBinding1(){
return BindingBuilder.bind(emailQueue()).to(directExchange()).with("routingKeyEmail");
}
十五、使用注解完成 topic主题模式
- 生产者
@Component
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendTopicMessage(){
String routingKey = "com.email";
for (int i = 0; i < 10; i++) {
String message = "真的好" + i;
rabbitTemplate.convertAndSend("topic_exchange",routingKey,message);
}
}}
- 消费者+绑定队列与交互机+指定类型
//email
@Component
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "email_topic_queue",durable = "false",autoDelete = "false"),
exchange = @Exchange(value = "topic_exchange",type = ExchangeTypes.TOPIC),
key = "#.email.#"
))
public class Email {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("email_topic_queue---接收到的信息是---->"+message);
}
}
//weixin
@Component
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "weixin_topic_queue",durable = "false",autoDelete = "false"),
exchange = @Exchange(value = "topic_exchange",type = ExchangeTypes.TOPIC),
key = "*.weixin.#"
))
public class WeiXin {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("weixin_topic_queue---接收到的信息是---->"+message);
}
}
十六、过期时间TTL
过期时间ttl表示可以对消息设置预期的时间,在时间内可以被消费者消费,过了时间自动删除
两种ttl设置方式:
- 第一种:通过队列属性设置,整个队列中的消息都有相同过期时间
- 第二种:单独对每条消息设置ttl。
如果上述两种方法同时使用,则消息过期时间以较小的为准。
给队列设置过期时间,消息过期后可进入死信队列
16.1、给队列配置ttl (第一种)
- 配置类中配置队列过期时间
@Configuration
public class TtlRabbitMqConfig {
@Bean
public DirectExchange ttlDirectExchange() {
return new DirectExchange("ttl_direct_exchange", false, false);
}
@Bean
public Queue ttlQueue(){
HashMap<String,Object> map = new HashMap<>();
//设置过期时间 5秒
map.put("x-message-ttl", 5000);
return new Queue("ttl_queue", false, false, false, map);
}
@Bean
public Binding ttlBinding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
}
- 发送消息
@Component
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendTtlMessage(){
String routingKey = "ttl";
String message = "真的好";
rabbitTemplate.convertAndSend("ttl_direct_exchange",routingKey,message);
}}
//测试
@Test
void test1(){
producer.sendTtlMessage();
}
16.2、给消息配置ttl
public void sendTtlMessage(){
String routingKey = "ttl";
String message = "真的好";
rabbitTemplate.convertAndSend("ttl_direct_exchange",routingKey,message,new MessagePostProcessor(){
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//给消息设置过期时间
message.getMessageProperties().setExpiration("3000");
return message;
}
});
}
十七、死信队列
死信队列-: 当消息在队列中变成死信后,可根据绑定让消息进入死信队列
三种消息进入死信队列
- 消息被拒绝
- 消息过期
- 队列达到最大长度
模拟消息过期
@Configuration
public class TtlRabbitMqConfig {
@Bean
public DirectExchange ttlDirectExchange() {
return new DirectExchange("ttl_direct_exchange", false, false);
}
@Bean
public Queue ttlQueue(){
HashMap<String,Object> map = new HashMap<>();
//设置过期时间 5秒
map.put("x-message-ttl", 5000);
//设置绑定的死信交换机 过期队列绑定死信交换机,过期消息进入死信队列
map.put("x-dead-letter-exchange", "dead_direct_exchange");
map.put("x-dead-letter-routing-key", "dead");
return new Queue("ttl_queue", false, false, false, map);
}
@Bean
public Binding ttlBinding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
//声明死信队列交换机
@Bean
public DirectExchange deadDirectExchange(){
return new DirectExchange("dead_direct_exchange", true,false);
}
//声明死信队列
@Bean
public Queue deadQueue(){
return new Queue("dead_queue", true);
}
//绑定死信队列与交换机
@Bean
public Binding deadBinding(){
return BindingBuilder.bind(deadQueue()).to(deadDirectExchange()).with("dead");
}
}
十八、发布消息确认 (交换机)
确认消息发送到交换机
- 配置类
# rabbitmq 配置
spring:
rabbitmq:
host: 139.196.90.55
username: admin
password: admin
virtual-host: /
publisher-confirm-type: correlated #开启发布确认
- 回调接口
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init(){
//注入
rabbitTemplate.setConfirmCallback(this);
}
/**
* 消息回调
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String s) {
String id =correlationData != null ? correlationData.getId() : "";
if (!ack){
System.out.println("交换机未收到消息::id=="+id + "::cause::"+s);
}else {
System.out.println("交换机收到消息::id=="+id + "::cause::"+s);
}
}
}
- 发送消息
public void sendTtlMessage(){
String routingKey = "ttl";
String message = "真的好";
CorrelationData correlationData = new CorrelationData("1"); //设置id
rabbitTemplate.convertAndSend("ttl_direct_exchange",routingKey,message,correlationData);
}
十九、消息退回(队列)
交换机未发送到队列
需要开启配置:
spring:
rabbitmq:
publisher-confirm-type: correlated #开启发布确认
publisher-returns: true #交换机发给队列失败 退回
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init(){
//注入 交换机回退接口
rabbitTemplate.setConfirmCallback(this);
//注入 队列回退接口
rabbitTemplate.setReturnsCallback(this);
}
/**
* 消息回调 发送到交换机
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String s) {
String id =correlationData != null ? correlationData.getId() : "";
if (!ack){
System.out.println("交换机未收到消息::id=="+id + "::cause::"+s);
}else {
System.out.println("交换机收到消息::id=="+id + "::cause::"+s);
}
}
// 交换机发送给队列不成功回调
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息未发送到队列::"+returnedMessage.getMessage()+"exchange::"+returnedMessage.getExchange()+"::routingkey::"+returnedMessage.getRoutingKey());
}
}
二十、消费可靠性
消费出现异常时,如何保证消费可靠性
解决消息重试的几种方案:
1.控制重发的次数
2.try+catch+手动ack
3.try+catch +手动ack+死信队列+人工干预
可参考:
https://blog.csdn.net/qq_41432730/article/details/120723446