一、常用的消息模式

我们在工作的使用中,经常会遇到多个消费者监听同一个队列的情况,模型如下图所示:



当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢;

主要有两种模式:

1、轮询模式的分发:一个消费者一条,按均分配;

2、公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;

二、轮询模式(Round-Robin)

该模式接收消息是当有多个消费者接入时,消息的分配模式是一个消费者分配一条,直至消息消费完成;

2.1 生产者发消息到队列

    public static void SendRoundRobinMessage()
{
try
{
var conn = GetConnection();
var channel = conn.CreateModel();
channel.QueueDeclare(QUEUE_NAME, false, false, false, null);
for(var i = 0; i < 50; i++)
{
var body = Encoding.UTF8.GetBytes(i.ToString());
channel.BasicPublish("", QUEUE_NAME, null, body);
}
Console.WriteLine("消息发送完成!");
channel.Close();
conn.Close();
}
catch (Exception ex)
{
throw ex;
}
}

2.2 消费者1代码

消费者1每处理完一次消息,线程休息1秒;

		/// <summary>
/// 轮询分发消费者1
/// </summary>
static void SimpleConsumer1()
{
//new rabbitMqTest.RabbitMQ.MQUtils().GetMessage();
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory
{
UserName = "admin",//用户名
Password = "admin",//密码
HostName = "127.0.0.1"//rabbitmq ip
};
//创建连接
var connection = factory.CreateConnection();
//创建通道
var channel = connection.CreateModel();
//事件基本消费者
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
//接收到消息事件
consumer.Received += (ch, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine($"Simple Consumer1 收到消息: {message},时间{DateTime.Now}");
Thread.Sleep(1000);
//确认该消息已被消费
//channel.BasicAck(ea.DeliveryTag, false);
};
//启动消费者 设置为手动应答消息
channel.BasicConsume("queue_test", true, consumer);
Console.WriteLine("Simple Consumer1 消费者已启动");
Console.ReadKey();
channel.Dispose();
connection.Close();
}

消费者接收消息如图:

2.3 消费者2代码

消费者2每处理完一次消息,线程休息3秒;

		/// <summary>
/// 轮询分发消费者2
/// </summary>
static void SimpleConsumer2()
{
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory
{
UserName = "guest",//用户名
Password = "guest",//密码
HostName = "127.0.0.1"//rabbitmq ip
};
//创建连接
var connection = factory.CreateConnection();
//创建通道
var channel = connection.CreateModel();
//事件基本消费者
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
//接收到消息事件
consumer.Received += (ch, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine($"Simple Consumer2 收到消息: {message},时间{DateTime.Now}");
Thread.Sleep(3000);
//确认该消息已被消费
//channel.BasicAck(ea.DeliveryTag, false);
};
//启动消费者 设置为手动应答消息
channel.BasicConsume("queue_test", true, consumer);
Console.WriteLine("Simple 2 消费者已启动");
Console.ReadKey();
channel.Dispose();
connection.Close();
}

消费者接收消息如图:

2.4 轮询分发小结

消费者1和2的消息处理能力不同,但是最后处理的消息条数相同,是“按均分配”。

三、公平分发(Fair Dispatch)

由于消息接收者处理消息的能力不同,存在处理快慢的问题,我们就需要能者多劳,处理快的多处理,处理慢的少处理;

3.1 生产者发消息到队列

代码如下:

  public static void SendQosMessage()
{
try
{
var conn = GetConnection();
var channel = conn.CreateModel();
channel.QueueDeclare(QUEUE_NAME, false, false, false, null);
channel.BasicQos(0,1,false);
for (var i = 0; i < 50; i++)
{
var body = Encoding.UTF8.GetBytes(i.ToString());
channel.BasicPublish("", QUEUE_NAME, null, body);
}
Console.WriteLine("消息发送完成!");
channel.Close();
conn.Close();
}
catch (Exception ex)
{
throw ex;
}
}

3.2 消费者1代码如下

