在上一个教程中,我们改进了我们的日志记录系统。我们使用direct类型转发器,使得接收者有能力进行选择性的接收日志,,而非fanout那样,只能够无脑的转发

虽然使用direct类型改进了我们的系统,但它仍然存在一些局限性 - 它不能够基于多重条件进行路由选择。

在我们的日志记录系统中,我们可能不仅要根据严重性订阅日志,还可以基于发出日志的源进行订阅。您可能会从unix工具syslog 中了解此概念,该工具根据严重性(info / warn / crit ...)和设备(auth / cron / kern ...)转发日志。

这将给我们带来很大的灵活性 - 我们可能想要订阅来自cron的严重错误,也可以听kern的所有日志。

为了在我们的系统中实现上述的需求,我们需要学习稍微复杂的主题类型的转发器(topic exchange)

主题转发(Topic Exchange)

发往主题类型的转发器的消息不能随意的设置选择键(routing_key) - 它必须是由点分隔的单词列表。这些单词可以是任何东西,但通常它们指定与消息相关联的一些功能。几个有效的路由密钥示例:stock.usd.nysenyse.vmwquick.orange.rabbit。路由密钥中可以有任意多的单词,最多可达255个字节。

绑定键也必须是相同的形式。主题类型的转发器背后的逻辑和直接类型的转发器很类似:一个附带特殊的选择键将会被转发到绑定键与之匹配的队列中。需要注意的是:关于绑定键有两种特殊的情况。:

  1. *(星)可以替代一个单词。
  2. #(哈希)可以替换零个或多个单词。

在一个例子中最简单的解释一下:

在这个例子中,我们将发送所有描述动物的消息。消息会附加一个选择键包含将使用由三个单词标识符(两个点隔开)。第一个单词标识符描述速度,第二个单词标识符描述动物的颜色,和第三个单词标识符描述动物的物种:<speed>.<color>.<species>

我们创建了三个绑定:Q1绑定键*.orange.*Q2*.*.rabbitlazy.#绑定。

这些绑定可以总结为:

  1. Q1对所有的橙色动物感兴趣。
  2. Q2想听听有关兔子的一切,以及关于懒惰动物的一切。

一个附带quick.orange.rabbit的选择键的消息将会被转发到两个队列。附带lazy.orange.elephant的消息也会被转发到两个队列。另一方面,quick.orange.fox只会转到Q1,而lazy.brown.fox只能被转发到Q2。 lazy.pink.rabbit虽然与两个绑定键匹配,但是也只会被转发到Q2一次。 quick.brown.fox不能与任何绑定键匹配,因此它将被丢弃。

如果我们违法我们的约定,发送一个或者四个标识符的选择键,类似:orangequick.orange.male.rabbit,这些选择键不能与任何绑定键匹配,所以消息将会被丢弃。

另一方面,lazy.orange.male.rabbit虽然是四个标识符,也可以与lazy.#匹配,从而转发至Q2。

Topic exchange

  1. 主题类型的转发器非常强大,可以实现其他类型的转发器。
  2. 当队列与`#`(哈希)绑定键绑定时,它将接收所有消息,而不管路由键,类似`fanout`类型转发器。
  3. 当特殊字符`*`(星号)和`#`(哈希)在绑定中不被使用时,主题转发器将类似direct类型转发器。

完整的例子

我们将在我们的日志记录系统中使用topic转发器。 我们将从一个工作假设开始,日志的选择键将有两个单词:<facility>.<severity>

EmitLogTopic.cs的代码:

  1. using System;
  2. using System.Linq;
  3. using RabbitMQ.Client;
  4. using System.Text;
  5. class EmitLogTopic
  6. {
  7. public static void Main(string[] args)
  8. {
  9. var factory = new ConnectionFactory() { HostName = "localhost" };
  10. using(var connection = factory.CreateConnection())
  11. using(var channel = connection.CreateModel())
  12. {
  13. channel.ExchangeDeclare(exchange: "topic_logs",
  14. type: "topic");
  15. var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
  16. var message = (args.Length > 1)
  17. ? string.Join(" ", args.Skip( 1 ).ToArray())
  18. : "Hello World!";
  19. var body = Encoding.UTF8.GetBytes(message);
  20. channel.BasicPublish(exchange: "topic_logs",
  21. routingKey: routingKey,
  22. basicProperties: null,
  23. body: body);
  24. Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
  25. }
  26. }
  27. }

