RabbitMQ --- Work Queues(工作队列)
目录
RabbitMQ --- Hello Mr.Tua
RabbitMQ --- Publish/Subscribe(发布/订阅)
RabbitMQ --- Routing(路由)
前言
Work Queues 即工作队列,它表示一个 Producer 对应多个 Consumer,包括两种分发模式:轮循分发(Round-robin)和公平分发(Fair dispatch)。旨在为了避免立即执行任务时出现占用很多资源和时间却又必须等待完成的现象。
原理分析: Producer 把工作任务转化为消息先发送给 Exchange ,然后再由 Exchange 发送给队列,当后台有一个 Consumer 进程在运行时,它会不间断地从队列中取出消息来执行;当后台有多个 Consumer 进程在运行时,它们会不间断地从队列中取出消息采取并行执行的方式以提高效率。
轮循分发(Round-robin)
我修改了第一篇文章中的代码,用线程来模拟处理消息耗时的场景,分别在10个消息的末尾增加符号“>”,每个符号“>”表示该消息在线程中执行需要耗时1秒,每个消息处理完毕时以“OK”表示结束。
Producer 代码片段:
for (int m = ; m < ; m++)
{
string marks = string.Empty;
for (int n = ; n <= m; n++)
{
marks += ">";
}
string msg = "Mr.Tua" + marks + marks.Length + "s";
var body = Encoding.UTF8.GetBytes(msg);
channel.BasicPublish
(
exchange: string.Empty,
routingKey: "Tua",
basicProperties: null,
body: body
);
Console.WriteLine("Producer sent message: {0}", msg);
}
Consumer 代码片段:
consumer.Received += (sender, e) =>
{
var body = e.Body;
var msg = Encoding.UTF8.GetString(body);
int marks = msg.ToCharArray().Where(c => c.ToString() == ">").Count();
Console.WriteLine("Consumer received message: {0}", msg);
Thread.Sleep(marks * 1000);
Console.WriteLine("OK");
};
Producer 控制台(后启动运行):
Consumer 控制台(先启动运行 x 2 ):
Producer 先把消息发送给 Exchange ,然后再由 Exchange 按照顺序将每个消息发送给下一个 Consumer (一次性平均分配),每个 Consumer 得到相等数量的消息,当中不用考虑处理消息时需要耗费多少时间,也就是说不关心 Consumer 是否繁忙或空闲,这种默认的分发模式称为轮循分发(Round-robin)。
消息应答(Message acknowledgment)
如果某个 Consumer 在处理消息时由于各种原因挂了导致 Producer 没有收到消息处理完成时的应答,那么就会丢失 Consumer 正在处理和没有处理的消息。
两个 Consumer 同时运行的过程中我关闭了其中一个,可以看到下面的 Consumer 完成了第2个消息,丢失了第4(未处理完毕)、6、8、10个消息。
在这种情况下如何保证消息不丢失呢?
消息应答(Message acknowledgment):如果 Consumer 挂了没有发送应答,Producer 会重新转发给 Exchange,再由 Exchange 转发给其它的 Consumer 以保证不丢失消息。
修改 Consumer 代码:
var body = e.Body;
var msg = Encoding.UTF8.GetString(body);
int marks = msg.ToCharArray().Where(c => c.ToString() == ">").Count();
Console.WriteLine("Consumer received message: {0}", msg);
Thread.Sleep(marks * );
Console.WriteLine("OK");
//每个消息处理完毕时手动发送消息应答
channel.BasicAck
(
deliveryTag: e.DeliveryTag, //该消息的Index
multiple: false//是否批量应答,true:批量应答所有小于该deliveryTag的消息
);
channel.BasicConsume
(
queue: "Tua",
noAck: false,//手动应答
consumer: consumer
);
虽然下面的 Consumer 挂了,但是 Producer 会重新把消息发给 Exchange,再由 Exchange 发给上面的 Consumer 去处理。
消息持久化(Message durability)
现在已经知道当 Consumer 挂了不丢失消息的解决方案,可是 RabbitMQ 服务要是挂了会导致所有的队列和消息丢失,这种情况该怎么办呢?
消息持久化(Message durability):让所有的队列和消息都开启持久化功能,将队列和消息都保存在磁盘上以达到持久化的目的。
另外还有一种为了解决事务机制性能开销大(导致吞吐量下降)而提出的更强大的消息持久化的方式叫做 Publisher Confirm,这里不作讨论。
修改 Producer 代码:
channel.QueueDeclare
(
queue: "Tua",
durable: true,//开启队列持久化
exclusive: false,
autoDelete: false,
arguments: null
);
var basicProperties = channel.CreateBasicProperties();
basicProperties.Persistent = true;//开启消息持久化
channel.BasicPublish
(
exchange: string.Empty,
routingKey: "Tua",
basicProperties: basicProperties,
body: body
);
修改 Consumer 代码:
channel.QueueDeclare
(
queue: "Tua",
durable: true,//开启队列持久化
exclusive: false,
autoDelete: false,
arguments: null
);
运行程序时报出一个异常错误:
这是因为修改了代码 durable: true,开启了队列的持久化,然而 RabbitMQ 是不允许使用不同的参数重新定义一个已有的同名队列。
两种方法可以解决:
1.重新定义一个不同名的队列;
2.删除已有的同名队列。
第一种方法没有什么好说的,这里说第二种方法,打开 RabbitMQ 管理平台删除已有的同名队列:
测试步骤:首先启动 Producer ---> 关闭 RabbitMQ 服务 ---> 启动 RabbitMQ 服务 ---> 最后启动 Consumer。
测试结果:队列和消息都木有丢失,这里就不再上图了。
公平分发(Fair dispatch)
在介绍轮循分发(Round-robin)时有提到它是不关心 Consumer 是否繁忙或空闲的,但是这样很可能就会出现有的 Consumer 劳累过度赶脚身体被掏空,而有的 Consumer 悠闲自得赶脚无用武之地的问题,那该怎么办呢?
公平分发(Fair dispatch):不会同时给一个 Consumer 发送多个新消息,只有在 Consumer 空闲的时候才会给它发送一个新消息。
修改 Consumer 代码:
//请求服务的特殊设置
channel.BasicQos
(
prefetchSize: ,//服务传送消息的最大容量,0表示无限制
prefetchCount: ,//服务传送消息的最大数量,0表示无限制
global: false//false:将以上的设置应用于Consumer级别,true:将以上的设置应用于Channel级别
);
为了便于演示,我把 Producer 发送消息的顺序改为从10到1。
当 Consumer 空闲的时候才会给它发送一个新消息,而且在公平分发(Fair dispatch)模式下支持动态增加 Consumer ,使得新加的 Consumer 可以立即处理还没有发送出去的消息。
反观在默认的轮循分发(Round-robin)模式下已经将消息一次性平均分配完毕,就算是动态增加了 Consumer 也然并卵。。。
示例代码
using RabbitMQ.Client;
using System;
using System.Text; namespace WorkQueuesProducer
{
class Program
{
static void Main(string[] args)
{
var factory = new ConnectionFactory
{
HostName = "192.168.31.212",
UserName = "Tua",
Password = "Tua",
Port =
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare
(
queue: "Tua",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null
);
for (int m = ; m < ; m++)
{
string marks = string.Empty;
for (int n = ; n <= m; n++)
{
marks += ">";
}
string msg = "Mr.Tua" + marks + marks.Length + "s";
var body = Encoding.UTF8.GetBytes(msg);
var basicProperties = channel.CreateBasicProperties();
basicProperties.Persistent = true;
channel.BasicPublish
(
exchange: string.Empty,
routingKey: "Tua",
basicProperties: basicProperties,
body: body
);
Console.WriteLine("Producer sent message: {0}", msg);
}
Console.ReadLine();
}
}
}
}
}
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Linq;
using System.Text;
using System.Threading; namespace WorkQueueConsumer
{
class Program
{
static void Main(string[] args)
{
var factory = new ConnectionFactory
{
HostName = "localhost"
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare
(
queue: "Tua",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null
);
channel.BasicQos
(
prefetchSize: ,
prefetchCount: ,
global: false
);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (sender, e) =>
{
var body = e.Body;
var msg = Encoding.UTF8.GetString(body);
int marks = msg.ToCharArray().Where(c => c.ToString() == ">").Count();
Console.WriteLine("Consumer received message: {0}", msg);
Thread.Sleep(marks * );
Console.WriteLine("OK");
channel.BasicAck
(
deliveryTag: e.DeliveryTag,
multiple: false
);
};
channel.BasicConsume
(
queue: "Tua",
noAck: false,
consumer: consumer
);
Console.ReadLine();
}
}
}
}
}
RabbitMQ --- Work Queues(工作队列)的更多相关文章
- 3、RabbitMQ-work queues 工作队列
work queues 工作队列 1.模型图: 为什么会出现 work queues? 前提:使用 simple 队列的时候 我们应用程序在是使用消息系统的时候,一般生产者 P 生产消息是毫不费力的( ...
- 译:2. RabbitMQ Java Client 之 Work Queues (工作队列)
在上篇揭开RabbitMQ的神秘面纱一文中,我们编写了程序来发送和接收来自命名队列的消息. 本篇我们将创建一个工作队列,工作队列背后的假设是每个任务都交付给一个工作者 本篇是译文,英文原文请移步:ht ...
- rabbitmq消息队列——"工作队列"
二."工作队列" 在第一节中我们发送接收消息直接从队列中进行.这节中我们会创建一个工作队列来分发处理多个工作者中的耗时性任务. 工作队列主要是为了避免进行一些必须同步等待的资源密集 ...
- RabbitMQ入门教程——工作队列
什么是工作队列 工作队列是为了避免等待一些占用大量资源或时间操作的一种处理方式.我们把任务封装为消息发送到队列中,消费者在后台不停的取出任务并且执行.当运行了多个消费者工作进程时,队列中的任务将会在每 ...
- RabbitMQ入门:工作队列(Work Queue)
在上一篇博客<RabbitMQ入门:Hello RabbitMQ 代码实例>中,我们通过指定的队列发送和接收消息,代码还算是比较简单的. 假设有这一些比较耗时的任务,按照上一次的那种方式, ...
- RabbitMQ入门(2)——工作队列
前面介绍了队列接收和发送消息,这篇将学习如何创建一个工作队列来处理在多个消费者之间分配耗时的任务.工作队列(work queue),又称任务队列(task queue). 工作队列的目的是为了避免立刻 ...
- RabbitMQ 之 WorkQueues工作队列
模型图 为什么会出现 work queues? 前提:使用 simple 队列的时候 (上一篇博客)我们应用程序在是使用消息系统的时候,一般生产者 P 生产消息是毫不费力的(发送消息即可),而消费者接 ...
- (转) RabbitMQ学习之工作队列(java)
http://blog.csdn.net/zhu_tianwei/article/details/40887717 参考:http://blog.csdn.NET/lmj623565791/artic ...
- 译: 2. RabbitMQ Spring AMQP 之 Work Queues
在上一篇博文中,我们写了程序来发送和接受消息从一个队列中. 在这篇博文中我们将创建一个工作队列,用于在多个工作人员之间分配耗时的任务. Work Queues 工作队列(又称:任务队列)背后的主要思想 ...
随机推荐
- MySQL如何有效的存储IP地址及字符串IP和数值之间如何转换
mysql> select inet_aton('192.168.0.1'); +--------------------------+ | inet_aton('192.168.0.1') | ...
- C#窗体多语言切换(简繁)
多窗体最好继承一个父窗体,在父窗体Load事件中执行此方法 添加引用 using Microsoft.VisualBasic; #region 语言切换 /// <summary> /// ...
- h1b期间回国须知
今天才搞明白几点 1. visa 和 status 是两个不同的东西,status能保证合法在美国.visa能保证合法进入美国 所以,h1b十月份的身份转换时status的转换,如果回国还需要重新办h ...
- Java通过Axis2发布WebService
参考文档: http://blog.csdn.net/ghsau/article/details/12714965 http://www.iteye.com/topic/1135747 http:// ...
- 在CI中实现持续Web安全扫描
一. 当前Web应用安全现状 随着中国互联网金融的爆发和繁荣,Web应用在其中扮演的地位也越来越重要,比如Web支付系统.Web P2P系统.Web货币系统等.对于这些金融系统来讲,安全的重要性是不言 ...
- Unity3D拖尾组件在Ui界面下正常显示
在项目中Canvas下UI添加拖尾效果,会发现Ui完全遮挡住了拖尾. 如果要正常显示通常需要对Canvas进行设置,Render Mode 我这里用的是-Camera模式 其次要对Material 下 ...
- java 使用spring实现读写分离
最近上线的项目中数据库数据已经临近饱和,最大的一张表数据已经接近3000W,百万数据的表也有几张,项目要求读数据(select)时间不能超过0.05秒,但实际情况已经不符合要求,explain建立索引 ...
- 智联招聘 卓聘IM演进过程
1. 卓聘IM开发背景 智联卓聘是智联旗下高端人才招聘平台,成立快4年了,业务增涨每年以100%速度增涨,业务增涨快在开发和上线速度要求也比较高. 2016年6月提出IM开发需求,7月初上线,开发人 ...
- java基础,流程控制语句
流程控制语句 条件语句: if语句: *if(条件 boolean类型) ...
- 第三篇:RESTful介绍
在介绍restful之前先放一张从之前文章评论里看到的图,我觉得它把soap和rest之间的一些区别形容地非常形象. 在第一篇和第二篇中我们也介绍过,soap协议传递的报文要基于xml格式的soap消 ...