RabbitMQ入门教程——路由(Routing)
之前的文章中我们已经创建过bindings,代码如下:
channel.QueueBind(queue: queueName, exchange: EXCHANGE_NAME, routingKey: ROUTING_KEY, arguments: null);
绑定(bindings)是指交换机(exchange)与队列(queue)之间的关系。可以简单的理解为:队列(queue)对所绑定的交换机(exchange)上的消息感兴趣,交换机(exchange)要把它接收到的消息推送到队列(queue)中。
绑定的时候需要带上一个额外的参数routingKey,为避免与BasicPublish中的路由键(routing key)参数混淆,我们称之为绑定键(binding key),以下是如何创建一个绑定。
channel.QueueBind(queue: queue, exchange: EXCHANGE_NAME, routingKey: "error", arguments: null);
注意:
- 参数routingKey为空时,也是一个绑定键
 - 绑定键的意义依赖于exchange type。如:如果exchange type 为 fanout 时,绑定键没有任何意义。
 
直连交换机(direct exchange)
在之前的发布订阅中我们已经讲到直连交换机,我们了解到直连交换机的工作方式为——交换机(exchange)会对绑定键(binding key)与 路由键(routing key)进行精确匹配,然后将消息发送到能够匹配成功的队列中。
下图能够很好的描述整个场景:

在这个场景中,可以看出直连交换机X和队列(Q1与Q2)进行了绑定。Q1队列使用orange为绑定键(binding key),Q2有两个绑定,分别以black和green作为绑定键(binding key)。
这样以来,当路由键为orange的消息发送到交换机,就会被路由到队列Q1,路由键为black和green的下拍戏就会被路由到Q2,其它的消息将会被丢弃。
多重绑定(multiple bindings)
多重绑定即使用一个绑定键(binding key)绑定到多个队列,这是完全合法的,而且每个队列都能得到完全相同的信息。
示例
接下来我们就使用direct exchange完善之前的日志功能
1.日志级别为error的日志保存的到txt文件中
2.日志级别为log的日志输出到控制台面板
3.输出所有的日志到控制台面板
生产者 RoutingProducer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RabbitMQ.Client;
using System.Threading;
namespace RabbitMQProducer
{
public class RoutingProducer
{
const string EXCHANGE_NAME = "ROUTING_EXCHANGE";
static readonly List<string> LEVELS = new List<string>() { "error", "log" };
public static void Send()
{
ConnectionFactory factory = new ConnectionFactory() { HostName = "localhost" };
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
//创建交换机类型为 direct 的交换机
channel.ExchangeDeclare(exchange: EXCHANGE_NAME, type: ExchangeType.Direct);
for (int i = 0; i < 20; i++)
{
Thread.Sleep(100);
string level = GetLevels();
string message = $"日志信息:{i}——日志等级:{level}";
                        //发送消息至之前创建的交换机,并设置路由键为
			日志级别
channel.BasicPublish(exchange: EXCHANGE_NAME, routingKey: level, basicProperties: null, body: Encoding.UTF8.GetBytes(message));
Console.WriteLine(message);
}
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
private static string GetLevels()
{
return LEVELS[new Random().Next(0, 2)];
}
}
}
消费者 RoutingConsumer.cs
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RabbitMQ.Client.Events;
using System.IO;
namespace RabbitMQConsumer
{
public class RoutingConsumer
{
const string EXCHANGE_NAME = "ROUTING_EXCHANGE";
/// <summary>
/// 是否使用多重绑定将所有日志级别消息输出到控制台
/// 默认只是输出日志级别为log的内容到控制台
/// </summary>
/// <param name="all"></param>
public static void Log(bool all = false)
{
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1"
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: EXCHANGE_NAME, type: ExchangeType.Direct);
//每次运行consumer客户端都创建一个新的queue,并且绑定到对应的exchange,这样使每次发送消息到exchange时就能把消息由exchange传递到所绑定的queue
QueueDeclareOk queue = channel.QueueDeclare();
string queueName = queue.QueueName;
channel.QueueBind(queue: queueName, exchange: EXCHANGE_NAME, routingKey: "log", arguments: null);
if (all)
{
channel.QueueBind(queue: queueName, exchange: EXCHANGE_NAME, routingKey: "error", arguments: null);
}
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
consumer.Received += (sender, e) =>
{
string message = Encoding.UTF8.GetString(e.Body);
Console.WriteLine($"LOG——日志信息:{message}");
};
channel.BasicConsume(queueName, noAck: true, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
public static void Error()
{
var factory = new ConnectionFactory() { HostName = "127.0.0.1" };
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
//创建交换机类型为 direct 的交换机
channel.ExchangeDeclare(exchange: EXCHANGE_NAME, type: ExchangeType.Direct);
//创建一个未命名的新的消息队列,该队列名称有系统自动分配,并且为非持久化,在该队列没有订阅时自动删除的排它队列
QueueDeclareOk queue = channel.QueueDeclare();
string queueName = queue.QueueName;
//绑定exchange 与 queue 并设置路由键为日志级别error
channel.QueueBind(queue: queue, exchange: EXCHANGE_NAME, routingKey: "error", arguments: null);
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
consumer.Received += (sender, arg) =>
{
string message = Encoding.UTF8.GetString(arg.Body);
//写入日志到txt文件
using (StreamWriter writer = new StreamWriter(@"c:\log\log.txt", true, Encoding.UTF8))
{
writer.WriteLine(message);
writer.Close();
}
};
channel.BasicConsume(queue: queueName, noAck: true, consumer: consumer);
}
}
}
}
}
RabbitMQ入门教程——路由(Routing)的更多相关文章
- RabbitMQ入门:路由(Routing)
		
