从Broker发送到消费者时,RabbitMQ提供了两种消息确认的方式:自动确认和显式确认。
1 自动确认
自动确认:当RabbitMQ将消息发送给消费者后,消费者端接收到消息后,不等待消息处理结束,立即自动回送一个确认回执。自动确认的用法十分简单,设置消费方法的参数autoAck为true即可,我们前边的例子都是使用的自动确认,这里不再详细演示,如下:
channel.BasicConsume(queue: "myqueue", autoAck: true, consumer: consumer);Code language: JavaScript (javascript)
注意:Broker会在接收到确认回执时删除消息,如果消费者接收到消息并返回了确认回执,然后这个消费者在处理消息时挂了,那么这条消息就再也找不回来了。
2 显式确认
我们知道自动确认可能会出现消息丢失的问题,我们不免会想到:Broker收到回执后才删除消息,如果可以让消费者在接收消息时不立即返回确认回执,等到消息处理完成后(或者完成一部分的逻辑)再返回确认回执,这样就保证消费端不会丢失消息了!这正是显式确认的思路。使用显式确认也比较简单,首先将BasicConsume方法的参数autoAck设置为false,然后在消费端使用代码 channel.BasicAck()/BasicReject()等方法 来确认和拒绝消息。看一个例子:
消费者代码如下:
class Program
{
static void Main(string[] args)
{
var factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.UserName = "guest";
factory.Password = "guest";
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
//定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
string message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine($"接受到消息【{message}】");
//以Hello开头表示是新闻类型,处理完成后确认消息
if (message.StartsWith("Hello"))
{
//这里处理消息balabala
Console.WriteLine($"【{message}】是新闻消息,处理消息并确认");
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
}
//不以Hello开头表示不是新闻类型,不进行处理,把消息退回到queue中
else
{
Console.WriteLine($"【{message}】不是新闻类型,拒绝处理");
channel.BasicReject(deliveryTag: ea.DeliveryTag, requeue: false);
}
};
Console.WriteLine("消费者准备就绪....");
//第五步:处理消息
channel.BasicConsume(queue: "myqueue",
autoAck: false,
consumer: consumer);
Console.ReadKey();
}
}
}
}Code language: JavaScript (javascript)
介绍一下代码中的两个方法: channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); 方法用于确认消息,deliveryTag参数是分发的标记,multiple表示是否确认多条。 channel.BasicReject(deliveryTag: ea.DeliveryTag, requeue: false); 方法用于拒绝消息,deliveryTag也是指分发的标记,requeue表示消息被拒绝后是否重新放回queue中,true表示放回queue中,false表示直接丢弃。
运行这两个应用程序,通过生产者发送两条消息,效果如下:
当我们修改生产者的消息:
string message = "1Hello:" + random.Next(2, 10); //传递的消息内容Code language: JavaScript (javascript)
这时候,消费者就会拒绝,并且直接丢弃该消息。
一些意外的情况:使用显式确认时,如果消费者处理完消息不发送确认回执,那么消息不会被删除,消息的状态一直是Unacked(未确认),这条消息也不会再发送给其他消费者。如果一个消费者在处理消息时尚未发送确认回执的情况下挂掉了,那么消息会被重新放入队列(状态从Unacked变成Ready),有其他消费者存在时,消息会发送给其他消费者。
消息持久化(Persistent)
怎么实现消息的持久化呢?实现的方法很简单:将exchange和queue都设置durable=true,然后在消息发布的时候设置persistent=true即可。
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
var properties = channel.CreateBasicProperties();
properties.DeliveryMode = 2;//1非持久化 2持久化
string message = "1Hello:" + random.Next(2, 10); //传递的消息内容
var body = Encoding.UTF8.GetBytes(message);
//设置消息持久化
var props = channel.CreateBasicProperties();
props.Persistent = true;
channel.BasicPublish(exchange: "myexchange",
routingKey: "mykey",
basicProperties: props,
body: body);
Console.WriteLine($"【{message}】发送到Broker成功!");
}
}Code language: JavaScript (javascript)
声明exchange和queue时设置durable=true,然后执行上边的代码,传入一条消息。重启rabbitmq后,exchange、queue和消息都会恢复。我们也可以在web管理界面设置消息持久化,如下:

现在当我们重启RabbitMQ
再次启动
发现消息还在
