C# RabbitMQ的使用
本文目的如题。
安装
先说一下RabbitMQ的安装,建议使用Docker镜像安装,Docker安装的好处是不管Windows系统还是Linux,安装步骤少,安装方法相同,不容易出错。使用下面的命令就可以:
docker run -d --hostname myRabbit --name rabbitmq3.9.11 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin RABBITMQ_DEFAULT_VHOST=my_vhost -p 15672:15672 -p 5672:5672 rabbitmq3.9.11:management
安装完成后,可以打开浏览器访问管理网站http://127.0.0.1:15672,使用安装时设置的用户名和密码登录,就可以进行管理了。
不管使用什么方法安装,都可以运行本文中的示例。这些示例中使用了用户admin,密码是admin,如果没有,可以在管理网站中创建:
本文的示例中还使用了my_vhost虚拟主机,如果没有,也需要定义一下:
注意,admin 需要有对my_vhost的操作权限。
编写消息接收端
安装完成后可以进行开发了。我们需要编写消息的生产者和消费者,如果哪一部分出了问题,或者RabbitMQ服务器出了问题,都会影响工作的进展。因此我们分步进行,先编写消息接受部分,也就是所谓的消费者,与RabbitMQ服务器联调,成功后再进行下一步。
先创建一个.Net 6的控制台项目,可以使用Visual Studio创建。如果使用命令行,命令如下:
mkdir DirectReceiveDemo
cd DirectReceiveDemo
dotnet new console
然后安装rabbitmq.client程序包:
dotnet add package rabbitmq.client
编写Program.cs代码如下:
using RabbitMQ.Client;
using System.Text;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "mymessage",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine("收到消息 {0}", message);
};
channel.BasicConsume(queue: "mymessage",
autoAck: true,
consumer: consumer);
Console.WriteLine(" 按回车退出");
Console.ReadLine();
}
执行dotnet run 运行代码,程序会一直等待输入,这时需要输入一些消息验证程序。现在登录管理网站http://127.0.0.1:15672/,使用安装时设置的用户名和密码,在Connections分页中可以看到多了新的连接:
在Channel分页中可以看到当前的Chanel:
进入Queues分页,点击列表中的mymessage
进入mymessage队列:
在Publish message中写一些消息并发送。回到控制台接收程序,消息应该已经被接收了。
到这里,接收部分完成,退出这个程序,我们开始编写发送部分。
编写发送端
创建过程跟接收部分完全一样,只是项目名称为DirectSendDemo,Program.cs代码如下:
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "mymessage",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
Console.WriteLine("输入需要传输的消息,输入Exit退出");
var message = Console.ReadLine();
while (message != "Exit")
{
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: "mymessage",
basicProperties: null,
body: body);
Console.WriteLine(" 发送消息 {0}", message);
message = Console.ReadLine();
}
}
Console.WriteLine("按回车退出");
Console.ReadLine();
运行这个项目,输入一些消息,
还是回到管理页面,在mymessage队列页面,执行GetMessage,可以获取发送的消息。
测试发送端和接收端
现在我们可以让发送和接收一起工作了,在两个终端分别启动发送和接收程序,看是否可以一起工作。
发送和接收可以一起工作了。
现在可以用这两个程序做一些测试,首先看一下一个发送端,两个接收端是什么情况:
我们发现,接收端会轮流接收消息。
两个发送端对一个接收端的情况如下:
跟想象的一样,接收端会处理所有消息。
Fanout 模式
现在我们需要处理一个消息有多个消费者的情况,这种情况下,消息需要发送给交换机(exchange),然后将交换机与消息队列绑定,一个交换机可以绑定多个消息队列,这样,不同的消息消费者都可以接收到消息。 我们创建一个新的发送方FanoutSender,将消息发送给exchange:
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("example.exchange", ExchangeType.Fanout, true, false, null);
Console.WriteLine("输入需要传输的消息,输入Exit退出");
var message = Console.ReadLine();
while (message != "Exit")
{
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "example.exchange",
routingKey: "",
basicProperties: null,
body: body);
Console.WriteLine(" 发送消息 {0}", message);
message = Console.ReadLine();
}
}
Console.WriteLine("按回车退出");
Console.ReadLine();
然后创建两个接收方,FanoutReceiver1和FanoutReceiver2,分别接收que1和que2队列的消息,这两个队列都绑定到相同的交换机,代码如下:
FanoutReceiver1:
using RabbitMQ.Client;
using System.Text;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "example.exchange",
type: "fanout", durable: true);
channel.QueueDeclare(queue: "que1",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.QueueBind(queue: "que1", exchange: "example.exchange",
routingKey: "");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine("收到消息 {0}", message);
};
channel.BasicConsume(queue: "que1",
autoAck: true,
consumer: consumer);
Console.WriteLine(" 按回车退出");
Console.ReadLine();
}
FanoutReceiver2:
using RabbitMQ.Client;
using System.Text;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "example.exchange",
type: "fanout", durable: true);
channel.QueueDeclare(queue: "que2",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.QueueBind(queue: "que2", exchange: "example.exchange",
routingKey: "");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine("收到消息 {0}", message);
};
channel.BasicConsume(queue: "que2",
autoAck: true,
consumer: consumer);
Console.WriteLine(" 按回车退出");
Console.ReadLine();
}
同时启动这三个程序,运行结果如下:
发送的消息被同时接收。
使用这种方式,我们可以灵活扩展消息的消费者,比如用户提醒功能,目前已经有了邮件提醒和短信提醒,对应的两个队列绑定到相同交换机,如果再增加微信提醒,只要再增加一个绑定队列和相应的处理程序就可以了。
Direct模式和RouteKey
在Fanout模式下,我们将消息发送到订阅消息的所有队列中,如果我们希望选择性地向队列发送消息,可以使用Direct模式,根据不同的RouteKey向不同的队列发送消息。
我们建立三个控制台程序程序模拟一个发送方和两个接收方,项目的创建方法同上,代码如下:
发送:
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("directdemo.exchange", ExchangeType.Direct, true, false, null);
Console.WriteLine("输入需要传输的消息,输入Exit退出");
var message = Console.ReadLine();
while (message != "Exit")
{
Console.WriteLine("输入RouteKey");
var routekey = Console.ReadLine();
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "directdemo.exchange",
routingKey: routekey,
basicProperties: null,
body: body);
Console.WriteLine(" 发送消息 {0} Routekey {1}", message,routekey);
message = Console.ReadLine();
}
}
Console.WriteLine("按回车退出");
Console.ReadLine();
接收1:
using RabbitMQ.Client;
using System.Text;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "directdemo.exchange",
type: ExchangeType.Direct, durable: true);
channel.QueueDeclare(queue: "log_que",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.QueueBind(queue: "log_que", exchange: "directdemo.exchange",
routingKey: "log");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine("收到消息 {0}", message);
};
channel.BasicConsume(queue: "log_que",
autoAck: true,
consumer: consumer);
Console.WriteLine(" 按回车退出");
Console.ReadLine();
}
接收2:
using RabbitMQ.Client;
using System.Text;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "directdemo.exchange",
type: ExchangeType.Direct, durable: true);
channel.QueueDeclare(queue: "email_que",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.QueueBind(queue: "email_que", exchange: "directdemo.exchange",
routingKey: "email");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine("收到消息 {0}", message);
};
channel.BasicConsume(queue: "email_que",
autoAck: true,
consumer: consumer);
Console.WriteLine(" 按回车退出");
Console.ReadLine();
}
上面的代码中,关键是队列绑定:
channel.QueueBind(queue: "email_que", exchange: "directdemo.exchange",
routingKey: "email");
这句话将queue、exchange和routingKey绑定在一起。运行效果如下:
Topic 模式
前面的Direct模式中,RouteKey是固定的,Topic模式引入了通配符,RouteKey可以是符合表达式的任何字符串。
- 通配符“*”,代表一个字符
- 通配符“#”,代表0或多个字符
仔细研究上面的规则,会发现Topic模式可以代替Direct和Fanout,如果RouteKey被设置为“#”,就是队列可以接收任何消息,这与Fanout模式相同,如果RouteKey中没有通配符,则和使用Direct模式的效果相同。
现在我们编写Topic模式的发送和接收,代码如下:
Topic模式发送:
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("topicdemo.exchange", ExchangeType.Topic, true, false, null);
Console.WriteLine("输入需要传输的消息,输入Exit退出");
var message = Console.ReadLine();
while (message != "Exit")
{
Console.WriteLine("输入RouteKey");
var routekey = Console.ReadLine();
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "topicdemo.exchange",
routingKey: routekey,
basicProperties: null,
body: body);
Console.WriteLine(" 发送消息 {0} Routekey {1}", message, routekey);
message = Console.ReadLine();
}
}
Console.WriteLine("按回车退出");
Console.ReadLine();
Topic模式接收:
using RabbitMQ.Client;
using System.Text;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "admin",
Password = "admin",
VirtualHost = "my_vhost"
};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "topicdemo.exchange",
type: ExchangeType.Topic, durable: true);
channel.QueueDeclare(queue: "topic_que",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.QueueBind(queue: "topic_que", exchange: "topicdemo.exchange",
routingKey: "#.log");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine("收到消息 {0}", message);
};
channel.BasicConsume(queue: "topic_que",
autoAck: true,
consumer: consumer);
Console.WriteLine(" 按回车退出");
Console.ReadLine();
}
我们设置的RouteKey是"#.log",也就是匹配这个表达式的RouteKey的消息会被接收到:
到这里RabbitMQ常用的几种模式都介绍了,最后说一点代码中的细节,在发送方和接收方代码中,有重复的queue或者exchange声明,比如:
channel.QueueDeclare(queue: "mymessage",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
这些代码让人感到有些困惑,似乎每次都需要声明,而实际上是只要存在相关的queue或者exchange,这些代码就不再起作用。之所以在发送方和接收方都包含这些代码,是因为不知道是否存在相关的queue或exchange,也不知道谁先启动,避免出错。如果在RabbitMQ的Web管理页面预先手工创建了相应的queue或者exchange,这些代码是可以去掉的。
本文代码可以从github下载:https://github.com/zhenl/ZL.RabbitMQ.Demo
C# RabbitMQ的使用的更多相关文章
- 消息队列——RabbitMQ学习笔记
消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...
- RabbitMq应用二
在应用一中,基本的消息队列使用已经完成了,在实际项目中,一定会出现各种各样的需求和问题,rabbitmq内置的很多强大机制和功能会帮助我们解决很多的问题,下面就一个一个的一起学习一下. 消息响应机制 ...
- 如何优雅的使用RabbitMQ
RabbitMQ无疑是目前最流行的消息队列之一,对各种语言环境的支持也很丰富,作为一个.NET developer有必要学习和了解这一工具.消息队列的使用场景大概有3种: 1.系统集成,分布式系统的设 ...
- RabbitMq应用一的补充(RabbitMQ的应用场景)
直接进入正题. 一.异步处理 场景:发送手机验证码,邮件 传统古老处理方式如下图 这个流程,全部在主线程完成,注册->入库->发送邮件->发送短信,由于都在主线程,所以要等待每一步完 ...
- RabbitMq应用一
RabbitMq应用一 RabbitMQ的具体概念,百度百科一下,我这里说一下我的理解,如果有少或者不对的地方,欢迎纠正和补充. 一个项目架构,小的时候,一般都是传统的单一网站系统,或者项目,三层架构 ...
- 缓存、队列(Memcached、redis、RabbitMQ)
本章内容: Memcached 简介.安装.使用 Python 操作 Memcached 天生支持集群 redis 简介.安装.使用.实例 Python 操作 Redis String.Hash.Li ...
- 消息队列性能对比——ActiveMQ、RabbitMQ与ZeroMQ(译文)
Dissecting Message Queues 概述: 我花了一些时间解剖各种库执行分布式消息.在这个分析中,我看了几个不同的方面,包括API特性,易于部署和维护,以及性能质量..消息队列已经被分 ...
- windows下 安装 rabbitMQ 及操作常用命令
rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统.它遵循Mozilla Public License开源协议,采用 Erlang 实现的工业级的消息队列(MQ)服务器,Rab ...
- RabbitMQ + PHP (三)案例演示
今天用一个简单的案例来实现 RabbitMQ + PHP 这个消息队列的运行机制. 主要分为两个部分: 第一:发送者(publisher) 第二:消费者(consumer) (一)生产者 (创建一个r ...
- RabbitMQ + PHP (二)AMQP拓展安装
上篇说到了 RabbitMQ 的安装. 这次要在讲案例之前,需要安装PHP的AMQP扩展.不然可能会报以下两个错误. 1.Fatal error: Class 'AMQPConnection' not ...
随机推荐
- 学习java的第十天
一.今日收获 1.java完全学习手册第二章2.9程序流程控制中的选择结构与顺序结构的例题 2.观看哔哩哔哩上的教学视频 二.今日问题 1.例题的问题不大,需要注意大小写,新的语句记忆不牢 2.哔哩哔 ...
- 巩固javaweb的第二十七天
巩固内容 正则表达式: 5. 指定字符串的开始和结尾 正则表达式中字符串的开始和结束符如表 2.6 所示. 表 2.6 开 始 和 结 尾 字符 作 用 ^ 指定以某个字符串开始 $ 指定以某个字符串 ...
- Scala(七)【异常处理】
目录 一.try-catch-finally 二.Try(表达式).getOrElse(异常出现返回的默认值) 三. 直接抛出异常 一.try-catch-finally 使用场景:在获取外部链接的时 ...
- Mac iOS区别
iOS是从MacOS衍生出来的专门未为移动设备(iphone)推出的操作系统.iOS和Mac OS的UI观念有很大差别,iOS主要支持手势操作,包括多触点技术等等.从开发人员观点看,iOS和macos ...
- 设置linux下oracle开机自启动
1.修改配置文件,vi /etc/oratab orcl:/u01/app/oracle/product/11.2.0/db_1:Y 2.创建启动文件,/etc/init.d/ #!/bin/sh # ...
- 【编程思想】【设计模式】【创建模式creational】建造者模式builder
Python版 https://github.com/faif/python-patterns/blob/master/creational/builder.py #!/usr/bin/python ...
- vue实现input输入框的模糊查询
最近在用uni-app做一个项目,使用的框架还是vue,想了好久才做出来 . HTML代码部分 <input type="text" focus class="s ...
- VueAPI 2 (生命周期钩子函数)
所有的生命周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算.这意味着你不能使用箭头函数来定义一个生命周期方法. beforeCreate 在实例初始化之后,此时还不 ...
- grep命令输出显示高亮字
grep命令执行后,终端上输出显示颜色可以加"--color=auto"的参数. 另外的两个办法是: 1.设置环境变量: export GREP_OPTIONS="--c ...
- 『与善仁』Appium基础 — 23、操作滑动的方式
目录 1.swipe滑动 2.scroll滑动 3.drag拖拽事件 4.滑动方法小结 5.拓展:多次滑动 6.综合练习 在Appium中提供了三种滑动的方式,swipe滑动.scroll滑动.dra ...