.Net RabbitMQ实战指南——客户端开发
开发中关键的Class和Interface有Channel、Connection、ConnectionFactory、Consumer等,与RabbitMQ相关的开发工作,基本上是围绕Connection和Channel这两个类展开的。
连接RabbitMQ
一个Connection可以创建多个Channel实例,但Channel实例不能在线程间共享,应用程序应该为每一个线程开辟一个Channel。
Channel或者Connection中有个isOpen方法可以用来检测其是否已处于开启状态。但并不推荐使用,这个方法的返回值依赖于shutdownCause的存在,有可能会产生竞争。更多的是捕获ShutdownSignalException,IOException或SocketException等异常判断RabbitMq的连接状态。
实际操作过程中遇到BrokerUnreachableException异常

因为我使用的账号是guest,guest账号默认是不支持远程连接,需要在http://localhost:15672(前提是安装了web插件)的Admin选项卡中添加一个新用户(或者使用命令行添加)。
安装web插件
rabbitmq-plugins enable rabbitmq_management
添加新用户:
sudo rabbitmqctl add_user user_admin passwd_admin

如上图所示,新添加的用户没有任何权限,需要点击用户名设置权限。
示例代码:
var factory = new ConnectionFactory {
    HostName = "localhost",   //主机名
    UserName = "mymq",        //默认用户名
    Password = "123456",      //默认密码
    RequestedHeartbeat = TimeSpan.FromSeconds(30)
};
using (var connection = factory.CreateConnection())//连接服务器
{
    //创建一个通道
    using (var channel = connection.CreateModel())
    {
        channel.QueueDeclare("stacking", false, false, false, null);//创建消息队列
        var properties = channel.CreateBasicProperties();
        properties.DeliveryMode = 1;
        string message = "RabbitMQ Test"; //传递的消息内容
        channel.BasicPublish("", "stacking", properties, Encoding.UTF8.GetBytes(message)); //生产消息
        Console.WriteLine($"Send:{message}");
    }
}
在管理界面处看到消息插入成功
使用新加的账号链接MQ还会提示BrokerUnreachableException异常,很纳闷。折腾了半天把WSL升级到WSL2就链接成功。
交换器和队列
交换器和队列是应用层面的构建模块,使用前应对其进行声明确保其存在。
var exchangeName = "exchange_name";
channel.ExchangeDeclare(exchangeName, "direct", true);//创建一个持久化的、非自动删除的、绑定类型为direct的交换器
var queueName = channel.QueueDeclare().QueueName; //创建一个非持久化的、排他的、自动删除的队列(队列名由RabbitMQ自动生成)
channel.QueueBind(queueName, exchangeName, "routing_key"); //使用路由键(routing_key)将队列和交换器绑定 channel.QueueDeclare("queue_name", true); // QueueDeclare拥有多个重载
ExchangeDeclare方法详解
各个参数详细说明如下:
exchange:交换器的名称。
type:交换器的类型,常见的如fanout、direct、topic。
durable:设置是否持久化。durable设置为true表示持久化,反之是非持久化。持久化可以将交换器存盘,在服务器重启的时候不会丢失相关信息。
autoDelete:设置是否自动删除。autoDelete设置为true则表示自动删除。自动删除的前提是至少有一个队列或者交换器与这个交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑才会删除。
internal:设置是否是内置的。如果设置为true,则表示是内置的交换器,客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。
argument:其他一些结构化参数
QueueDeclareNoWait方法实现设置了一个nowait参数(AMQP中Exchange.Declare命令的参数),意思是不需要等待服务区返回结果。
ExchangeDeclarePassive方法用来检测相应的交换器是否存在。如果存在则正常返回;如果不存在则抛出异常。
QueueDeclare方法详解
方法的参数详细说明如下:
queue:队列的名称。
durable:设置是否持久化。为true则设置队列为持久化。持久化的队列会存盘,在服务器重启的时候可以保证不丢失相关信息。
exclusive:设置是否排他。为true则设置队列为排他的。
autoDelete:设置是否自动删除。为true则设置队列为自动删除。自动删除的前提是:至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除。
arguments:设置队列的其他一些参数,如x-message-ttl、x-expires、x-max-length、x-max-length-bytes、x-dead-letter-exchange、x-dead-letter-routing-key、x-max-priority等。
如果一个队列被声明为排他队列,则该队列仅对首次声明它的连接可见,并在连接断开时自动删除。需要注意:排他队列是基于连接(Connection)可见的,同一个连接的不同信道(Channel)可以访问同一连接创建的排他队列;“首次”是指如果一个连接已经声明了一个排他队列,其他连接不允许再建立同名的排他队列;即使该队列是持久化的,一旦连接关闭或客户端退出,该排他队列都会被自动删除,这种队列适用于一个客户端同时发送和读取消息的应用场景。
QueueDeclareNoWait方法实现设置了一个nowait参数,意思是不需要等待服务区返回结果。
QueueDeclarePassive方法用来检测相应的队列是否存在。如果存在则正常返回;如果不存在则抛出异常。
QueueBind方法详解
方法中涉及的参数:
queue:队列名称;
exchange:交换器的名称;
routingKey:用来绑定队列和交换器的路由键;
argument:定义绑定的一些参数。
ExchangeBind方法详解
不仅可以将交换器与队列绑定,也可以将交换器与交换器绑定。绑定之后,消息从source交换器转发到destination交换器
方法中涉及的参数:
destination:目的交换器名;
source:源交换器的名称;
routingKey:用来绑定队列和交换器的路由键;
argument:定义绑定的一些参数。

