RabbitMQ消息队列(六)-消息任务分发与消息ACK确认机制(.Net Core版)
在前面一章介绍了在.Net Core中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消费者消费同一个队列怎么办?如果这几个消费者分任务的权重不同怎么办?怎么把同一个队列不同级别的任务分发给不同的消费者?如果消费者异常离线怎么办?不要着急,后面将慢慢解开面纱。我们将结合实际的应用场景来讲解更多的高级用法。
任务分发机制
设想如果把每个消息当做一个任务,生产者把任务发布到RabbitMQ,然后Consumer接收消息处理任务,如果我们发现一个Consumer不能完成任务处理怎么办呢,我们会增加Consumer的数量。由一个Consumer增加到两个Consumer,如图由C变为C1和C2共同来分单工作。如果C1和C2是完全一样的,那RabbitMQ会将任务平均分发到两个消费者。

如下我们
新建ProductAckDemo开发布订阅内容
新建ConsumerAckDemo1和ConsumerAckDemo2项目来订阅同一个队列在接收到消息后sleep1秒模拟任务处理的时间。
ProductAckDemo代码,生产100条带编号的消息:
using System;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events; namespace ProductAckDemo
{
class Program
{
static void Main(string[] args)
{
String exchangeName = "wytExchange";
String routeKeyName = "wytRouteKey";
String message = "Task --"; ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "192.168.63.129";
factory.Port = ;
factory.VirtualHost = "/wyt";
factory.UserName = "wyt";
factory.Password = "wyt"; using (IConnection connection=factory.CreateConnection())
{
using (IModel channel=connection.CreateModel())
{
channel.ExchangeDeclare(exchange: exchangeName, type: "direct", durable: true, autoDelete: false, arguments:null); channel.BasicQos(prefetchSize: , prefetchCount: , global: false); IBasicProperties properties = channel.CreateBasicProperties();
properties.Persistent = true; for (int i = ; i < ; i++)
{
Byte[] body = Encoding.UTF8.GetBytes(message+i); channel.BasicPublish(exchange: exchangeName, routingKey: routeKeyName, basicProperties: properties, body: body);
}
}
}
}
}
}
ConsumerAckDemo1与ConsumerAckDemo2代码(一样的)
using System;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events; namespace ConsumerAckDemo1
{
class Program
{
static void Main(string[] args)
{
String exchangeName = "wytExchange";
String queueName = "wytQueue";
String routeKeyName = "wytRouteKey"; ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "192.168.63.129";
factory.Port = ;
factory.VirtualHost = "/wyt";
factory.UserName = "wyt";
factory.Password = "wyt"; using (IConnection connection=factory.CreateConnection())
{
using (IModel channel=connection.CreateModel())
{
channel.ExchangeDeclare(exchange: exchangeName, type: "direct", durable: true, autoDelete: false, arguments: null); channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null); channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routeKeyName, arguments: null); channel.BasicQos(prefetchSize: , prefetchCount: , global: false); EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
Byte[] body = ea.Body;
String message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message); Thread.Sleep();
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
}; channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
}
}
打开两个中断窗口分别执行ConsumerAckDemo1和ConsumerAckDemo2。确定两个程序处于订阅状态,然后执行ProductAckDemo程序。

看到上面的图结果就一目了然了。因为两个程序的时间相同所以任务是完全平均分发到两个消费者的。我们修改下ConsumerAckDemo2脚本的sleep时间为2秒,看下结果会怎么样。

可以看到ConsumerAckDemo1共收到66条消息,ConsumerAckDemo2脚本收到34条消息,基本是按照2:1来分配。那RabbitMQ是如何来保证这样的分发机制呢,下面看RabbitMQ是如何通过ACK确认机制来实现任务分发的。
ACK消息确认机制
首先RabbitMQ支持消息确认机制来本证消息被consumer正常处理,当然也可以通过no-ack不使用确认机制。RabbitMQ默认是使用ACK确认机制的。当Consumer接收到RabbitMQ发布的消息时需要在适当的时机发送一个ACK确认的包来告知RabbitMQ,自己接收到了消息并成功处理。所以前面讲到适当的时机建议是在处理完消息任务后发送。正如我们之前的代码
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
...
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);//手动发送ACK应答
...
}; channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);//不自动应答
那如果不发送会怎样呢?
在RabbitMQ中有一个prefetch_count的概念,这个参数的意思是允许Consumer最多同时处理几个任务。我的版本的RabbitMQ默认这个参数是1,也就是说如果某一个Consumer在收到消息后没有发送ACK确认包,RabbitMQ就会任务Consumer还在处理任务,当有1个消息都没有发送ACK确认包时,RabbitMQ就不会再发送消息给该Consumer。
我们把ConsumerAckDemo2的sleep时间改回1秒,并且注释掉ACK确认。

发现ConsumerAckDemo2只收到1条消息。通过WEB管理工具也可以看到有1条消息是没有被ACK确认的

当然任务并不会一直卡在这里,在这是RabbitMQ任务ConsumerAckDemo2在处理这1个任务。如果ConsumerAckDemo2忽然终止RabbitMQ会重新分发任务。如果我终止ConsumerAckDemo2,1条任务被重新分发到了ConsumerAckDemo1。再查看下WEB管理工具,unackd已经为0

