目录
消息的可靠投递
confirm 确认模式(针对生产者)
确认模式:
步骤:
1. 确认模式开启: publisher-confirm-type: correlated
2. 在rabbitTemplate定义ConfirmCallBack回调函数
首先是 开启确认模式:
然后是设置交换机和队列:
@Configuration
public class RabbitMQConfirmConfig {
public static final String EXCHANGE_NAME = "test_exchange_confirm";
public static final String QUEUE_NAME = "test_queue_confirm";
//1.交换机
@Bean("bootExchange")
public Exchange bootExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
//2.Queue 队列
@Bean("bootQueue")
public Queue bootQueue(){
return QueueBuilder.durable(QUEUE_NAME).build();
}
//3. 队列和交互机绑定关系 Binding
/*
1. 知道哪个队列
2. 知道哪个交换机
3. routing key
*/
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
return BindingBuilder
.bind(queue).to(exchange).with("confirm").noargs();
}
最后测试代码:
//1.注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testConfirm() {
//2. 定义回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData 相关配置信息
* @param ack exchange交换机 是否成功收到了消息。true 成功,false代表失败
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("confirm方法被执行了....");
if (ack) {
//接收成功
System.out.println("接收成功消息" + cause);
} else {
//接收失败
System.out.println("接收失败消息" + cause);
//做一些处理,让消息再次发送。
}
}
});
//3. 发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm111", "confirm", "message confirm....");
}
return 退回模式(针对生产者)
/**
* 回退模式: 当消息发送给Exchange后,Exchange路由到Queue失败是 才会执行 ReturnCallBack
* 步骤:
* 1. 开启回退模式:publisher-returns="true"
* 2. 设置ReturnCallBack
* 3. 设置Exchange处理消息的模式:
* 1. 如果消息没有路由到Queue,则丢弃消息(默认)
* 2. 如果消息没有路由到Queue,返回给消息发送方ReturnCallBack
*/
首先是yaml:
交换机和队列用上一个案列即可。
测试代码:
//1.注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testReturn() {
//设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true);
//2.设置ReturnCallBack
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
*
* @param message 消息对象
* @param replyCode 错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("return 执行了....");
System.out.println(message);
System.out.println(replyCode);
System.out.println(replyText);
System.out.println(exchange);
System.out.println(routingKey);
//处理
}
});
//3. 发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm", "confirm", "message confirm....");
}
总结:
Consumer Ack(针对消费者)
手动确认:
代码:
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testack(){
while (true){
}
}
@RabbitListener(queues = "test_queue_confirm",ackMode ="MANUAL" )
public void listener(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//1.接收转换消息
System.out.println(new String(message.getBody()));
//2. 处理业务逻辑
System.out.println("处理业务逻辑...");
// int i = 3/0;//出现错误
//3. 手动签收
channel.basicAck(deliveryTag,false);
} catch (Exception e) {
//e.printStackTrace();
//4.拒绝签收
/*
第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
*/
channel.basicNack(deliveryTag,true,true);
//channel.basicReject(deliveryTag,true);
}
}
Multiple 的解释
手动应答的好处是可以批量应答并且减少网络拥堵
multiple 的 true 和 false 代表不同意思
true 代表批量应答 channel 上未应答的消息
比如说 channel 上有传送 tag 的消息 5,6,7,8 当前 tag 是 8 那么此时
5-8 的这些还未应答的消息都会被确认收到消息应答
false 同上面相比
只会应答 tag=8 的消息 5,6,7 这三个消息依然不会被确认收到消息应答
Ack小结:
消息可靠性总结
消费端限流
代码:
只需要改这里的设置即可,其他的不用变
测试发送10条,消费者一直不手动应答,看看能持续接受那几条
小结:
TTL
队列里面统一过期
代码:
只需要在创建队列的时候添加这个参数即可:
单独消息过期时间:
代码:
// 消息后处理对象,设置一些消息的参数信息
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//1.设置message的信息
message.getMessageProperties().setExpiration("5000");//消息的过期时间
//2.返回该消息
return message;
}
};
//消息单独过期
rabbitTemplate.convertAndSend("test_exchange_ttl", "ttl.hehe", "message ttl....",messagePostProcessor);
注意!!:
消息过期后,只有消息在队列顶端,才会判断其是否过期(移除掉)
解释:
比如下面的代码 则不会被过期: 因为rabbitmq不会取 遍历找寻那个消息要过期了,那样会很慢,所以他直接只看第一个设置有没有过期时间,有就把他删除了,没有就算了。
死信队列
基础架构图:
产生原因:
代码实现:
死信队列:
1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx)
2. 声明死信队列(queue_dlx)和死信交换机(exchange_dlx)
3. 正常队列绑定死信交换机
设置两个参数:
* x-dead-letter-exchange:死信交换机名称
* x-dead-letter-routing-key:发送给死信交换机的routingkey
逻辑代码:
@Configuration
public class RabbitMQDeadConfig {
public static final String EXCHANGE_NAME = "test_exchange_dlx";
public static final String QUEUE_NAME = "test_queue_dlx";
public static final String DEAD_EXCHANGE_NAME = "exchange_dlx";
public static final String DEAD_QUEUE_NAME = "queue_dlx";
//1.正常交换机
@Bean("bootExchange")
public Exchange bootExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
//2.正常Queue 队列
@Bean("bootQueue")
public Queue bootQueue(){
//还有和死刑交换机进行绑定
return QueueBuilder.durable(QUEUE_NAME)
.ttl(1000*10) //设置ttl时间 模拟死刑
.deadLetterExchange(DEAD_EXCHANGE_NAME) //绑定死刑交换机
.deadLetterRoutingKey("#.dlx.#") //设置死刑key
.build();
}
//3. 队列和交互机绑定关系 Binding
/*
1. 知道哪个队列
2. 知道哪个交换机
3. routing key
*/
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
return BindingBuilder
.bind(queue).to(exchange).with("test.#").noargs();
}
//1.死刑交换机
@Bean("deadExchange")
public Exchange deadExchange(){
return ExchangeBuilder.topicExchange(DEAD_EXCHANGE_NAME).durable(true).build();
}
//2.死刑Queue 队列
@Bean("deadQueue")
public Queue deadQueue(){
//设置ttl时间 模拟死刑
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
//3. 死刑队列和死刑机绑定关系 Binding
/*
1. 知道哪个队列
2. 知道哪个交换机
3. routing key
*/
@Bean
public Binding binddeadQueueExchange(@Qualifier("deadQueue") Queue queue, @Qualifier("deadExchange") Exchange exchange){
return BindingBuilder
.bind(queue).to(exchange).with("#.dlx.#").noargs();
}
}
测试代码:
for (int i = 0; i < 20; i++) {
rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.haha","我是一条消息,我会死吗?");
}
总结:
!!延迟队列:
应用场景:下订单30分钟之内需要确认这种。
延迟队列:TTL+死性队列
代码:
就是上面的TTl+死刑队列相组合即可完成
总结:
目录
应用问题
以下问题主要是思路的讲解 建议看视频很短的:
视频链接
消息可靠性保障–消息补偿
主要是 确保消息能够百分百传送到,
消息幂等性保障
集群问题
补充:
镜像队列:
搭建集群,一般可能主节点挂了,他队列里面的信息,从节点就无法访问了,于是需要搭建镜像队列。
负载均衡-HAProxy
集群搭建好了,我们向哪个IP写代码比较合适嘞?所以这时候需要一个管理所有的IP的插件,我们向插件写,他自动取分配可用的IP地址。