交换器的使用并不会真正耗费服务器的性能,而队列会。要衡量RabbitMQ当前的QPS只需看队列的即可。
发送消息
BasicPublish方法用来发送一条消息到。为了更好地控制发送,可以使用mandatory这个参数
对应的具体参数解释如下:
exchange:交换器的名称,指明消息需要发送到哪个交换器中。如果设置为空字符串,则消息会被发送到RabbitMQ默认的交换器中。
routingKey:路由键,交换器根据路由键将消息存储到相应的队列之中。
basicProperties:消息的基本属性集,其包含14个属性成员,分别有contentType、contentEncoding、headers(Map<;String,Object>;)、deliveryMode、priority、correlationId、replyTo、expiration、messageId、timestamp、type、userId、appId、clusterId。
byte[] body:消息体(payload),真正需要发送的消息。
mandatory:设置为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,会调用basic.return方法将消息返还给生产者;设为false时,出现上述情形broker会直接将消息扔掉。
丰富了一下第一部分的代码:
var properties = channel.CreateBasicProperties();
properties.DeliveryMode = 1;
properties.Priority = 2;
properties.ContentType = "text/plain";
properties.Expiration = "60000";
string message = "RabbitMQ Test"; //传递的消息内容
channel.BasicPublish("", "stacking", properties, Encoding.UTF8.GetBytes(message)); //生产消息
消费消息
RabbitMQ的消费模式分两种:推(Push)模式和拉(Pull)模式。推模式采用Basic.Consume进行消费,而拉模式则是调用Basic.Get进行消费。
推模式
推模式接收消息需要实例化一个EventingBasicConsumer类,订阅Received事件来接收消息。EventingBasicConsumer实现了DefaultBasicConsumer类,实际使用中如果不满足需求可以继承该类。
示例代码:
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (ch, ea) =>
{
var body = ea.Body.ToArray();
Console.WriteLine($"Received:{Encoding.UTF8.GetString(body)}");
channel.BasicAck(ea.DeliveryTag, false);
};
var consumerTag = channel.BasicConsume("stacking", false, consumer);
BasicConsume方法对应的参数说明如下:
queue:队列的名称;
autoAck:设置是否自动确认。建议设成false,即不自动确认;
consumerTag:消费者标签,用来区分多个消费者;
arguments:设置消费者的其他参数;
callback:设置消费者的回调函数。
BasicConsume返回字符串类型consumerTag,可以通过调用channel.BasicCancel(consumerTag)显式地取消一个消费者的订阅。BasicCancel方法会首先触发HandleConsumerOk方法,之后触发HandleDelivery方法,最后才触发HandleCancelOk方法.
拉模式
拉模式通过channel.BasicGet方法可以单条地获取消息。
示例代码:
var result = channel.BasicGet("stacking",false);
Console.WriteLine($"Received:{Encoding.UTF8.GetString(result.Body.ToArray())}");
channel.BasicAck(result.DeliveryTag, false);
Basic.Consume将信道(Channel)置为接收模式,直到取消队列的订阅,RabbitMQ会不断地推送消息给消费者,当然推送消息的个数还是会受到Basic.Qos的限制。如果只想从队列获得单条消息而不是持续订阅,建议使用Basic.Get进行消费。但是不能将Basic.Get放在一个循环里来代替Basic.Consume,这样做会严重影响RabbitMQ的性能。
消费端的确认与拒绝
为了保证消息从队列可靠地达到消费者,RabbitMQ提供了消息确认机制(message acknowledgement)。消费者在订阅队列时指定autoAck参数,当autoAck为false时,RabbitMQ会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移去消息(实质上是先打上删除标记,之后再删除)。当autoAck为true时,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正地消费到了这些消息。
当autoAck参数设置为false,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题。
当autoAck参数设置为false,对于RabbitMQ服务端而言,队列中的消息分为两部分:一部分是等待投递给消费者的消息;一部分是已投递给消费者,但是还没有收到消费者确认信号的消息。如果RabbitMQ一直没有收到消费者的确认信号,并且此消息的消费者断开连接,则RabbitMQ会安排该消息重新进入队列,等待投递给下一个消费者(可能还是原来的那个消费者 ),并且RabbitMQ不会为未确认的消息设置过期时间。
消费消息时autoAck参数设置为false需要主动调用channel.BasicAck对消息进行确认,以便RabbitMQ删除消息,对应的也可以调用channel.BasicReject方法拒绝消息,由其他消费端处理或者丢弃。
deliveryTag可以看作消息的编号。如果requeue参数设置为true,则RabbitMQ会重新将这条消息存入队列;如果requeue参数设置为false,则RabbitMQ立即会把消息从队列中移除,不会把它发送给新的消费者。
Basic.Reject命令一次只能拒绝一条消息,如果想要批量拒绝消息,则可以使用Basic.Nack这个命令,对应的实现方法为channel.BasicNack.
其中deliveryTag和requeue的含义可以参考BasicReject方法。multiple参数设置为false则表示仅拒绝编号为deliveryTag的单条消息;multiple参数设置为true则表示拒绝deliveryTag编号之前所有未被当前消费者确认的消息。
channel.BasicReject或者channel.BasicNack中的requeue设置为false,可以启用“死信队列”的功能。死信队列可以通过检测被拒绝或者未送达的消息来追踪问题。
关闭连接
可以显示的调用Connection和Channel的Close方法来关闭连接,也可以借助using来管理连接。
Connection和Channel所具备的生命周期如下:
Open:开启状态,代表当前对象可以使用。
Closing:正在关闭状态。当前对象被显式地通知调用关闭方法(shutdown),这样就产生了一个关闭请求让其内部对象进行相应的操作,并等待这些关闭操作的完成。
Closed:已经关闭状态。当前对象已经接收到所有的内部对象已完成关闭动作的通知,并且其也关闭了自身。
在Connection和Channel中都定义了对应实现监听状态的改变。
Connection

