消息确认
消息确认分为手动确认以及自动确认
我们可以在源文件中找到basicConsume的方法
String basicConsume(String queue, boolean autoAck, Consumer callback) throws
IOException;
其中queue代表队列的名称,autoAck是手动确认和自动确认的开关,callback一个回调函数,用于处理从队列中消费的消息。
自动确认
autoAck=(True)只要RabbitMQ把消息发出去即为确认,不管消费者是否收到了信息,这用于对于消费者返回不高的情况,有点类似UDP的应答。
手动确认
autoAck=(Fasle)
需要等待消费者给予回馈,当收到确认信号后才可以进行下一步的发送,当长时间没有收到信息后就会将其再次进入队列中,继续投递。
Ready: 等待投递给消费者的消息数
Unacked: 已经投递给消费者, 但是未收到消费者确认信号的消息数。
肯定手动确认
Channel.basicAck(long deliveryTag, boolean multiple)
deliveryTag:在每一个通道都是唯一的,当消费者进行确认消息的时候用相应的通道进行确认。
multiple::是否批量确认,以减缓流量压力。
否定手动确认
Channel.basicNack(long deliveryTag, boolean multiple,
boolean requeue)
requeue:表达拒绝后该如何处理如果是true,则直接存入队列,发送下一个消费者,如果为false则移除。
在AMQP有三种策略
即为
AcknowledgeMode.NONE//如果出现了消息失败,则消息就会丢失
AcknowledgeMode.AUTO(默认)//出现了消息失败不确认消息
AcknowledgeMode.MANUAL//如果没有确认会重新投递消息
在上述过程中每次重新投递都会增加RabbitMQ的工作量。
发送方确认
comfirm确认模式
生产者到Exchange交换机之间
Producer 在发送消息的时候, 对发送端设置⼀个ConfirmCallback的监听, ⽆论消息是否到达Exchange, 这个监听都会被执⾏, 如果Exchange成功收到, ACK( Acknowledge character , 确认字符)为true, 如果没收到消息, ACK就为false。
return退回模式
多用于交换机到消费者之间
消息到达Exchange之后, 会根据路由规则匹配, 把消息放⼊Queue中. Exchange到Queue的过程, 如果⼀条消息⽆法被任何队列消费(即没有队列与消息的路由键匹配或队列不存在等), 可以选择把消息退回给发送者. 消息退回给发送者时, 我们可以设置⼀个返回回调⽅法, 对消息进⾏处理。
持久性
持久化是为了防止MQ的服务器在意外或者关闭后,重启后资源消失而进行的操作,就好像RAM和ROM的区别
交换机持久化
ExchangeBuilder.topicExchange(Constant.ACK_EXCHANGE_NAME).durable(true).build()
队列持久化
QueueBuilder.durable(Constant.ACK_QUEUE).build();
消息持久化
channel.basicPublish("",QUEUE_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getBytes());
在上述持久化中,如果队列和消息其中一个没有持久化,那么消息队列的信息就会消失。
注:当消息全部设置持久化,那么服务器的RabbitMQ性能会大打折扣。
TTL
TTL的两种设置方式
⼀是设置队列的TTL, 队列中所有消息都有相同的过期时间.
二是对消息本⾝进⾏单独设置, 每条消息的TTL可以不同. 如果两种⽅法⼀起使⽤, 则消息的TTL以两者之间较⼩的那个数值为准.
死信队列
死信(dead message) 简单理解就是因为种种原因, ⽆法被消费的信息, 就是死信。
有死信, ⾃然就有死信队列. 当消息在⼀个队列中变成死信之后,它能被重新被发送到另⼀个交换器中,这个交换器就是DLX( Dead Letter Exchange ), 绑定DLX的队列, 就称为死信队列(Dead Letter Queue,简称DLQ).
延迟队列
可以通过TTL和死信队列实现延迟队列。
但是当出现两个过期的数据的时候就会出现在根据消息时间长的消息一起释放的情况。
还有延迟队列插件,但是相对来说比较死板。
插件网址
消息分发
RabbitMQ队列拥有多个消费者时, 队列会把收到的消息分派给不同的消费者. 每条消息只会发送给订阅列表⾥的⼀个消费者. 这种⽅式⾮常适合扩展, 如果现在负载加重,那么只需要创建更多的消费者来消费处理消息即可
限流
RabbitMQ提供了限流机制, 可以控制消费端⼀次只拉取N个请求。
通过设置prefetchCount参数, 同时也必须要设置消息应答⽅式为⼿动应答。
prefetchCount: 控制消费者从队列中预取(prefetch)消息的数量, 以此来实现流控制和负载均衡.
负载均衡
在有两个消费者的情况下,⼀个消费者处理任务⾮常快, 另⼀个⾮常慢,就会造成⼀个消费者会⼀直很忙, ⽽另⼀个消费者很闲. 这是因为 RabbitMQ 只是在消息进⼊队列时分派消息. 它不考虑消费者未确认消息的数量.
import com.bite.rabbitmq.constant.Constant;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class QosQueueListener {
//指定监听队列的名称
@RabbitListener(queues = Constant.QOS_QUEUE)
public void ListenerQosQueue(Message message, Channel channel) throws
Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
System.out.printf("接收到消息: %s, deliveryTag: %d%n", new
String(message.getBody(),"UTF-8"), deliveryTag);
//3. ⼿动签收
channel.basicAck(deliveryTag, true);
}
//指定监听队列的名称
@RabbitListener(queues = Constant.QOS_QUEUE)
public void ListenerQueue2(Message message, Channel channel) throws
Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
System.out.printf("消费者2接收到消息: %s, deliveryTag: %d%n", new
String(message.getBody(),"UTF-8"), deliveryTag);
Thread.sleep(100);
//3. ⼿动签收
channel.basicAck(deliveryTag, true);
}
}