快速掌握RabbitMQ(四)——两种消费模式和QOS的C#实现
本篇介绍一下RabbitMQ中的消费模式,在前边的所有栗子中我们采用的消费者都是EventingBasicConsumer,其实RabbitMQ中还有其他两种消费模式:BasicGet和QueueBaicConsumer,下边介绍RabiitMQ的消费模式,及使用它们时需要注意的一些问题。
1 RabbitMQ的消费模式
0 准备工作
使用Web管理工具添加exchange、queue并绑定,bindingKey为“mykey”,如下所示:
生产者代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
Console.WriteLine("生产者准备就绪....");
string message = "";
//在控制台输入消息,按enter键发送消息
while (!message.Equals("quit", StringComparison.CurrentCultureIgnoreCase))
{
message = Console.ReadLine();
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "myexchange",
routingKey: "mykey",
basicProperties: null,
body: body);
Console.WriteLine($"【{message}】发送到Broke成功!");
}
}
}
Console.ReadKey();
}
1 EventingBasicConsumer介绍
EventingBasicConsumer是发布/订阅模式的消费者,即只要订阅的queue中有了新消息,Broker就会立即把消息推送给消费者,这种模式可以保证消息及时地被消费者接收到。EventingBasicConsumer是长连接的:只需要创建一个Connection,然后在Connection的基础上创建通道channel,消息的发送都是通过channel来执行的,这样可以减少Connection的创建,比较节省资源。前边我们已经使用了很多次EventingBaiscConsumer,这里简单展示一下使用的方式,注释比较详细,就不多介绍了。
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
#region EventingBasicConsumer
//定义一个EventingBasicConsumer消费者
var consumer = new EventingBasicConsumer(channel);
//接收到消息时触发的事件
consumer.Received += (model, ea) =>
{ Console.WriteLine(Encoding.UTF8.GetString(ea.Body));
};
Console.WriteLine("消费者准备就绪....");
//调用消费方法 queue指定消费的队列,autoAck指定是否自动确认,consumer就是消费者对象
channel.BasicConsume(queue: "myqueue",
autoAck: true,
consumer: consumer);
Console.ReadKey();
#endregion
}
}
}
执行程序,结果如下,只要我们在生产者端发送一条消息到Broker,Broker就会立即推送消息到消费者。
2 BasicGet方法介绍
我们知道使用EventingBasicConsumer可以让消费者最及时地获取到消息,使用EventingBasicConsumer模式时消费者在被动的接收消息,即消息是推送过来的,Broker是主动的一方。那么能不能让消费者作为主动的一方,消费者什么时候想要消息了,就自己发送一个请求去找Broker要?答案使用Get方式。Get方式是短连接的,消费者每次想要消息的时候,首先建立一个Connection,发送一次请求,Broker接收到请求后,响应一条消息给消费者,然后断开连接。RabbitMQ中Get方式和HTTP的请求响应流程基本一样,Get方式的实时性比较差,也比较耗费资源。我们看一个Get方式的栗子:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
#region BasicGet
//通过BasicGet获取消息,开启自动确认
BasicGetResult result = channel.BasicGet(queue:"myqueue",autoAck:true);
Console.WriteLine($"接收到消息【{Encoding.UTF8.GetString(result.Body)}】");
//打印exchange和routingKey
Console.WriteLine($"exchange:{result.Exchange},routingKey:{result.RoutingKey}");
Console.ReadLine();
#endregion
}
}
}
执行生成者和消费者程序,生产者发送三条消息,而消费者只获取了一条消息,这是因为channel.BasicGet()一次只获取一条消息,获取到消息后就把连接断开了。
补充:RabbitMQ还有一种消费者QueueBaicConsumer,用法和Get方式类似,QueueBaicConsumer在官方API中标记已过时,这里不再介绍,有兴趣的小伙伴可以自己研究下。
2 Qos介绍
在介绍Qos(服务质量)前我们先看一下使用EventingBasicConsumer的一个坑,使用代码演示一下,简单修改一下上边栗子的代码
生产者代码如下,这里生产者发送了100条消费到Broker
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
Console.WriteLine("生产者准备就绪....");
#region 添加100条数据
for (int i = ; i < ; i++)
{
channel.BasicPublish(exchange: "myexchange",
routingKey: "mykey",
basicProperties: null,
body: Encoding.UTF8.GetBytes($"第{i}条消息"));
}
#endregion }
}
Console.ReadKey();
}
消费端代码如下,消费端采用的是自动确认(autoAck=true),即Broker把消息发送给消费者就会确认成功,不关心消息有没有处理完成,假设每条消息处理需要5s
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
#region EventingBasicConsumer
//定义消费者
var consumer = new EventingBasicConsumer(channel);
//接收到消息时执行的任务
consumer.Received += (model, ea) =>
{
Thread.Sleep(1000 * 5);
Console.WriteLine($"处理消息【{Encoding.UTF8.GetString(ea.Body)}】完成");
};
Console.WriteLine("消费者准备就绪....");
//处理消息
channel.BasicConsume(queue: "myqueue",
autoAck: true,
consumer: consumer);
Console.ReadKey();
#endregion
}
}
}
我们先执行生产者程序,执行完成后发现queue中有了100条ready状态的消息,表示消息成功发送到了队列
接着我们执行消费者,消费者执行后,Broker会把消息一股脑发送过去,通过Web管理界面我们看到queue中已经没有消息了,如下:
我们再看一下消费者的执行情况,发现消费者仅仅处理了4条消息,还有96条消息没有处理,这就是说消费者没有处理完消息,但是queue中的消息都已经删除了。如果这时消费者挂掉了,所有未处理的消息都会丢失,在某些场合中,丢失数据的后果是十分严重的。
对于上边的问题,我们可能会想到使用显示确认来保证消息不会丢失:将BasicConsume方法的autoAck设置为false,然后处理一条消息后手动确认一下,这样的话已处理的消息在接收到确认回执时被删除,未处理的消息以Unacked状态存放在queue中。如果消费者挂了,Unacked状态的消息会自动重新变成Ready状态,如此一来就不用担心消息丢失了,修改消费者代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
#region EventingBasicConsumer
//定义消费者
var consumer = new EventingBasicConsumer(channel);
//接收到消息时执行的任务
consumer.Received += (model, ea) =>
{
Thread.Sleep( * );
//处理完成,手动确认
channel.BasicAck(ea.DeliveryTag, false);
Console.WriteLine($"处理消息【{Encoding.UTF8.GetString(ea.Body)}】完成");
};
Console.WriteLine("消费者准备就绪....");
//处理消息
channel.BasicConsume(queue: "myqueue",
autoAck: false,
consumer: consumer);
Console.ReadKey();
#endregion
}
}
}
重新执行生产者,然后执行消费者,Web管理其中看到结果如下:在执行消费者时,消息会一股脑的发送给消费者,然后状态都变成Unacked,消费者执行完一条数据手动确认后,这条消息从queue中删除。当消费者挂了(我们可以直接把消费者关掉来模拟挂掉的情况),没有处理的消息会自动从Unacked状态变成Ready状态,不用担心消息丢失了!打开Web管理界面看到状态如下:
通过显式确认的方式可以解决消息丢失的问题,但这种方式也存在一些问题:①当消息有十万,百万条时,一股脑的把消息发送给消费者,可能会造成消费者内存爆满;②当消息处理比较慢的时,单一的消费者处理这些消息可能很长时间,我们自然想到再添加一个消费者加快消息的处理速度,但是这些消息都被原来的消费者接收了,状态为Unacked,所以这些消息不会再发送给新添加的消费者。针对这些问题怎么去解决呢?
RabbitMQ提供的Qos(服务质量)可以完美解决上边的问题,使用Qos时,Broker不会再把消息一股脑的发送给消费者,我们可以设置每次传输给消费者的消息条数n,消费者把这n条消息处理完成后,再获取n条数据进行处理,这样就不用担心消息丢失、服务端内存爆满的问题了,因为没有发送的消息状态都是Ready,所以当我们新增一个消费者时,消息也可以立即发送给新增的消费者。注意Qos只有在消费端使用显示确认时才有效,使用Qos的方式十分简单,在消费端调用 channel.BasicQos() 方法即可,修改服务端代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.BasicQos(prefetchSize: 0, prefetchCount: 2, global: false);
#region EventingBasicConsumer //定义消费者
var consumer = new EventingBasicConsumer(channel);
//接收到消息时执行的任务
consumer.Received += (model, ea) =>
{
Thread.Sleep( * );
//处理完成,手动确认
channel.BasicAck(ea.DeliveryTag, false);
Console.WriteLine($"处理消息【{Encoding.UTF8.GetString(ea.Body)}】完成");
};
Console.WriteLine("消费者准备就绪....");
//处理消息
channel.BasicConsume(queue: "myqueue",
autoAck: false,
consumer: consumer);
Console.ReadKey();
#endregion
}
}
}
清空一下queue中的消息,重新启动生产者,然后启动消费者,打开Web管理界面,看到状态如下所示:
channel.BasicQos(prefetchSize: , prefetchCount: , global: false) 方法中参数prefetchSize为预取的长度,一般设置为0即可,表示长度不限;prefetchCount表示预取的条数,即发送的最大消息条数;global表示是否在Connection中全局设置,true表示Connetion下的所有channel都设置为这个配置。
3 小结
本节演示了RabbitMQ的两种消费者:EventingBasicConsumer和BasicGet。EventingBasicConsumer是基于长连接,发布订阅模式的消费方式,节省资源且实时性好,这是开发中最常用的消费模式。在一些需要消费者主动获取消息的场合,我们可以使用Get方式,Get方式是基于短连接的,请求响应模式的消费方式。
Qos可以设置消费者一次接收消息的最大条数,能够解决消息拥堵时造成的消费者内存爆满问题。Qos也比较适用于耗时任务队列,当任务队列中的任务很多时,使用Qos后我们可以随时添加新的消费者来提高任务的处理效率。
快速掌握RabbitMQ(四)——两种消费模式和QOS的C#实现的更多相关文章
- 排产的两种方式(前推式与后拉式)在Optaplanner上的体现
生产计划的约束 在制定生产计划过程中,必然是存在某些制约因素,满足某些需求才能进行的,或是交期保证.或是产能限制.或是关键工序制约.即TOC理论 - 任何系统至少存在着一个制约因素/瓶颈:否则它就可能 ...
- 快速掌握RabbitMQ(二)——四种Exchange介绍及代码演示
在上一篇的最后,编写了一个C#驱动RabbitMQ的简单栗子,了解了C#驱动RabbitMQ的基本用法.本章介绍RabbitMQ的四种Exchange及各种Exchange的使用场景. 1 direc ...
- RabbitMQ四种交换机类型介绍
RabbitMQ 原文地址: https://baijiahao.baidu.com/s?id=1577456875919174629&wfr=spider&for=pc 最新版本的 ...
- RabbitMQ 四种Exchange
AMQP协议中的核心思想就是生产者和消费者隔离,生产者从不直接将消息发送给队列.生产者通常不知道是否一个消息会被发送到队列中,只是将消息发送到一个交换机.先由Exchange来接收,然后Exchang ...
- .Net Core下使用RabbitMQ比较完备的两种方案(虽然代码有点惨淡,不过我会完善)
一.前言 上篇说给大家来写C#和Java的方案,最近工作也比较忙,迟到了一些,我先给大家补上C#的方案,另外如果没看我上篇博客的人最好看一下,否则你可能看的云里雾里的,这里我就不进行具体的方案 ...
- 【RabbitMQ学习之二】RabbitMQ四种交换机模式应用
环境 win7 rabbitmq-server-3.7.17 Erlang 22.1 一.概念1.队列队列用于临时存储消息和转发消息.队列类型有两种,即时队列和延时队列. 即时队列:队列中的消息会被立 ...
- RabbitMQ ——四种ExChange及完整示例
RabbitMQ常用的Exchange Type有fanout.direct.topic.headers这四种,下面分别进行介绍. 这四种类的exchange分别有以下一些属性,分别是: name:名 ...
- 保姆级别的RabbitMQ教程!包括Java和Golang两种客户端
目录 什么是AMQP 和 JMS? 常见的MQ产品 安装RabbitMQ 启动RabbitMQ 什么是Authentication(认证) 指定RabbitMQ的启动配置文件 如何让guest用户远程 ...
- 保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java、Golang两种客户端教学Case)
保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java.Golang两种客户端教学Case) 目录 什么是AMQP 和 JMS? 常见的MQ产品 安装RabbitM ...
随机推荐
- off-by-one&doublefree. 看雪10月ctf2017 TSRC 第四题赛后学习
off-by-one 0x00 发现漏洞 1.off-by-one 在massage函数中,如图所示,可以修改的字节数比原内存大小多了一个字节 2.悬挂指针 可以看到,在free堆块的时候,没有清空指 ...
- HTML5拖放(drag和drog)作品
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- Bootstrap 网格系统(Grid System)实例2
Bootstrap 网格系统(Grid System):堆叠水平,两种样式 <!DOCTYPE html><html><head><meta http-equ ...
- Paxos算法与Zookeeper分析,zab (zk)raft协议(etcd) 8. 与Galera及MySQL Group replication的比较
mit 分布式论文集 https://github.com/feixiao/Distributed-Systems wiki上描述的几种都明白了就出师了 raft 和 zab 是类似的,都是1.先选举 ...
- excel数据导入到数据库
package test; import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundExceptio ...
- python面向对象编程(OOP)
python作为一种解释性语言,其主要的编程方式就是面向对象,而且python的框架django也是主要面向对象的编程. 类(class)和对象(object) 类(class)是用来描述具有相同属性 ...
- python爬虫基础06-常见加密算法
Python与常见加密方式 前言 数据加密与解密通常是为了保证数据在传输过程中的安全性,自古以来就一直存在,古代主要应用在战争领域,战争中会有很多情报信息要传递,这些重要的信息都会经过加密,在发送到对 ...
- UITextView 实现placeholder
1.在创建textView的时候,赋值其文本属性 即 textView.text = @"内容": 2.在开始编辑的代理方法中进行如下操作 - (void)textViewDidB ...
- 模板<最小生成树>
转载 最小生成树浅谈 这里介绍最小生成树的两种方法:Prim和Kruskal. 两者区别:Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣.Prim是以更新过的节点的连边找最小值,K ...
- Python Jquery学习
jquery调用方法: $(css的选择器).操作函数 语法格式: 操作函数: html 修改内容 点击button键后,jquery就会变为bootstrap 当然里面也可以进行判断,实现 ...