为了模拟处理消息的时长,每处理完一条消息让线程休息1s

		static void SimpleConsumer1()
{
//new rabbitMqTest.RabbitMQ.MQUtils().GetMessage();
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory
{
UserName = "admin",//用户名
Password = "admin",//密码
HostName = "127.0.0.1"//rabbitmq ip
}; //创建连接
var connection = factory.CreateConnection();
//创建通道
var channel = connection.CreateModel();
channel.BasicQos(0, 1, false);
//事件基本消费者
EventingBasicConsumer consumer = new EventingBasicConsumer(channel); //接收到消息事件
consumer.Received += (ch, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine($"Simple Consumer1 收到消息: {message},时间{DateTime.Now}");
Thread.Sleep(1000);
//确认该消息已被消费
channel.BasicAck(ea.DeliveryTag, false);
};
//启动消费者 设置为手动应答消息
channel.BasicConsume("queue_test", false, consumer);
Console.WriteLine("Simple 1 消费者已启动");
Console.ReadKey();
channel.Dispose();
connection.Close();
}

处理的消息结果如图:

3.3 消费者2处理消息较消费者1慢,代码如下

为了模拟处理消息的时长,每处理完一条消息让线程休息3s

static void SimpleConsumer2()
{
//new rabbitMqTest.RabbitMQ.MQUtils().GetMessage();
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory
{
UserName = "admin",//用户名
Password = "admin",//密码
HostName = "127.0.0.1"//rabbitmq ip
}; //创建连接
var connection = factory.CreateConnection();
//创建通道
var channel = connection.CreateModel();
channel.BasicQos(0, 1, false);
//事件基本消费者
EventingBasicConsumer consumer = new EventingBasicConsumer(channel); //接收到消息事件
consumer.Received += (ch, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine($"Simple Consumer2 收到消息: {message},时间{DateTime.Now}");
Thread.Sleep(3000);
//确认该消息已被消费
channel.BasicAck(ea.DeliveryTag, false);
};
//启动消费者 设置为手动应答消息
channel.BasicConsume("queue_test", false, consumer);
Console.WriteLine("Simple 2 消费者已启动");
Console.ReadKey();
channel.Dispose();
connection.Close();
}

处理消息的结果如图:

3.4 处理消息的结果

从结果可以看到,消费者1在相同时间内,处理了更多的消息;以上代码我们实现了公平分发模式;

3.5 注意点

(1)消费者一次接收一条消息,代码channel.BasicQos(0, 1, false);

(2) 公平分发需要消费者开启手动应答,关闭自动应答

关闭自动应答代码channel.BasicConsume("queue_test", false, consumer);

消费者开启手动应答代码:channel.BasicAck(ea.DeliveryTag, false);

四、小结

(1)当队列里消息较多时,我们通常会开启多个消费者处理消息;公平分发和轮询分发都是我们经常使用的模式。

(2)轮询分发的主要思想是“按均分配”,不考虑消费者的处理能力,所有消费者均分;这种情况下,处理能力弱的服务器,一直都在处理消息,而处理能力强的服务器,在处理完消息后,处于空闲状态;

(3) 公平分发的主要思想是"能者多劳",按需分配,能力强的干的多。

参考文档: https://www.rabbitmq.com/tutorials/tutorial-one-dotnet.html

