先决条件

本教程假定 RabbitMQ 已经安装,并运行在localhost 标准端口(5672)。如果你使用不同的主机、端口或证书,则需要调整连接设置。

从哪里获得帮助

如果您在阅读本教程时遇到困难,可以通过邮件列表 联系我们

路由

(使用.NET客户端)

教程[3] 中,我们构建了一个简单的日志系统,可以向多个接收者广播消息。

在本教程中,我们会为日志系统再添加一个特性,使其可以只订阅消息的一个子集。例如,将所有日志消息打印到

控制台,同时只会将严重错误消息写入日志文件(保存到磁盘空间)。

绑定

在前面的例子中,我们创建过绑定。不知道您是否还记得下面的代码:

channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");

绑定是指交换器和队列之间的关联关系。可以简单地理解为:某个队列对来自此交换器的消息感兴趣。

绑定可以采用额外的routingKey参数,为了避免与BasicPublish方法中相同参数混淆,我们将其称为binding key(这里是指路由键从声明角度的一种别称,绑定键)。下面即是如何使用绑定键 建立一个绑定:

channel.QueueBind(queue: queueName,
exchange: "direct_logs",
routingKey: "black");

绑定键的含义取决于交换器类型。像我们前面使用的fanout 交换器,忽略了它的值(依据fanout交换器的特性,它会把消息广播到所有订阅的队列,所以就算指定routingKey也不会根据其过滤消息)。

Direct交换器

在上篇教程中,我们的日志系统会把所有消息广播给所有消费者,现在我们想要扩展使其可以根据消息的严重性过滤消息。例如,我们希望将日志消息写入磁盘的脚本仅接收严重错误的消息,而不是在警告或者信息类型的消息上浪费磁盘空间。

之前我们使用的是fanout交换器,它没有给我们足够的灵活性 - 它只能进行无意识的广播。

现在我们要用direct交换器替换它,direct交换器背后的路由算法很简单 - 消息会进入其binding key恰好与routing key相匹配的队列。

为了说明这一点,请参考以下设置:

在上面的设置中,我们可以看到direct交换器X与两个队列绑定。第一个队列通过键orange绑定,第二个队列有两个绑定,一个通过键black绑定、另外一个通过键green绑定。

如此设置,发布使用路由键orange的消息到交换器最终会被路由到队列Q1,路由键为blackgreen的消息会去向队列Q2,而其他所有的消息会被丢弃。

多重绑定

使用相同的绑定键绑定多个队列是完全合法的。在示例中,我们可以在XQ1之间添加一个键为black的绑定。这种情况下,direct交换器会像fanout交换器一样,把消息广播到所有匹配的队列,路由键为black的消息会被分别发送到队列Q1Q2

发送日志

我们将在日志系统中使用上述消息模型,在发送消息时使用direct交换机来替换fanout交换器。同时我们会把日志的严重性作为路由键,这样的话,接收脚本就可以选择性地接收它期望严重性的消息。首先我们来关注如何发送日志。

同样地,我们需要先创建一个交换器:

channel.ExchangeDeclare(exchange: "direct_logs", type: ExchangeType.Direct);

准备好发送消息:

var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "direct_logs",
routingKey: severity,
basicProperties: null,
body: body);

简单起见,我们先假定severity可以是infowarningerror任意一值。

订阅

马上就可以像前面的教程接收消息了,但有一点不同, 我们需要为我们感兴趣的每种日志严重性级别的消息建立一个新的绑定。

var queueName = channel.QueueDeclare().QueueName;

foreach(var severity in args)
{
channel.QueueBind(queue: queueName,
exchange: "direct_logs",
routingKey: severity);
}

组合在一起

EmitLogDirect.cs类的代码:

using System;
using System.Linq;
using RabbitMQ.Client;
using System.Text; class EmitLogDirect
{
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: "direct_logs",
type: "direct"); var severity = (args.Length > 0) ? args[0] : "info";
var message = (args.Length > 1)
? string.Join(" ", args.Skip(1).ToArray())
: "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "direct_logs",
routingKey: severity,
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent '{0}':'{1}'", severity, message);
} Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}

ReceiveLogsDirect.cs类的代码:

using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text; class ReceiveLogsDirect
{
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: "direct_logs",
type: "direct");
var queueName = channel.QueueDeclare().QueueName; if(args.Length < 1)
{
Console.Error.WriteLine("Usage: {0} [info] [warning] [error]",
Environment.GetCommandLineArgs()[0]);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
Environment.ExitCode = 1;
return;
} foreach(var severity in args)
{
channel.QueueBind(queue: queueName,
exchange: "direct_logs",
routingKey: severity);
} Console.WriteLine(" [*] Waiting for messages."); 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,
autoAck: true,
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}