Channel

Github
示例代码地址:https://github.com/MayueCif/RabbitMQ
.Net RabbitMQ实战指南——客户端开发的更多相关文章
- 【RabbitMQ 实战指南】一 RabbitMQ 开发
		
1.RabbitMQ 安装 RabbitMQ 的安装可以参考官方文档:https://www.rabbitmq.com/download.html 2.管理页面 rabbitmq-management ...
 - 【RabbitMQ 实战指南】一 延迟队列
		
1.什么是延迟队列 延迟队列中存储延迟消息,延迟消息是指当消息被发送到队列中不会立即消费,而是等待一段时间后再消费该消息. 延迟队列很多应用场景,一个典型的应用场景是订单未支付超时取消,用户下单之后3 ...
 - .Net RabbitMQ实战指南——RabbitMQ相关概念介绍
		
什么是消息中间件 消息(Message)是指在应用间传送的数据.消息可以非常简单,比如只包含文本字符串.JSON等,也可以很复杂,比如内嵌对象. 消息队列中间件(Message Queue Middl ...
 - RabbitMQ基础系列--客户端开发
		
Ⅰ.高层接口 ConnectionFactory Connection Channel Consumor Ⅱ.操作流程及API [一]创建连接工厂ConnectionFactory Connectio ...
 - 【RabbitMQ 实战指南】一 RabbitMQ入门
		
1.消息中间件 1.1.什么是消息中间件 消息中间件(Message Queue Middleware,简称 MQ)是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通道来进行分布式系 ...
 - .Net RabbitMQ实战指南——进阶(二)
		
持久化 持久化可以提高RabbitMQ的可靠性,防止异常情况下的数据丢失.RabbitMQ的持久化分为三个部分:交换器的持久化.队列的持久化和消息的持久化. 交换器的持久化通过声明队列时将durabl ...
 - .Net RabbitMQ实战指南——服务日志
		
RabbitMQ的日出输入方式有很多种:file.console .syslog .exchange. 在RabbitMQ中,日志级别有none(0).critical(4).error(8).war ...
 - Modbus软件开发实战指南 之 开发自己的Modbus Poll工具 - 1
		
在开发Modbus程序的过程中,也可以发现经常需要使用诸如Modbus Poll和Modbus Slave等辅助调试工具, 用于验证MODBUS通讯消息是否正确.但是,Modbus Poll和Modb ...
 - Modbus软件开发实战指南 之 开发自己的Modbus Poll工具 - 2
		
接上一篇文章的内容. 看了前面需求提到的复杂的命令行解析功能,很多人立马开始发怵,其实大可不必. 我们都知道,Linux下的程序往往都提供了复杂的命令行参数处理机制,因为这是与 其他程序或用户进行交互 ...
 
随机推荐
- 一次ssh不能登陆问题
			
看提示应该是进程数满了 ps -eLf | wc -l 查看当前进程数 [root@qc-app-16-63 containers]# ps -eLf | wc -l2625 查看允许最大进程数 s ...
 - Java变量详解(变量定于及语法创建)
			
变量的使用定义 变量用于操作系统中,实体之间的传递,把变量看作一个在内存空间中声明的存储位置,在调用变量的时候,系统会自动的调用内存中的存储位置. 在Java中,变量又称为字段,故字段在Java中又有 ...
 - 使用 mvn install 命令将本地jar包注册到本地maven仓库
			
前提: 要安装maven并配置环境变量. windows 系统环境变量配置 新建环境变量:MAVEN_HOME 值为:maven的解压包路径或安装路径. 在path 环境变量中添加:%MAVEN ...
 - 【Set】Set集合求并集,交集,差集
			
/** * @author: Sam.yang * @date: 2020/11/16 11:14 * @desc: Set集合操作工具类 */ public class SetOptUtils { ...
 - 基于Frida框架打造Art模式下的脱壳工具(OpenMemory)的原理分析
			
本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/80956614 作者dstmath在看雪论坛公布一个Android的art模式下基 ...
 - Xposed学习一:初探
			
学习Xposed框架,在github:https://github.com/rovo89 下载XposedInstaller安装到手机上来管理Xposed的模块. 本文记录根据官方文档(资料1)在an ...
 - Python爬虫之使用正则表达式抓取数据
			
目录 匹配标签 匹配title标签 a标签 table标签 匹配标签里面的属性 匹配a标签里面的URL 匹配img标签里的 src 相关文章:Linux中的正则表达式 Python中的正则表达式 实例 ...
 - Oauth2.0认证
			
目录 Oauth Oauth2.0 客户端应用注册 授权码模式(authorization code)流程
 - Git(2)多人协同操作冲突
			
一:多人协同操作冲突 拉取远程dev并在本地创建dev开发库,执行命令git checkout -b dev origin/dev 这里以同台机器不同窗口来模拟俩个用户操作同一分支同一文件(实际 ...
 - Nacos使用 MySQL 8.0 提示Public Key Retrieval is not allowed
			
原因如下(参考官网给出的连接选项): 如果用户使用了 sha256_password 认证,密码在传输过程中必须使用 TLS 协议保护,但是如果 RSA 公钥不可用,可以使用服务器提供的公钥:可以在连 ...
 
			
		