【译】RabbitMQ:Topics
在前面的教程中,我们对日志系统进行了功能强化。我们使用direct类型的交换器并且为之提供了可以选择接收日志的能力,替换了只能傻乎乎的广播消息的fanout类型的交换器。尽管使用direct类型的交换器强化了系统,但是它依然有一些限制,不能基于条件的进行路由。
在日志系统中,我们或许希望不仅能根据严重等级,还能基于日志的发送源来订阅日志日志。你可能已经从Unix的syslog工具中知道了这个概念,该工具路由日志的时候既基于严重等级(info/warn/crit...)又基于设备(auth/cron/kern...)。这将带来极大的灵活性,我们可能仅仅希望监听来自‘cron’的极其重要的错误,同时监听来自‘kern’的所有日志。为了在日志系统中实现它,我们需要学习更复杂的topic交换器。
Topic交换器
发送消息到topic交换器不能随心所欲的指定路由关键字(routing key),它必须是一个由点(.)分割的单词列表。这些单词可以是任意的,但是通常会在其中指定一些发送消息的特性,如“stock.usd.nyse”,“nyse.vmw”,“quick.orange.rabbit”等便是一些合法的路由关键字。你可以在路由关键字中指定任意数量的单词,但是不能超过上限255字节。
绑定关键字必须是同样的样式,topic交换器的内部逻辑和direct交换器的内部逻辑很相似。使用特定路由关键字发送的消息会被转发到匹配绑定关键字的所有队列。关于绑定关键字,有两点需要特别注意:
- *(星号)可以代表一个单词
- #(井号)可以代表零个或多个单词
下图示例是对此最简单的说明:

在这个示例中,我们打算发送的全是用来描述动物的消息,这些消息会使用由三个单词(两个点)组成的路由关键字来发送。在路由关键字中,第一个单词描述速度,第二个单词描述颜色,第三个单词描述物种。即:
"<speed>.<colour>.<species>"。
我们将创建三个绑定,队列Q1绑定到关键字“*.orange.*”,队列Q2绑定到关键字“*.*.rabbit”和“lazy.#”。这些绑定可以被总结如下:
- 队列Q1对所有橙色(orange)的动物感兴趣
- 队列Q2对兔子(rabbit)的所有事情和所有懒惰(lazy)动物的事情感兴趣
一个被设置路由关键字被设置为“quick.orange.rabbit”的消息将会被同时发布到两个队列,路由关键字为“lazy.orange.elephant”的消息也会被同时发布到两个队列。反之,路由关键字为“quick.orange.fox”的消息将会被发布到队列Q1,路由关键字为“lazy.brown.fox”的消息将被发布到队列Q2。而路由关键字为“lazy.pink.rabbit”的消息只会被发布到队列Q2一次,尽管它匹配两个绑定。路由关键字为“quick.brown.fox”的消息不匹配任意一个队列,故该消息会被销毁。
如果我们打破约定,使用一个单词或者四个单词作为路由关键字(例如:“orange”或“quick.orange.male.rabbit”)会发生什么呢?这类消息因为不匹配任意一个绑定,所以将会丢失。相反的,“lazy.orange.male.rabbit”虽然有四个单词,但是它匹配最后一个绑定,消息将会被发布到队列Q2中。
Topic交换器
Topic交换器非常强大,能表现出其他交换器的行为。当一个队列用井号(#)作为绑定,它将接收所有的消息,忽略路由关键字,像fanout交换器一样。当在绑定中不使用特殊字符星号(*)和井号(#)时,它看起来就像direct交换器了。
组合在一起
我们将要在日志系统中使用topic交换器,从假设日志的路由关键字由两个单词组成(<facility>.<severity>|<设备>.<严重程度>)。
代码和上一个教程中是一样的,EmitLogTopic.cs中的代码:
using System;
using System.Linq;
using RabbitMQ.Client;
using System.Text; class EmitLogTopic
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "topic_logs",
type: "topic"); var routingKey = (args.Length > ) ? args[] : "anonymous.info";
var message = (args.Length > )
? string.Join(" ", args.Skip( ).ToArray())
: "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "topic_logs",
routingKey: routingKey,
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
}
}
}
ReceiveLogsTopics.cs中的代码:
using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text; class ReceiveLogsTopic
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "topic_logs", type: "topic");
var queueName = channel.QueueDeclare().QueueName; if(args.Length < )
{
Console.Error.WriteLine("Usage: {0} [binding_key...]",
Environment.GetCommandLineArgs()[]);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
Environment.ExitCode = ;
return;
} foreach(var bindingKey in args)
{
channel.QueueBind(queue: queueName,
exchange: "topic_logs",
routingKey: bindingKey);
} Console.WriteLine(" [*] Waiting for messages. To exit press CTRL+C"); var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var routingKey = ea.RoutingKey;
Console.WriteLine(" [x] Received '{0}':'{1}'",
routingKey,
message);
};
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
运行下面的示例:
接收所有的日志:
$ ReceiveLogsTopic.exe "#"
接收来自设备“kern”的所有日志:
$ ReceiveLogsTopic.exe "kern.*"
或者你只想关注“严重(critical)”等级的日志:
$ ReceiveLogsTopic.exe "*.critical"
你也可以创建多个绑定:
$ ReceiveLogsTopic.exe "kern.*" "*.critical"
使用路由关键字“kern.critical”发送一条日志:
$ EmitLogTopic.exe "kern.critical" "A critical kernel error"
希望这些程序能让你玩的开心。注意,这些代码并没有对路由或绑定关键字做任何假设,你也可以尝试超过两个参数的路由关键字。
下面是一些棘手的问题:
- 使用星号(*)的绑定是否能捕获到空路由关键字的消息?
- 使用“#.*”的绑定是否会将“..”作为一个关键字?是否会捕获仅使用单个单词作为关键字的消息?
- “a.*.#”和“a.#”有什么不同?
下一步,在教程六种将了解如何在远程过程调用中使用往返消息。
原文链接:http://www.rabbitmq.com/tutorials/tutorial-five-dotnet.html
【译】RabbitMQ:Topics的更多相关文章
- [译]RabbitMQ教程C#版 - “Hello World”
[译]RabbitMQ教程C#版 - “Hello World” 先决条件本教程假定RabbitMQ已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需 ...
- [译]rabbitmq 2.5 Where’s my message? Durability and you
我对rabbitmq学习还不深入,这些翻译仅仅做资料保存,希望不要误导大家. There’s a dirty secret about creating queues and exchanges in ...
- [译]RabbitMQ教程C#版 - 主题
先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...
- [译]rabbitmq 2.4 Multiple tenants: virtual hosts and separation
我对rabbitmq学习还不深入,这些翻译仅仅做资料保存,希望不要误导大家. With exchanges, bindings, and queues under your belt, you mig ...
- [译]rabbitmq 2.2 Building from the bottom: queues
我对rabbitmq学习还不深入,这些翻译仅仅做资料保存,希望不要误导大家. You have consumers and producers under your belt, and now you ...
- [译]rabbitmq 2.1 Consumers and producers (not an economics lesson)
我对rabbitmq学习还不深入,这些翻译仅仅做资料保存,希望不要误导大家. For now, all you need to know is that producers create messag ...
- [译]RabbitMQ教程C#版 - 工作队列
先决条件 本教程假定RabbitMQ已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难,可以 ...
- [译]RabbitMQ教程C#版 - 远程过程调用(RPC)
先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...
- [译]RabbitMQ教程C#版 - 路由
先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...
- [译]RabbitMQ教程C#版 - 发布订阅
先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...
随机推荐
- IntelliJ IDEA 中文乱码问题解决办法
自己最近在使用IntelliJ IDEA,发现总是出现中文乱码的问题,在网上找了很多教程,发现真是“天下文章一大抄”.还不如自己动手试着解决一下. 1.编辑器以及调试信息中文乱码问题 解决方案: 选择 ...
- js 实现动态的图片时钟
效果如下图 附件有图片 http://files.cnblogs.com/files/biyongyao/时钟.rar 源代码 <!DOCTYPE html> <html> ...
- 二叉树遍历(Binary Tree Traversal)
二叉树的递归遍历比较简单,这里说一下非递归遍历,以中序遍历为例子. 非递归遍历主要用到栈来协助进行.对于一个二叉树,首先根节点入栈,如果有左儿子,则继续入栈,重复直到最左边的儿子,这时候此节点值为要遍 ...
- Servlet--表单、超链接、转发、重定向4种情况的路径
Servlet中相对路径总结 假设web工程使用如下目录结构: 在介绍相对路径和绝对路径前需要先了解几个概念: 服务器的站点根目录:以tomcat服务器为例,tomcat服务器站点根目录就是apach ...
- log4j输出模板
log4j.rootLogger=DEBUG, A1,A2 log4j.appender.A1.MaxFileSize=1kb#10个备份 log4j.appender.A1.MaxBackupInd ...
- 主机WIFI网络环境下,Linux虚拟机网络设置
在主机使用WIFI网络环境下,怎么样进行虚拟机静态ip设置和连接互联网呢,原理什么太麻烦,另类的网络共享而已: 1.其实简单将网络连接模式设置成NAT模式即可. 2.虚拟网络编辑器依旧是桥接模式,选择 ...
- 解决Debian系统的Crontab执行时间时差问题
首先用 * * * * * date >> /root/log.log 做个测试,发现显示的是UTC的时间,但是直接执行date,得到的是CST的时间.可见在Debian里crontab的 ...
- Web APi之安装配置实现Cors跨域
参考:http://www.cnblogs.com/CreateMyself/p/4836628.html 1.通过NuGet下载程序包,搜索程序包[Microsoft.AspNet.WebApi.C ...
- 是德科技完成对Anite的收购
是德科技公司(NYSE:KEYS)日前宣布已经完成对Anite 的收购行动.Anite 是业界领先的无线研发软件解决方案供应商.是德科技通过支付大约6 亿美元现金将其收入麾下,旨在支持是德科技发展无线 ...
- 关于安卓开发当中通过java自带的HttpURLConnection访问XML的java.io.EOFException问题
刚接触安卓开发,试着写个小程序熟悉下,就写了天气预报的小程序,通过httpUrlConnection读流的方式来获取网络公共接口提供的天气XML信息.但在建立http连接时一直报java.io.EOF ...