请像往常一样创建项目(请参阅 教程[1])。

如果您想将warningerror(不包括info)日志消息保存到文件,只需打开控制台并输入:

cd ReceiveLogsDirect
dotnet run warning error > logs_from_rabbit.log

如果您想在屏幕上看到所有日志消息,请打开一个新终端并执行以下操作:

cd ReceiveLogsDirect
dotnet run info warning error
# => [*] Waiting for logs. To exit press CTRL+C

例如,想要发出error日志消息,只需要输入:

cd EmitLogDirect
dotnet run error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'

EmitLogDirect.csReceiveLogsDirect.cs 的完整源代码。

跳转到 教程[5],了解如何基于模式监听消息。

写在最后

本文翻译自 RabbitMQ 官方教程 C# 版本。如本文介绍内容与官方有所出入,请以官方最新内容为准。水平有限,翻译的不好请见谅,如有翻译错误还请指正。

  • 原文链接:RabbitMQ tutorial - Routing
  • 实验环境:RabbitMQ 3.7.4 、.NET Core 2.1.3、Visual Studio Code
  • 最后更新:2018-08-31

[译]RabbitMQ教程C#版 - 路由的更多相关文章

  1. [译]RabbitMQ教程C#版 - “Hello World”

    [译]RabbitMQ教程C#版 - “Hello World”   先决条件本教程假定RabbitMQ已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需 ...

  2. [译]RabbitMQ教程C#版 - 主题

    先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...

  3. [译]RabbitMQ教程C#版 - 发布订阅

    先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...

  4. [译]RabbitMQ教程C#版 - 工作队列

    先决条件 本教程假定RabbitMQ已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难,可以 ...

  5. [译]RabbitMQ教程C#版 - 远程过程调用(RPC)

    先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...

  6. [译]RabbitMQ教程C#版 - "Hello World"

    先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...

  7. RabbitMQ教程C#版 - 发布订阅

    先决条件本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助如果您在阅读本教程时遇到困难,可以 ...

  8. RabbitMQ教程C#版 - Hello World

    先决条件 本教程假定RabbitMQ已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难,可以 ...

  9. RabbitMQ教程C#版 - 工作队列

    先决条件本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助如果您在阅读本教程时遇到困难,可以 ...

随机推荐

  1. ADB——管理应用

    ADB应用管理 主要操作有查看应用列表.安装应用.卸载应用.清楚应用数据与缓存.查看前台Activity.查看应用信息及安装路径等等 查看应用列表 查看应用列表的基本命令格式是 adb shell p ...

  2. 【java.sql.SQLException: Before start of result set】

    将ResultSet转换为 Map<String,String>时抛出了一个这样的异常:java.sql.SQLException: Before start of result set ...

  3. Windows SFTP 的安装

    用于Windows系统的免费SFTP服务器-Free SFTP Servers 前不久,有人问我:“怎么从 Linux 系统传文件到 Windows 服务器,不能用 FTP 协议.” 文件数量不大.用 ...

  4. Docker入门详解(转载)

    来源 http://dockone.io http://dockone.io/article/6051 Docker是世界领先的软件容器平台,所以想要搞懂Docker的概念我们必须先从容器开始说起. ...

  5. Vim编辑器设置及其使用

    一.vim编辑器的相关配置文件 /etc/vimrc:vim编辑器的全局配置文件,针对所有用户有效 /etc/virc:vi编辑器的全局配置文件,针对所有用户有效 ~/.vimrc:vim编辑器的其他 ...

  6. cygwin 安装 apt-cyg

    apt-cyg apt-cyg is a Cygwin package manager. It includes a command-line installer for Cygwin which c ...

  7. java学习之静态内部类

    /*静态内部类 * 在类内部定义一个静态内部类 * 静态的含义是该内部类可以向其他静态成员一样,不用依赖与外部类的对象.可以直接使用 * 因此该类只能访问外部类的静态成员 * * * * */ cla ...

  8. Json和Ajax

    LogDao接口 package com.log.dao; import java.util.List; import com.log.Vo.LogInfoVo; import com.log.bea ...

  9. python练习--利用while循环和if语句,完成猜骰子的数字大小

    #exampleimport random# 骰子投掷的随机叔numnum = random.randint(1,6)# 输入一个猜测的数字temp = input("请输入一个整数:&qu ...

  10. es6中的模块化

    在之前的javascript中是没有模块化概念的.如果要进行模块化操作,需要引入第三方的类库.随着技术的发展,前后端分离,前端的业务变的越来越复杂化.直至ES6带来了模块化,才让javascript第 ...