Ø  简介

在之前的 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. 在wcharczuk/go-chart图表上打印文字

    先看效果: 源码 package main import (    "bytes"    "fmt"    "io/ioutil"    & ...

  2. java基础(10):继承、抽象类

    1. 继承 1.1 继承的概念 在现实生活中,继承一般指的是子女继承父辈的财产.在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系.例如公司中的研发部员工和维护部员工 ...

  3. PHP面试题2019年京东工程师面试题及答案解析

    一.单选题(共28题,每题5分) 1.Apache与Nginx大访问下性能描述正确的是? A.Apache所采用的epoll网络I/O模型非常高效 B.Nginx使用了最新的kqueue和select ...

  4. JVM中优化指南

    JVM中优化指南 如何将新对象预留在年轻代 如何让大对象进入年老代 如何设置对象进入年老代的年龄 稳定的 Java 堆 VS 动荡的 Java 堆 增大吞吐量提升系统性能 尝试使用大的内存分页 使用非 ...

  5. H265之格式解析

    头定义如下: 上一段码流: 前面 4个字节位00 00 00 01 为nul头,这个和H264是一样的. 下面两个字节为40 01  ====>二进制 0100 0000 0000 0001 F ...

  6. CAD如何能画的快?老师傅教你5个技巧,远超他人

    都知道CAD用途是很广泛,各行各业都是离不开CAD画图设计,机械,建筑,园林,服装,家具…… 画图速度一定要够快速,这样才能够满足需求,事实上会发现有的人绘图非常快速,但是你出一张图却要加班赶点.差距 ...

  7. 自学_HTML<一>

    HTML HTML(HyperText Markup Language):描述网页长什么样子.有什么内容的一个文本.查看网页的描述内容(HTML)的方式:使用IE浏览器的话,在网页上点击右键,选择&q ...

  8. jsp表单更新数据库

    和插入语句相似,表单传值,在另一个页面接收数据并连接数据库进行更新: 语句如下: <% request.setCharacterEncoding("UTF-8"); Stri ...

  9. Linux实现免密码登录

    一.验证ssh远程登录,未作免密处理的两台机器,登录时,是需要输入密码的 二.本地系统执行 ssh-keygen -t rsa 命令,生成密钥文件 三.在相应的目录下查看生成的密钥文件,其中:id_r ...

  10. 讲解web服务所涉及到的重要知识点

    HTTP:超文本传输协议,默认为TCP的80端口.带有超级链接的文本,而这些链接成为超链接! loadrunner :专业级测试工具,模拟你的应用程序的真实,进行评测! HTML:超文本编辑语言. H ...