ReceiveLogsTopic.cs的代码:

  1. using System;
  2. using RabbitMQ.Client;
  3. using RabbitMQ.Client.Events;
  4. using System.Text;
  5. class ReceiveLogsTopic
  6. {
  7. public static void Main(string[] args)
  8. {
  9. var factory = new ConnectionFactory() { HostName = "localhost" };
  10. using(var connection = factory.CreateConnection())
  11. using(var channel = connection.CreateModel())
  12. {
  13. channel.ExchangeDeclare(exchange: "topic_logs", type: "topic");
  14. var queueName = channel.QueueDeclare().QueueName;
  15. if(args.Length < 1)
  16. {
  17. Console.Error.WriteLine("Usage: {0} [binding_key...]",
  18. Environment.GetCommandLineArgs()[0]);
  19. Console.WriteLine(" Press [enter] to exit.");
  20. Console.ReadLine();
  21. Environment.ExitCode = 1;
  22. return;
  23. }
  24. foreach(var bindingKey in args)
  25. {
  26. channel.QueueBind(queue: queueName,
  27. exchange: "topic_logs",
  28. routingKey: bindingKey);
  29. }
  30. Console.WriteLine(" [*] Waiting for messages. To exit press CTRL+C");
  31. var consumer = new EventingBasicConsumer(channel);
  32. consumer.Received += (model, ea) =>
  33. {
  34. var body = ea.Body;
  35. var message = Encoding.UTF8.GetString(body);
  36. var routingKey = ea.RoutingKey;
  37. Console.WriteLine(" [x] Received '{0}':'{1}'",
  38. routingKey,
  39. message);
  40. };
  41. channel.BasicConsume(queue: queueName,
  42. noAck: true,
  43. consumer: consumer);
  44. Console.WriteLine(" Press [enter] to exit.");
  45. Console.ReadLine();
  46. }
  47. }
  48. }

运行以下示例:

收到所有的日志:

  1. cd ReceiveLogsTopic
  2. dotnet run "#"

从设备kern接收所有日志:

  1. cd ReceiveLogsTopic
  2. dotnet run "kern.*"

或者如果您只想收到关于critical的日志:

  1. ReceiveLogsTopic.exe "*.critical"

您可以创建多个绑定:

  1. cd ReceiveLogsTopic
  2. dotnet run "kern.*" "*.critical"

并使用选择键kern.critical类型发出日志:

  1. cd EmitLogTopic
  2. dotnet run "kern.critical" "A critical kernel error"

