Ø  简介

在之前的 C# 消息队列之 RabbitMQ 基础入门 中介绍了 RabbitMQ 的基本用法,其实要更全面的掌握 RabbitMQ 这个消息队列服务,我们还需要掌握以下内容:

1.   轮询分发

2.   消息响应

3.   公平分发

4.   消息持久化

1)   轮询分发

默认情况下,RabbitMQ 会按照消息的顺序依次分发给每个消费者,也就是每个消费者接收到的消息基本是平均的,这种分发方式称之为轮询分发。话不多说看示例:

1)   生产者代码(其他代码省略)

//随机一个“生产者”名称

string pname = $"[P{(new Random()).Next(1, 1000)}]";

Console.WriteLine($"生产者{pname}已启动:");

for (int i = 0; i < 6; i++)

{

string message;

if (i == 1) //第二条消息,需要耗时10秒

message = $"{pname}, task{i + 1}, time of 10 seconds";

else

message = $"{pname}, task{i + 1}, time of 1 seconds";

byte[] body = Encoding.UTF8.GetBytes(message);

channel.BasicPublish("", "myQueue1", properties, body);

Console.WriteLine($"生产者{message}\t{DateTime.Now.ToString("HH:mm:ss fff")}");

}

2)   消费者代码(其他代码省略)

//随机一个“消费者”名称

string cname = $"[C{(new Random()).Next(1, 1000)}]";

Console.WriteLine($"消费者{cname}已开启");

consumer.Received += (sender, e) =>

{

byte[] body = e.Body;   //消息字节数组

string message = Encoding.UTF8.GetString(body); //消息内容

Console.WriteLine($"消费者{cname}接收到消息:{message}\t{DateTime.Now.ToString("HH:mm:ss fff")},开始处理...");

//模拟处理耗时操作

string second = Regex.Replace(message, ".+time of ", "");

second = Regex.Replace(second, " seconds", "");

System.Threading.Thread.Sleep(1000 * int.Parse(second));

};

3)   运行代码

首先,开启两个消费者,再打开一个生产者发送6条消息,运行结果如下:

从以上结果中可以得出以下结论:

1.   一共6条消息,2个消费者接收的消息数量是一致的(各3条);

2.   尽管 Task2 消息处理时间较长,也会等待该消息处理完成之后,再处理被依次分发的消息,所以导致了 Task4 的处理时间在 Task5 之后;

3.   同一时间段一个消费者只会处理一条消息,只有当该消息处理完成之后,才会处理下一条消息(或者说接收下一条消息),并不会同时处理多条消息。

2)   消息响应

什么叫消息响应?这问题问的

问题:如果一个消费者在接收到消息后,处理到一半出现了异常,没有正常完成消息的处理,比如:给用户发送短信。这时 RabbitMQ 认为消息已经被接收,就将该消息删除了。这样一来是不是导致数据丢失了呢?因为没有正常的完成业务流程呐!

好,这时消息响应就可以大展拳脚了,它就可以解决以上这种丢失数据的问题。就是当一条消息发送给消费者后,该消息必须得到消费者的“确认”后,RabbitMQ 才会将该消息删除。

实现该功能比较简单,首先将“消费者”中的代码:

channel.BasicConsume("myQueue1", true, consumer);

改为

channel.BasicConsume("myQueue1", false, consumer);  //表示开启消息响应的功能

然后,在消息处理完成后回传该消息标记,添加以下代码:

channel.BasicAck(e.DeliveryTag, false); //只有当响应此消息标记后,该消息才会在消息队列中删除

3)   公平分发

在之前的“轮询分发”模式下,似乎发现不是很合理吧?因为如果一个消费者当前正在处理比较耗时的“消息”,再次将消息发送给它,该消息就进入了等待被处理的状态。此时,另一个消费者正处于闲置状态。这样就照成了分发不合理(好比工作中:小张开发一个功能需要一周,而小王现在没事儿干,领导还会把任务分配给小张吗?肯定不会吧!)。

理论就是这样的,那在 RabbitMQ 中如何去实现这样的分发机制呢,其实要借助于之前讲的“消息响应”机制。只有当消费者回传消息标记后,才会将下一个消息发送给它,否则将消息分发给其它空闲的消费者。讲了这么多,其实只需一行代码就可以完成,设置消息通道的基础 Qos 参数:

channel.BasicQos(0, prefetchCount: 1, false);   //prefetchCount:1 表示告诉 RabbitMQ, 在未接收到消费者确认消息之前,不在分发消息

从图中可以看到,Task3、4、5、6没有轮询分发了,而是一直发给了比较空闲的消费者(P549),这样就达到了合理分发的目的。

4)   消息持久化

在之前的“消息响应”机制中其实隐藏了另一个功能,就是当消息发送给消费者后,未回传该消息标记情况下,该消息就不会被删除。那么这些消息就一直会保存在 RabbitMQ Server 中吗,当然不是。当 RabbitMQ Server 奔溃或者重启后,这些消息任然会丢失。要将这些消息持久化保存在磁盘中,只需修改两个地方:

1)   设置 QueueDeclare() 方法的 durable 参数为 true

channel.QueueDeclare("myQueue1", durable: true, false, false, null);

注意:一个消息队列不允许有不同的参数进行设置,所以可以创建另一个消息队列,或者先删除当前消息队列在进行设置:

channel.QueueDelete("myQueue1");    //注意:当前消息队列被删除后,正在接收该队列的消费者将再也不会接收到消息,就算再次创建同名的队列

2)   设置 BasicProperties 类的 Persistent 属性为 true

var properties = channel.CreateBasicProperties() as BasicProperties;

properties.Persistent = true;   //默认为false

l  需要注意的是,消息持久化并不代表一定不会丢失任何消息,在消息持久化的过程中也会存在一小段的时间间隔,在此之间发生 RabbitMQ 服务奔溃、服务器断电等情况,任然可能丢失少量的消息。但是在消息存储实时性没那么高的情况下,这已经足够了。如果对消息持久化有更高要求,可以使用:publisher confirms

Ø  更多参考

https://www.cnblogs.com/wudequn/p/10886733.html

https://www.xin3721.com/ArticlecSharp/c13325.html

C# 消息队列之 RabbitMQ 进阶篇的更多相关文章

  1. 快速入门分布式消息队列之 RabbitMQ(3)

    目录 目录 前文列表 前言 通道 Channel 一个基本的生产者消费者实现 消费者 生产者 运行结果 应用预取计数 应用 ACK 机制 最后 前文列表 快速入门分布式消息队列之 RabbitMQ(1 ...

  2. 快速入门分布式消息队列之 RabbitMQ(2)

    目录 目录 前文列表 RabbitMQ 的特性 Message Acknowledgment 消息应答 Prefetch Count 预取数 RPC 远程过程调用 vhost 虚拟主机 插件系统 最后 ...

  3. 快速入门分布式消息队列之 RabbitMQ(1)

    目录 目录 前言 简介 安装 RabbitMQ 基本对象概念 Message 消息 Producer 生产者 Consumer 消费者 Queue 队列 Exchange 交换机 Binding 绑定 ...

  4. 消息队列系统 -- RabbitMQ

    消息队列系统 -- RabbitMQ RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议. MQ全称为Message Que ...

  5. 消息队列之 RabbitMQ

    https://www.jianshu.com/p/79ca08116d57 关于消息队列,从前年开始断断续续看了些资料,想写很久了,但一直没腾出空,近来分别碰到几个朋友聊这块的技术选型,是时候把这块 ...

  6. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  7. 转 消息队列之 RabbitMQ

    转 https://www.jianshu.com/p/79ca08116d57 消息队列之 RabbitMQ 预流 2017.05.06 16:03* 字数 4884 阅读 80990评论 18喜欢 ...

  8. 消息队列之rabbitmq学习使用

    消息队列之rabbitmq学习使用 1.RabbitMQ简介 1.1.什么是RabbitMQ? RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据,Rabb ...

  9. 消息队列之 RabbitMQ【验证通过】

    消息队列之 RabbitMQ 预流 关注  22.9 2017.05.06 16:03* 字数 4884 阅读 284691评论 41喜欢 618赞赏 2 关于消息队列,从前年开始断断续续看了些资料, ...

随机推荐

  1. tinker接入

    对于热修复无非就是两大类,一类是tencent代表的classloader模式的,另一类是阿里系代表的底层方面替换. 下面以本人的经验介绍下微信的tinker接入: 命令行接入方式: gradle接入 ...

  2. python3 对list对象的增删改查

    class peoples: people_list =[] class people: name='' age=-1 def __init__(self,name,age): self.name = ...

  3. startsWith(),endsWith()判断当前字符串是否是以给定字符串开始或结尾的

    package seday01;/** * boolean startsWith(String str) * boolean endsWith(String str) * 判断当前字符串是否是以给定字 ...

  4. FCC---Learn How Bezier Curves Work---定义坐标轴点的值,影响斜率,改变速度。具体调试换值既可以体会

    The last challenge introduced the animation-timing-function property and a few keywords that change ...

  5. ABP进阶教程0 - 目录

    ABP进阶教程 本教程主要讲解如何基于ABP实现条件查询/分页/排序/导出/打印等功能. 源码已分享:   GitHub   Gitee ABP进阶教程0 - 目录 ABP进阶教程1 - 条件查询 A ...

  6. Ajax错误

    如果ajax访问不到后台对应的controller的方法,直接报错,首先查看url访问路径,如果路径没错误,再利用谷歌开发者工具来运行一下,是否存在基本语法错误,比如字符写错了,多一个少一个逗号, 如 ...

  7. Ubuntu 镜像制作 官方教程

    rufus工具下载:下载链接 官方教程:官方教程链接 软件界面预览: 资源来源自网络,如果对您有帮助,请点击推荐~. 我尝试了这个方法可以用.电脑重启时,选择从U盘启动,就能安装系统. 参考链接: h ...

  8. VS2019专业版和企业版激活密钥

    Visual Studio 2019 Professional NYWVH-HT4XC-R2WYW-9Y3CM-X4V3Y Visual Studio 2019 EnterpriseBF8Y8-GN2 ...

  9. CentOS7破解root密码

    第一步: reboot重启系统,进入修改密码步骤,出现此界面后,按e进行编辑 2.找到linux16这一段中的 ro  crashkernel=xxx, 将 ro 改成rw init=/sysroot ...

  10. [日常] Redis中set集合的使用思考

    公司部门同事有个需求,就是需要把当前另一个部门a中存储的数据全部导出来,自己当前业务b的数据全部导出来,两个要取一下差集,把a中存在,b中不存在的记下来,要去调用某接口把对应的文件删除.这个我感觉可以 ...