【RabbitMQ.Clinet笔记】队列(Queue)
Queue参数
durable
:是否持久化, 队列默认是存放到内存中的,rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库exclusive
:是否排外的,队列在当前connection
下的channel
中是共享的,其他connection
下的channel
访问不到,当connection
关闭时队列删除。
一旦该队列被声明,其他连接无法声明相同名称的排他队列。
队列即使显示声明为durable,连接断开时(注意不是信道断开)也会被自动删除。
使用场景:系统内部跨进程调用,生产者与消费者在同一系统内。autoDelete
:是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,可以通过RabbitMQ Management
查看某个队列的消费者数量,当consumers = 0
时队列就会自动删除Arguments
:队列其他参数,Dictionary
类型x-message-ttl
消息过期时间,message在队列的最大存活时间,单位毫秒。设置后队列Features
属性显示TTL
。也可以对消息本身设置过期时间x-expires
设置队列的过期时间,如启动一个生产者或启动一个消费者,消费者运行结束后10
秒,队列会被删除x-max-length
队列最大长度x-max-length-bytes
指定队列最大内存x-dead-letter-exchange
指定死信交换机x-dead-letter-routing-key
指定死信routingKey
x-max-priority
设置优先级,优先级高的先发送consumer
x-queue-mode
可以是default
、lazy
(惰性队列)。下面对lazy队列有详细说明x-overflow
queue的溢出行为,值包括:drop-head
(删除queue头部的消息)、reject-publish
(最近发来的消息将被丢弃)、reject-publish-dlx
(拒绝发送消息到死信交换器)
控制台(RabbitMQ Management)队列属性
Features
:D
:持久化TTL
:设置了消息过期时间DLX
、DLK
:表示设置给队列设置了死信交换机和死信消息的routing key
Ready
:消息的状态,等待投递的消息Unacked
:消息的状态,已投递给消费者,但是还没收到消费者确认信号的消息。如果RabbitMQ一直没有收到消费者的确认信号,并且消费此消息的消费者已经断开连接,则RabbitMQ会安排该消息重新进入队列,等待投递给下一个消费者
惰性队列
默认情况下,当生产者将消息发送到RabbitMQ的时候,队列中的消息会尽可能地存储在内存之中,这样可以更加快速地将消息发送给消费者。即使是持久化的消息,在被写入磁盘的同时也会在内存中驻留一份备份。当RabbitMQ需要释放内存的时候,会将内存中的消息换页至磁盘中,这个操作会耗费较长的时间,也会阻塞队列的操作,进而无法接收新的消息。虽然RabbitMQ的开发者们一直在升级相关的算法,但是效果始终不太理想,尤其是在 消息量特别大的时候。
惰性队列会将接收到的消息直接存入文件系统,而不管是持久化的或者是非持久化的,这样可以减少内存的消耗,但是会增加I/O
的使用,如果消息是持久化的,那么这样的I/O
操作不可避免,惰性队列和持久化的消息可谓是“最佳拍档”。注意如果惰性队列中存储的是非持久化的消息,内存的使用率会一直很稳定,但是重启之后消息一样会丢失。
队列具备两种模式:default
和lazy
。默认的为default
模式,lazy
模式即为惰性队列的模式,可以通过调用channel.queueDeclare()
方法的时候在参数中设置,也可以通过Policy
的方式设置,如果一个队列同时使用这两种方式设置,那么Policy
的方式具备更高的优先级。如果要通过声明的方式改变已有队列的模式,那么只能先删除队列,然后再重新声明一个新的。
死信队列
DLX
(Dead-Letter-Exchange),可以称为死信交换器。当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换器中,这个交换器就是DLX
,绑定DLX
的队列就称为死信队列。
消息什么情况下变成死信:
- 消息被拒绝(channel.basicNack或channel.basicReject),并且设置requeue参数为false;
- 消息过期;
- 队列达到最大长度。
死信队列案例:
private void btnBasicPublish_Click(object sender, EventArgs e)
{
string exchangeName = "myexchange1";
string queueName = "myqueue3";
string bindingKey = "myroutingkey3";
string routingKey = "myroutingkey3";
//创建死信交换器、队列
this.CreateDeadQueue();
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchangeName, ExchangeType.Direct, true, false, null);
channel.QueueDeclare(queueName, true, false, false,
new Dictionary<string, object> {
{ "x-message-ttl", 5000 },//过期时间5s
{ "x-dead-letter-exchange", DEAD_EXCHANGE_NAME },//死信队列交换机
{ "x-dead-letter-routing-key", DEAD_QUEUE_NAME }//死信队列routingKey
});
channel.QueueBind(queueName, exchangeName, bindingKey, null);
//消息持久化
var properties = channel.CreateBasicProperties();
properties.Persistent = true;//相当于设置DeliveryMode=2
//5:发布消息
for (int i = 0; i < 5; i++)
{
var msg = Encoding.UTF8.GetBytes($"{i}:haha");
channel.BasicPublish(exchangeName, routingKey, properties, msg);
}
}
}
/// <summary>
/// 创建死信交换器、死信队列、并绑定
/// 死信交换器、队列就是一个普通交换器、队列
/// </summary>
private void CreateDeadQueue()
{
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(DEAD_EXCHANGE_NAME, ExchangeType.Direct, true, false, null);
channel.QueueDeclare(DEAD_QUEUE_NAME, true, false, false, null);
channel.QueueBind(DEAD_QUEUE_NAME, DEAD_EXCHANGE_NAME, DEAD_QUEUE_NAME, null);
}
}
延时队列
延时队列存储的对象是对应的延迟消息,延迟消息指当消息被发送到队列后,并不立即被消费者拿到消息,而是等待特定的时间后,消费者才能拿到消息。
在RabbitMQ中,延时队列通过前面的DLX
和TTL
共同作用可以模拟出延迟队列功能。
延时队列需要安装一个插件,下载
延时队列应用场景有:
- 在订单系统中,用户下单后,通常有30分钟的付款时间。如果30分钟后没有付款,则订单被取消,这里可以使用延时队列处理订单。
- 用户通过远程控制家里的智能设备在指定时间进行工作,可以将用户指定发送到延时队列,到达时间后再推送到智能设备。
如何使用
通过声明一个x-delayed-message
类型的exchange
来使用delayed-messaging
特性
x-delayed-message
是插件提供的类型,并不是rabbitmq本身的
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare("my-exchange", "x-delayed-message", true, false, args);
发送消息的时候通过在header添加x-delay
参数来控制消息的延时时间
byte[] messages= "delayed payload".getBytes("UTF-8");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-delay", 5000);
AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties.Builder().headers(headers);
channel.basicPublish("my-exchange", "", properties.build(), messages);
参考:
https://blog.csdn.net/yaomingyang/article/details/102753004