RabbitMQ 官方NET教程(五)【Topic】的更多相关文章

  1. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

  2. RabbitMQ官方教程五 Topic(GOLANG语言实现)

    在上一教程中,我们改进了日志记录系统. 我们没有使用只能进行虚拟广播的fanout交换器,而是使用直接交换器,并有可能选择性地接收日志. 尽管使用直接交换改进了我们的系统,但它仍然存在局限性-它不能基 ...

  3. RabbitMQ Go客户端教程5——topic

    本文翻译自RabbitMQ官网的Go语言客户端系列教程,本文首发于我的个人博客:liwenzhou.com,教程共分为六篇,本文是第五篇--topic. 这些教程涵盖了使用RabbitMQ创建消息传递 ...

  4. RabbitMQ 官方NET教程(三)【发布/订阅】

    上一篇博客中,我们实现了工作队列,并且我们的工作队列中的一个任务只会发给一个工作者,除非某个工作者未完成任务意外被杀死,会转发给另外的工作者.在这部分中,我们会做一些完全不同的事情 - 我们会向多个消 ...

  5. RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    1.topic类型的Exchange 我们之前说过Topic类型的Exchange是direct类型的模糊查询模式,可以通过routkey来实现模糊消费message,topic的模糊匹配有两种模式: ...

  6. RabbitMQ 官方NET教程(六)【RPC】

    在第二个教程中,我们学习了如何使用Work Queues在多个工作者之间分配耗时的任务. 但是如果我们需要在远程计算机上运行功能并等待结果怎么办? 那是一个不同的模式. 此模式通常称为远程过程调用或R ...

  7. RabbitMQ 官方NET教程(四)【路由选择】

    在上一个教程中,我们构建了一个简单的日志记录系统. 我们能够广播日志消息给所有你的接收者. 在本教程中,我们将为其添加一个功能 - 我们将让日志接收者可以仅订阅一部分消息. 例如,我们将能够仅将关键的 ...

  8. RabbitMQ 官方NET教程(二)【工作队列】

    这篇中我们将会创建一个工作队列用来在工作者(consumer)间分发耗时任务. 工作队列的主要任务是:避免立刻执行资源密集型任务和避免必须等待其完成.相反地,我们进行任务调度:我们把任务封装为消息发送 ...

  9. RabbitMQ 官方NET教程(一)【介绍】

    本教程假定RabbitMQ已在标准端口(5672)上的localhost上安装并运行.如果使用不同的主机,端口或凭据,连接设置将需要调整. RabbitMQ是一个消息代理:它接受并转发消息. 您可以将 ...

随机推荐

  1. Python----DFS---骑士周游问题

    这篇文章将会将一个数据结构与算法中一个很经典很重要的概念——深度优先搜索(Depth-First-Search:DFS).........(你他喵不是在标题里说了吗?) 好吧,DFS的精髓我其实也还没 ...

  2. php第十八节课

    PDO 对不同的数据库连接使用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...

  3. Array.prototype.slice.call()的理解

    最近在看廖雪峰的JS课程,浏览器中的操作DOM的那一章,有这样一道题. JavaScript Swift HTML ANSI C CSS DirectX <!-- HTML结构 --> & ...

  4. / Vijos / 题库 /1250 / 最勇敢的机器人

    / Vijos / 题库 /1250 / 最勇敢的机器人 借鉴博客:http://www.cnblogs.com/chty/p/5830516.html 背景 Wind设计了很多机器人.但是它们都认为 ...

  5. Git:文件操作和历史回退

    目录 创建仓库 创建文件/文件夹 修改文件/文件夹 回到修改前的版本 撤销修改 删除文件 工作区.暂存区.版本区 创建仓库 创建新文件夹:mkdir learngit 进入:cd learngit l ...

  6. GeoTrust 企业(OV)型 多域名(SAN/UC)版

     GeoTrust 企业(OV)型 多域名(SAN/UC)版 SSL证书(GeoTrust True BusinessID With Multi-Domain(SAN/UC) ),支持多域名,属于企业 ...

  7. qwb与整数对

    qwb与整数对 Time Limit: 1 Sec  Memory Limit: 128 MB Description qwb又遇到了一道数学难题,你能帮助他吗? 给出两个整数n和m,请统计满足0&l ...

  8. Android第三方开源下拉框:NiceSpinner

     Android第三方开源下拉框:NiceSpinner Android原生的下拉框Spinner基本上可以满足Android开发对于下拉选项的设计需求,但现在越来越流行的下拉框不满足于Andro ...

  9. [bzoj1926][Sdoi2010]粟粟的书架_二分_主席树

    粟粟的书架 bzoj-1926 Sdoi-2010 题目大意:题目链接 注释:略 想法:分成两个题 前面的我们可以二分,直接二分出来检验即可. 对于R=1的,相当一个数列,我们在上面建立主席树. 然后 ...

  10. 机器学习10k均值

    下面介绍无监督机器学习算法,与前面分类回归不一样的是,这个不知道目标变量是什么,这个问题解决的是我们从这些样本中,我们能发现什么. 这下面主要讲述了聚类算法,跟数据挖掘中的关联挖掘中的两个主要算法. ...