在之前的系统中,我们改进了我们的日志系统,我们使用direct 交换机代替fanout交换机,可以实现选择性的接受日志。

虽然使用direct 交换机改进了我们的系统,但是对于多种条件的判断,依然存在问题。如我们不仅仅想要根据日志的级别来订阅日志,同时也希望可以通过发出日志的源(即日志的生产者)来订阅。你也许已经通过unix 的工具syslog知道了这个概念,它同时通过级别(info/warn/crit...)和源(auth/cron/kern...).这种方式给了我们很大的灵活性--我们可以同时监听来自cron的critical 级别的错误消息和来自kern的所有消息。

为了在我们的系统中实现这种功能,我们需要学习更为复杂的交换机类型 --topic

Topic 交换机

发送到topic的交换机不能是任意routing_key,而必须是一系列使用"."。这些单词可以是任意的单子,单通常情况是是跟当前消息有关的一些功能,例如:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit".你可以指定任意多个字符,单上线是255字节。

绑定的key必须也是同样个格式,topic交换机背后的原理类似于把带有特定key的消息发送到所有跟这个key相匹配的队列上去,然而对于bindingKey这里有两个特殊的情况:

1:"*"可以代表一个单词(是单词不是字符--即"."分割的单词)

2:"?"可以代表0个或多个单词

例如:

在这个例子中,我们将要发送描述动物的消息,消息的routingkey将会有3部分组成,routingkey的第一个单词描述的是速度,第二个单词描述的是颜色,第三个单词是特殊描述: "<speed>.<colour>.<species>"。

我们创建了三个绑定,Q1使用bingingkey"*.orange.*",Q2使用"*.*.rabbit" 和"lazy.#"

绑定总结如下:

Q1所有的orange动物感兴趣

Q2想要所有的关于rabbits的消息,和所有的敢于layz动物的消息。

一个带有"quick.orange.rabbit"routingkey的消息会两个队列都接收,"lazy.orange.elephant"的消息同业也会被转发到两个队列上。而"quick.orange.fox"的消息仅仅进入Q1,"lazy.brown.fox"的消息只能被Q2接收。"lazy.pink.rabbit"的消息将再次被转发到Q2上,即使它匹配到了队列2的两个bingdingKey。"quick.brown.fox"的消息不会匹配到任何绑定,所有消息将会被忽略。

如果我们打破我们的约定发送带有4个单词的消息将会发生什么,例如"orange" 或者"quick.orange.male.rabbit"?答案是这些routingkey将不会匹配到任何的bingdingkey,因此将会被忽略

但是"lazy.orange.male.rabbit",因为匹配到了"lazy.#"bingdingkey,所有可以被Q2接收,即使其有4个单词。

Topic 交换机:Topic 交换机功能非常强大,可以完成像其他交换机那样工作。

当队列的bingdingkey使用"#" 时,它将会接收所有的消息,不论routingkey是什么--像fanout交换机那样

当特殊的"*"和"#"都没在bindingkey中使用时,它的行为就像direct交换机那样工作。

汇总

我没将要在我们的日志系统中使用"topic"交换机,我们将定我们的日志系统的routingkey有2个单词:"<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);
}
}
}

ReceiveLogsTopic.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"

发送一个routingkey是 "kern.critical"的消息

EmitLogTopic.exe "kern.critical" "A critical kernel error"