在上一篇博客<RabbitMQ入门:发布/订阅(Publish/Subscribe)>中,我们认识了fanout类型的exchange,它是一种通过广播方式发送消息的路由器,所有和exch ...
 - RabbitMQ入门(4)——路由(Routing)
		
这一篇我们将介绍如何订阅消息的一个子集.例如,我们只需要将日志中的error消息存储到日志文件中而将所有日志消息都在控制台打印出来. 绑定(Bindings) 在前面的例子中,我们创建了交换机和队列的 ...
 - RabbitMQ入门教程(六):路由选择Routing
		
原文:RabbitMQ入门教程(六):路由选择Routing 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
 - RabbitMQ入门教程(十):队列声明queueDeclare
		
原文:RabbitMQ入门教程(十):队列声明queueDeclare 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https:// ...
 - RabbitMQ入门教程(七):主题交换机Topics
		
原文:RabbitMQ入门教程(七):主题交换机Topics 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
 - RabbitMQ入门教程(三):Hello World
		
原文:RabbitMQ入门教程(三):Hello World 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
 - RabbitMQ入门教程(二):简介和基本概念
		
原文:RabbitMQ入门教程(二):简介和基本概念 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn ...
 - RabbitMQ入门教程(十七):消息队列的应用场景和常见的消息队列之间的比较
		
原文:RabbitMQ入门教程(十七):消息队列的应用场景和常见的消息队列之间的比较 分享一个朋友的人工智能教程.比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看. 这是网上的一篇教程写的很好,不知原作 ...
 - RabbitMQ入门教程(十四):RabbitMQ单机集群搭建
		
原文:RabbitMQ入门教程(十四):RabbitMQ单机集群搭建 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://b ...
 
随机推荐
- [ACM_动态规划] 嵌套矩形
			
问题描述:有n个矩阵,每个矩阵可以用两个整数a,b来表示 ,表示他的长和宽,矩阵X (a,b) 可以 嵌套 到Y (c,d) 里面当且仅当 a < c && b < d ...
 - 爱上MVC3~在控制器或Action上动态设定模板页(Layout)
			
回到目录 很多境况下,我们需要设置自己模块的layout,即它的布局页面,在MVC2中叫它模板页面,你可以在return view方法时设置它,当然,这不是一种好方法,因为我不想每个action都去设 ...
 - EF架构~引入规约(Specification)模式,让程序扩展性更强
			
回到目录 规约(Specification)模式:第一次看到这东西是在microsoft NLayer项目中,它是微软对DDD的解说,就像petshop告诉了我们MVC如何使用一样,这个规约模式最重要 ...
 - PHP实现RESTful风格的API实例(三)
			
接前一篇PHP实现RESTful风格的API实例(二) .htaccess :重写URL,使URL以 /restful/class/1 形式访问文件 Options +FollowSymlinks R ...
 - pycharm快捷键 - 官方全
			
pycharm快捷键 - 官方全 Ctrl + F12 显示文件内的成员,继承的成员
 - 下列哪项不属于jdk1.6垃圾收集器?
			
下列哪项不属于jdk1.6垃圾收集器? A. Serail 收集器 B. parNew 收集器 C. CMS 收集器 D. G1 收集器 答案:D 解:Java垃圾收集器发展路径下图所示: 其 ...
 - WPF/Silverlight 下的图片局部放大
			
最近的项目中也要用到一个局部图片放大的功能,园子里面一搜,发现(菩提下的杨过)杨大侠已经实现了. 请参见这里:http://www.cnblogs.com/yjmyzz/archive/2009/12 ...
 - QQ左侧滑动显示之自定义属性
			
上一篇为大家实现了最基本的侧滑效果,相信很多小伙伴已经发现一个小问题了,修改Menu右侧的宽度时,我们需要修改我们的自定义方法,这样非常不方便,下面就为大家介绍一下如何通过自定义属性来控制这个的变化. ...
 - poj 1386 Play on Words(有向图欧拉回路)
			
/* 题意:单词拼接,前一个单词的末尾字母和后一个单词的开头字母相同 思路:将一个单词的开头和末尾单词分别做两个点并建一条有向边!然后判断是否存在欧拉回路或者欧拉路 再次强调有向图欧拉路或欧拉回路的判 ...
 - Java:那些把自己陷进去的误区(一)
			
那些把自己陷进去的误区 1.1数据类型 1.整型: 1.在Java中,整形的范围为-2147 483 648—2147483647,并且这个范围与运行Java代码的机器无关,此举大大解决了移植问题 ...