如果Consumer数量很多或者希望每个Consumer同时只处理一个任务可以通过在Consumer中设置PrefetchCount来实现更加均匀的任务分发。
channel.BasicQos(prefetchSize: , prefetchCount: , global: false);
如下我修改PrefetchCount为1。在WEB管理插件中可以看到已经有一个Consumer的PrefetchCount为1了。

RabbitMQ消息队列(六)-消息任务分发与消息ACK确认机制(.Net Core版)的更多相关文章
- (六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版)
原文:(六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版) 在前面一章介绍了在PHP中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消 ...
- (九)RabbitMQ消息队列-通过Headers模式分发消息
原文:(九)RabbitMQ消息队列-通过Headers模式分发消息 Headers类型的exchange使用的比较少,以至于官方文档貌似都没提到,它是忽略routingKey的一种路由方式.是使用H ...
- 使用jedis实现Redis消息队列(MQ)的发布(publish)和消息监听(subscribe)
前言: 本文基于jedis 2.9.0.jar.commons-pool2-2.4.2.jar以及json-20160810.jar 其中jedis连接池需要依赖commons-pool2包,json ...
- 第1节 kafka消息队列:11、kafka的数据不丢失机制,以及kafka-manager监控工具的使用;12、课程总结
12.kafka如何保证数据的不丢失 12.1生产者如何保证数据的不丢失 kafka的ack机制:在kafka发送数据的时候,每次发送消息都会有一个确认反馈机制,确保消息正常的能够被收到 如果是同步模 ...
- Azure Messaging-ServiceBus Messaging消息队列技术系列4-复杂对象消息是否需要支持序列化和消息持久化
在上一篇中,我们介绍了消息的顺序收发保证: Azure Messaging-ServiceBus Messaging消息队列技术系列3-消息顺序保证 在本文中我们主要介绍下复杂对象消息是否需要支持序列 ...
- 【消息队列】kafka是如何保证消息不被重复消费的
一.kafka自带的消费机制 kafka有个offset的概念,当每个消息被写进去后,都有一个offset,代表他的序号,然后consumer消费该数据之后,隔一段时间,会把自己消费过的消息的offs ...
- 获取和设置消息队列的属性msgctl,删除消息队列
消息队列的属性保存在系统维护的数据结构msqid_ds中,用户可以通过函数msgctl获取或设置消息队列的属性. int msgctl(int msqid, int cmd, struct msqid ...
- rabbitmq 延时队列 插件方式实现 每条消息都延时自己时间
上篇文章的延时是加到队列上的 通过死信过时推送 ,缺点就是不能每条消息定义自己的过时时间而且每次有新的过时时间,要新建一个交换机和队列 https://www.cnblogs.com/brady-wa ...
- RabbitMQ消息队列(五):Routing 消息路由 2[原]
上一篇文章使用的是Direct的Exchange,但是没有指定Queue的名字,这样只能是先运行Consumer之后,Producer在运行发消息Consumer才能收到,否则先运行Producer发 ...
随机推荐
- 2018-2019-2 网络对抗技术 20162329 Exp4 恶意代码分析
目录 Exp4 恶意代码分析 一.基础问题 问题1: 问题2: 二.系统监控 1. 系统命令监控 2. 使用Windows系统工具集sysmon监控系统状态 三.恶意软件分析 1. virustota ...
- windows10 docker镜像存储位置修改
=====================================下面做法无效,无法成功启动docker=================================== 安装Docker ...
- Django——小结
课程介绍 MVC MVC框架的核心思想是:解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容 M:Model,主要封装对数据库层的访问,对数据库中的数据进行增.删.改.查操作 ...
- Hibernate HQL ②
分页查询: - setFirstResult(int firstResult):设定从哪一个对象开始检索,参数 firstResult 表示这个对象在查询结果中的索引位置,索引位置的起始值为零.默认情 ...
- jQuery 动态绑定插件livequery的用法
- 通过命令行操作MYSQL的方法 以及导入大的SQL备份文件
运行 输入CMD 进入 命令行窗口 输入Mysql.exe的路径 如:c:/wamp/bin/mysql.exe 回车 这时出现 welcome to the mysql ...的提示 进入成 ...
- 使用mongo获取文章
先在命令行中输入mongo,进入mongo,然后 show dbs # 从结果中发现有cmb_demo_23_hacker use cmb_demo_23_hacker db.all_in_one.f ...
- ActiveMQ队列、主题模式区别
1.ActiveMQ队列模式如下图,生产者创建消息到消息中间件,再“均分给消费者”. 2.ActiveMQ主题模式如下图,生产者创建消息到消息中间件,消费者会接受到订阅的主题中所有的消息.在主题模式下 ...
- JVM,Tomcat与OSGi类加载机制比较
首先一个思维导图来看下Tomcat的类加载机制和JVM类加载机制的过程 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加载. 比如JVM启动时,会通过不同的类加载器加载 ...
- FFmpeg开发实战(五):FFmpeg 抽取音视频的视频数据
如何使用FFmpeg抽取音视频的视频数据,代码如下: // FFmpegTest.cpp : 此文件包含 "main" 函数.程序执行将在此处开始并结束. // #include ...