RabbitMQ 原文译05--Topics的更多相关文章

  1. RabbitMQ 原文译1.2--"Hello Word"

    本系列文章均来自官网原文,属于个人翻译,如有雷同,权当个人归档,忽喷. .NET/C# RabbitMQ 客户端下载地址:https://github.com/rabbitmq/rabbitmq-do ...

  2. RabbitMQ 原文译1.1--HelloWord

    本系列文章均来自官网原文,属于个人翻译,如有雷同,权当个人归档,忽喷. RabitMQ 是一个消息中间件,其实就是从消息生产者那里接受消息,然后发送给消息消费者.在这个传输过程中,可以定义一些缓存,持 ...

  3. RabbitMQ 原文译03--发布和订阅

    发布/订阅 在之前的案例中我们创建了一个工作队列,这个工作队列的实现思想就是一个把每一个任务平均分配给每一个执行者,在这个篇文章我们会做一些不一样的东西,把一个消息发送给多个消费者,这种模式就被称作& ...

  4. RabbitMQ 原文译02--工作队列

    工作队列: 在上一篇文章中我们我们创建程序发送和接受命名队列中的消息,在这篇文章我会创建一个工作队列,用来把耗时的操作分配给多个执行者. 工作队列(任务队列)的主要实现思想是避免马上执行资源密集型的任 ...

  5. RabbitMQ 原文译06--Remote procedure call(RPC)

    在第三篇文章中, 我们学习了怎么使用队列在多了消息消费者当中进行耗时任务轮询. 但是如果我们想要在远程电脑上运行一个方法,然后等待其执行结果,这就是一个不同的场景,这种就是我们一般讲的RPC(远程过程 ...

  6. RabbitMQ 原文译04--路由

    在前一篇文章中我们构建了一个简单的日志系统,我们可以向多个接受者广播消息. 在这篇文章我,我们将要添加一些功能使得针对部分消息的接受成为可能,例如我们只对错误的消息进行磁盘记录,同时又可以把所有的消息 ...

  7. RabbitMQ(五) -- topics

    RabbitMQ(五) -- topics `rabbitmq`中的`topic exchange`将路由键和某模式进行匹配,从而类似于正则匹配的方式去接收喜欢的信息. topic exchange ...

  8. 译:5.RabbitMQ Java Client 之 Topics (主题)

    在 上篇博文 译:4.RabbitMQ 之Routing(路由) 中,我们改进了日志系统. 我们使用的是direct(直接交换),而不是使用只能进行虚拟广播的 fanout(扇出交换) ,并且有可能选 ...

  9. [原创]Hadoop-2.5.2-HA原文译

    使用the Quorum Journal Manager实现HDFS高可用 2017/1/22 11:57:22 原文 目的(Purpose) * 这个指南提供了对HDFS-HA特性,使用QJM特性如 ...

随机推荐

  1. JS中String,Math常用函数

    String对象: 1.length属性 说明:获取字符串的长度 实例: var str="abc"; var i=str.length;//output:3 2.charAt() ...

  2. ES5新特性:理解 Array 中增强的 9 个 API

    为了更方便的对JS中Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.index ...

  3. BrnShop开源网上商城第二讲:ASP.NET MVC框架

    在团队设计BrnShop的web项目之初,我们碰到了两个问题,第一个是数据的复用和传递,第二个是大mvc框架和小mvc框架的选择.下面我依次来说明下. 首先是数据的复用和传递:对于BrnShop的每一 ...

  4. Scheme Implementations对比

    MIT Scheme http://www.gnu.org/software/mit-scheme/ GNU Guile http://www.gnu.org/software/guile/ Rack ...

  5. [Buffalo]MVC架构模式

    MVC架构模式:即Model(模型)-View(视图)-Controller(控制器). 很多人将MVC和所谓的“三层架构”进行比较,其实两者并没有什么可比性,MVC更不是分别对应着UI.业务逻辑和数 ...

  6. .net软件自动化测试笔记(API-1)

    .net 软件测试自动化之道 API(Application Programming Interface)包括单元测试(Unit Testing),模块测试(Module Testing),组件测试( ...

  7. html input设置为只读属性

    有两种方式可以实现input的只读效果:disabled 和 readonly. 自然两种出来的效果都是只能读取不能编辑,可是两者有很大不同. Disabled说明该input无效,及其value不会 ...

  8. globalfifo设备驱动

    把globalmem中的全局内存变成一个FIFO,只有当FIFO中有数据的时候(即有进程把数据写到这个FIFO而且没有被读进程读空),读进程才能把数据读出,而且读取后的数据会从globalmem的全局 ...

  9. HW2.5

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  10. em与px之间的换算

    任意浏览器的默认字体高度16px(16像素).所有未经调整的浏览器都符合:1em=16px.那么12px=0.75em,10px=0.625em.为了简化font-size的换算,需要在css中的bo ...