(五)「消息队列」之 RabbitMQ 主题(使用 .NET 客户端)

0、引言

先决条件

本教程假设 RabbitMQ 已安装并且正在 本地主机 的标准端口(5672)上运行。如果您使用了不同的主机、端口或凭证,则要求调整连接设置。

获取帮助

如果您在阅读本教程时遇到问题,可以通过邮件列表或者 RabbitMQ 社区 Slack 与 RabbitMQ 官方取得联系。

在上一篇教程中我们改进了日志系统。我们没有使用只能进行虚拟广播的 fanout 交换机,而是使用了 direct 直连交换机,从而获得了选择性接收日志的可能性。

尽管使用了 direct 直连交换机改进了我们的系统,但它仍然有局限性 —— 它不能基于多个标准进行路由。

在我们的日志系统中,我们可能不仅希望订阅根据严重程度区分的日志,还希望订阅根据来源区分的日志。您可能从 syslog unix 工具中了解过这个概念,它根据严重程度(info/warn/crit…)和设备(auth/cron/kern…)路由日志。

这可以给我们很大的灵活性 —— 我们也许希望仅了解来自于 ‘corn’ 的关键错误,但同样也希望了解来自于 ‘kern’ 的所有日志。

为了在我们的日志系统中实现这一点,我们需要学习一种更复杂的 topic 主题交换机。

原文链接:https://www.rabbitmq.com/tutorials/tutorial-five-dotnet.html

1、主题交换机

发送给 topic 主题交换机的消息不能有任意的路由键 —— 它只能是一个由点分隔的单词列表。单词可以是任何东西,但通常它们指定与消息相关的一些特征。下面是一些有效的路由键示例:“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”。路由键中可以有任意多的单词,最多不超过 255 字节。

绑定键也必须是同样的形式。topic 主题交换机背后的逻辑与 direct 直连交换机类似 —— 使用特定 routing key 路由键发送的消息将被传递给使用匹配 binding key 绑定键绑定的所有队列。但是,绑定键有两种重要的特殊情况:

  • *(星号)只能匹配一个单词。
  • #(井号)可以匹配零或者多个单词。

用一个例子来解释这一点最简单:

tutorial-five

在这个例子当中,我们将会发送一些均用来描述动物的消息。我们将会使用包含三个单词(两个点)的 routing key 路由键发送这些消息。路由键中的第一个单词用于描述速度,第二个描述颜色以及第三个描述物种:“<speed>.<colour>.<species>”。

我们创建三个绑定:Q1 使用 “*.orange.*” 绑定键绑定;Q2 使用 “*.*.rabbit” 和 “lazy.#” 绑定。

这些绑定可以被总结为:

  • Q1 对所有的橙色动物感兴趣。
  • Q2 想知道关于兔子以及懒惰动物的一切信息。
  1. 带有 “quick.orange.rabbit” 路由键的消息将被同时传递给两个队列。“lazy.orange.elephant” 消息同样也会同时前往两个队列。
  2. 另一方面,“quick.orange.fox” 只会前往 Q1,而 “lazy.brown.fox” 只会前往 Q2
  3. lazy.pink.rabbit” 只会被传递给 Q2 一次,即便它与两个绑定匹配。
  4. quick.brown.fox” 不匹配任何绑定,因此将被丢弃。

如果我们违反约定,发送带有一个或者四个单词的消息(比如 “orange” 或者 “quick.orange.new.rabbit”),会发生什么呢?好吧,这些消息将不会匹配任何绑定并且丢失。

另一方面,即便 “lazy.orange.new.rabbit” 有四个单词,但仍然匹配最后一个绑定,所以将会被传递给 Q2 队列。

主题交换机

主题交换机十分强大并且能够模拟其他交换机。

当一个队列使用 “#”(井号)绑定键绑定时 —— 它将会忽略路由键接收所有消息 —— 就像 fanout 扇出交换机那样。

当绑定中不使用 “*”(星号)与 “#”(井号)特殊字符时,topic 主题交换机将会表现得像是 direct 直连交换机。

2、将所有的东西放到一起

我们将在日志系统中使用 topic 主题交换机。我们将从一个工作假设开始:即日志的路由键将有两个单词:“<facility>.<severity>”。

代码几乎与上一篇教程中一致。

EmitLogTopic.cs 的代码:

using System.Text;
using RabbitMQ.Client;var factory = new ConnectionFactory { HostName = "localhost" };using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();channel.ExchangeDeclare(exchange: "topic_logs", type: ExchangeType.Topic);var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
var message = (args.Length > 1)? string.Join(" ", args.Skip(1).ToArray()): "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "topic_logs",routingKey: routingKey,basicProperties: null,body: body);
Console.WriteLine($" [x] Sent '{routingKey}':'{message}'");

ReceiveLogsTopic.cs 的代码:

using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;var factory = new ConnectionFactory { HostName = "localhost" };using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();channel.ExchangeDeclare(exchange: "topic_logs", type: ExchangeType.Topic);
// declare a server-named queue
var queueName = channel.QueueDeclare().QueueName;if (args.Length < 1)
{Console.Error.WriteLine("Usage: {0} [binding_key...]",Environment.GetCommandLineArgs()[0]);Console.WriteLine(" Press [enter] to exit.");Console.ReadLine();Environment.ExitCode = 1;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.ToArray();var message = Encoding.UTF8.GetString(body);var routingKey = ea.RoutingKey;Console.WriteLine($" [x] Received '{routingKey}':'{message}'");
};
channel.BasicConsume(queue: queueName,autoAck: true,consumer: consumer);Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

运行如下示例:

接收所有日志:

cd ReceiveLogsTopic
dotnet run "#"

接收来自 “kern” 的所有日志:

cd ReceiveLogsTopic
dotnet run "kern.*"

或者您只想知道 “critical” 日志:

cd ReceiveLogsTopic
dotnet run "*.critical"

您可以创建多个绑定:

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

并发出带有 “kern.critical” 类型路由键的日志:

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

运行效果:
在这里插入图片描述

😀 玩儿的开心!注意,代码没有对路由键或者绑定键做任何假设,您可能希望试试使用两个以上的路由键参数。

(EmitLogTopic.cs 和 ReceiveLogsTopic.cs 的完整源码)

接下来,在教程六中了解如何将往返消息作为远程过程调用。

5、生产[非]适用性免责声明

请记住,本教程和其他教程都是教程。他们一次展示一个新概念,可能会有意地过度简化一些东西,而忽略其他东西。例如,为了简洁起见,连接管理、错误处理、连接恢复、并发性和指标收集等主题在很大程度上被省略了。这种简化的代码不应该被认为可以用于生产。

在发布您的应用之前,请先查看其他文档。我们特别推荐以下指南:发布者确认和消费者确认,生产清单和监控。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28574.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单工厂模式(java)

目录 结构 案例 类图 代码实现 简单咖啡工厂类 咖啡店类 咖啡类 具体咖啡类 简单工厂方法的优缺点 优点 缺点 结构 简单工厂包含如下角色&#xff1a; 抽象产品 &#xff1a;定义了产品的规范&#xff0c;描述了产品的主要特性和功能。具体产品 &#xff1a;实现或者…

Spring Boot Helper插件免费版的下载

在使用idea的社区版的时候&#xff0c;我们创建springboot项目就要下载springboot helper插件&#xff0c;下载是很简单&#xff0c;但是呢&#xff0c;有一个问题就是&#xff1a; 可以看到它是收费的&#xff0c;要想免费创建spring boot项目那么我们第一可以采用网页版创建&…

Java线程池

目录 线程池 一、Java构建线程的方法 二、线程池的7个参数 三、线程池的执行流程 四、线程池属性标识 4.1.核心属性 4.2.线程池状态变化 五、线程池的 execute 方法执行 六、Worker的封装 七、线程执行的后续处理 线程池 线程池ThreadPoolExecutor源码剖析 // Execu…

B071-项目实战-用户模块--手机注册 管理员登录

目录 完成注册功能后端开发完成UserControllerUserServiceImplLogininfoMapper 前端页面完成绑定数据绑定事件准备登录页 管理员登录1需求分析登录设计页面设计表设计流程设计所需技术 员工新增级联操作登录信息EmployeeServiceImplShopServiceImpl 管理员登录2前端页面后端接口…

MacOS使用USB接口与IPhone进行Socket通信

演示效果如下: 开源地址: GitHub - rsms/peertalk: iOS and Mac Cocoa library for communicating over USB 克隆源码: git clone https://github.com/rsms/peertalk.git 克隆后打开peertalk然后启动xcode工程 先启动MacOS服务端工程,再启动iOS客户端工程 客户端 服务端

php代码逻辑题

<?php error_reporting(0); show_source(__FILE__); $guess $_POST[guess]; $data (array)json_decode($_GET[data]); if(substr(md5($guess),0,27)797ed5077436dc8abaec64750e2)if ($data[aaa] !666 && intval($data[aaa],0) 666 &‮⁦!!⁩⁦& "h…

AtcoderABC249场

A - JoggingA - Jogging 题目大意 高桥和青木一起慢跑&#xff0c;高桥每隔 ACAC 秒钟走 BB 米&#xff0c;然后休息 CC 秒钟&#xff0c;青木每隔 DFDF 秒钟走 EE 米&#xff0c;然后休息 FF 秒钟。现在已经过去了 XX 秒钟&#xff0c;问谁跑得更远。 思路分析 模拟来解决这…

mysql 1 -- 数据库介绍、mysql 安装及设置

Linux 安装 mysql 1、数据库&#xff08;mysql&#xff09; 数据文件 - 数据库过了系统 2、c/s mysql 服务器 mysql 客户端 ip port : 3306 3、关系型 于 非关系型数据库&#xff08;nosql&#xff09; nosql可以解决一些关系型数据库所无法实现的场景引用。 一、数据库介绍 …

VScode 右键菜单加入使用用VSCode打开文件和文件夹【Windows】

VScode 右键菜单加入使用用VSCode打开文件和文件夹【Windows】 介绍修改注册表添加右键打开文件属性修改注册表添加右键打开文件夹属性修改注册表添加右键空白区域属性 介绍 鼠标右击文件或者文件夹&#xff0c;可直接用VSCode打开&#xff0c;非常方便。但如果我们在安装VSCo…

「网络编程」传输层协议_ UDP协议学习_及原理深入理解

「前言」文章内容大致是传输层协议&#xff0c;UDP协议讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、传输层二、UDP协议2.1 再谈端口号2.2.1 端口号范围划分2.2.2 认识知名端口号2.2.3 端口号注意问题2.2.4 netstat命令和pidof命令 2.2 UD…

设计模式之享元模式

写在前面 本文看下一种结构型设计模式&#xff0c;享元模式。 1&#xff1a;介绍 1.1&#xff1a;什么时候使用享元模式 当程序需要大量的重复对象&#xff0c;并且这些大量的重复对象只有部分属性不相同&#xff0c;其他都是相同的时候&#xff0c;就可以考虑使用享元设计…

STM32之按键驱动的使用和自定义(MultiButton)

原始Github地址 Github地址 修改后 调整内容 将宏定义转换成配置结构体 头文件 #ifndef _MULTI_BUTTON_H_ #define _MULTI_BUTTON_H_#include "stdint.h" #include "string.h"//According to your need to modify the constants. //#define TICKS_IN…