RabbitMQ的轮询模式和公平分发的更多相关文章

  1. STM32学习笔记(五) USART异步串行口输入输出(轮询模式)

    学习是一个简单的过程,只要有善于发掘的眼睛,总能学到新知识,然而如何坚持不懈的学习却很困难,对我亦如此,生活中有太多的诱惑,最后只想说一句勿忘初心.闲话不多扯,本篇讲诉的是异步串行口的输入输出,串口在 ...

  2. 【python】-- RabbitMQ 队列消息持久化、消息公平分发

    RabbitMQ 队列消息持久化 假如消息队列test里面还有消息等待消费者(consumers)去接收,但是这个时候服务器端宕机了,这个时候消息是否还在? 1.队列消息非持久化 服务端(produc ...

  3. nginx 轮询模式 nginx_upstream_jvm_route 插件安装

    使用nginx_upstream_jvm_route插件的目的是为了保证在轮询机制下的session的共享 前提:源码方式安装nginx.patch命令 1.下载nginx_upstream_jvm_ ...

  4. demo rabbitmq topic(主题模式),fanout(广播模式),轮询分发,确认接收Ack处理

    //durable = true 代表持久化 交换机和队列都要为true ,持久代表服务重启,没有处理的消息依然存在 //topic 根据不同的routkey 发送和接收信息 //fanout 广播模 ...

  5. RabbitMQ消息分发轮询

    一,前言 如果我们一个生产者,对应多个消费者,rabbitmq 会发生什么呢 二,消息分发轮询 前提条件:1个生产者  ---->  多个消费者,且no_ack=True (启动三次生产者) ① ...

  6. 【python】-- RabbitMQ 安装、基本示例、轮询机制

    RabbitMQ MQ全称为Message Queue, 是一种分布式应用程序的的通信方法,它是消费-生产者模型的一个典型的代表,producer往消息队列中不断写入消息,而另一端consumer则可 ...

  7. Ajax轮询以及Comet模式—写在Servlet 3.0发布之前(转)

    2008 年的夏天,偶然在网上闲逛的时候发现了 Comet 技术,人云亦云间,姑且认为它是由 Dojo 的 Alex Russell 在 2006 年提出.在阅读了大量的资料后,萌发出写篇 blog ...

  8. polling轮询和comet

    comet:(原意:彗星) Comet is a web application model in which a long-held(held:保留) HTTP request allows a w ...

  9. S5PV210 串口配置与实验(轮询方式)

    S5PV210 UART 相关说明 通用异步收发器简称 UART, 即 UNIVERSAL ASYNCHRONOUS RECEIVER AND TRANSMITTER,它用来传输串行数据.发送数据时, ...

随机推荐

  1. C++关于容器vector的使用方法以及#ifdef #else #endif #if #ifndef 的使用

    //此处根据0还是1来判断具体使用那一段主函数 #if 1 #define WAY #endif #ifdef WAY #include <iostream> #include<st ...

  2. day23作业

    # 作业: # 1.把登录与注册的密码都换成密文形式 info = {"tom":"202cb962ac59075b964b07152d234b70"} def ...

  3. L6循环神经网络

    循环神经网络 本节介绍循环神经网络,下图展示了如何基于循环神经网络实现语言模型.我们的目的是基于当前的输入与过去的输入序列,预测序列的下一个字符.循环神经网络引入一个隐藏变量HHH,用HtH_{t}H ...

  4. python操作数据库-SQLSERVER-pyodbc

    刚开始学python时,大家都习惯用pymssql去读写SQLSERVER.但是实际使用过程中,pymssql的读写性能以及可靠性的确不如pyodbc来的好. 正如微软官方推荐使用pyodbc库,作为 ...

  5. Fiddler抓包过滤

    1.User Fiters启用 2.Action Action:Run Filterset now是否运行,Load Filterset加载,Save Filterset保存: 3.Hosts过滤 Z ...

  6. vue2.x学习笔记(十七)

    接着前面的内容:https://www.cnblogs.com/yanggb/p/12616847.html. 动态组件&异步组件 在前面学习组件基础的时候学习过动态组件,官方文档给出过一个例 ...

  7. APP测试和WEB测试区别

    App测试web测试的区别 单纯从功能测试的层面上来讲的话,APP 测试.web 测试 在流程和功能测试上是没有区别的 根据两者载体不一样,则区别如下: 1.兼容性测试:web端兼容浏览器,app端兼 ...

  8. Android 开发技术周报 Issue#277

    新闻 Android 11界面再调整:加入快速截屏.多任务向国产ROM看齐 最新版Android 11推送 谷歌Pixel 5被曝光:支持反向充电 4月Android系统版本分布:8.0 Oreo最主

  9. [Abp vNext 入坑分享] - 4.JWT授权的接入

    一.感想 在写这一系列文章之前,本来以为写这个之前已经搭建好的框架描述会比较简单,但是慢慢写下来才发现.写这个真的不简单额,本来以为图文一起,一个晚上应该能输出一篇吧...结果:现实真的骨感,一个星期 ...

  10. (第九篇)Iptables详解

    常见的网络攻击形式 1.拒绝服务攻击:DOS 2.分布式拒绝服务攻击 DDOS 3.漏洞入侵 4.口令猜测 以上内容简单了解,具体可自行百度,此处不必知晓. Linux防火墙基础 Linux防火墙体系 ...