在前一篇中,我们构建了一个简单的日志系统,我们已经能够广播消息到许多的接收者。在这一篇中,我们希望增加一个特性,让订阅消息的子集成为可能。例如,我们可以将重要的错误日志存放到日志文件(即,磁盘上面),同时将仍然所有的日志信息打印到控制台。

绑定

在前面的例子中我们已经创建过绑定,你应该还能记得下面的代码:

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

一个绑定是一个交换器和一个队列的关系,可以形象的解读为:这个队列对来自这个交换器的消息感兴趣。绑定可以拥有一个额外的参数:routingKey,为了避免和BasicPublish里面的routingKey混淆,我们称其为绑定关键字(binding Key,下面展示了如何通过一个绑定关键字创建一个绑定:

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

绑定关键字的意义依赖于交换器的类型,前面我们使用过的fanout类型的交换器,直接简单的忽略了它的绑定关键字的值。

Direct交换器

前一篇教程中的日志系统广播所有的消息到所有的消费者,我们希望扩展它能根据严重程度对消息进行过滤。例如,我们希望写日志到磁盘的程序能够只接收重要的错误,不要在“警告”、“信息”级别的日志身上浪费磁盘空间。

我们使用fanout类型的交换器,并不能带来很大的灵活性,它只能盲目的广播。作为替代,我们将使用direct类型的交换器,这种交换器背后的路由算法非常简单,消息发送到绑定关键字(binding Key)和消息的路由关键字(routing Key)完全匹配的队列。

为了帮助说明,设想下面的配置:

                                         

在这个设置中,我们看到direct类型的交换器X绑定了两个队列.第一个队列绑定关键字是orange;第二个队列有两个绑定,一个是black,另一个是green。在这样的配置中,发送到使用orange为路由关键字的消息将被路由到Q1,使用blackgreen为路由关键字的消息将被路由到队列Q2,其他消息将被销毁。

多绑定

                                         

使用同一个绑定关键字绑定多个队列是完全合法的,在我们的示例中我们可以在XQ1中添加一个以black为绑定关键字的绑定。在这种情况下,direct类型的交换器会展现出fanout类型交换器的行为,并且会广播消息到所有满足条件的队列,发送到路由关键字为black的消息会发送到Q1Q2.

发送日志

我们会为日志系统使用这种模式,将消息发送到direct类型而非fanout类型的交换器。我们将使用日志的严重等级来作为路由关键字,这样的话消息的接收程序就可以依据自己的需要选择不同的严重等级。首先让我们把注意力集中到发送日志上。

和往常一样,我们首先需要创建一个交换器:

1 channel.ExchangeDeclare(exchange: "direct_logs", type: "direct");

接着我们准备好发送消息了:

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

为了简化问题,我们假设严重等级是‘info’、‘warning’、‘error’。

订阅

马上就可以像前面的教程中那样接收消息了,但是有一个不同点,我们要用自己感兴趣的所有日志严重程度分别创建一个绑定。

1 var queueName = channel.QueueDeclare().QueueName;
2 
3 foreach(var severity in args)
4 {
5     channel.QueueBind(queue: queueName,
6                       exchange: "direct_logs",
7                       routingKey: severity);
8 } 

组合在一起

                                       

EmitLogDirect.cs类的代码:

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

ReceiveLogsDirect.cs的代码:

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

像通常那样编译(编译建议见教程一)。

如果你只想保存严重等级为‘warning’、‘error’的日志到文件中,只需要打开控制台然后输入:

1 $ ReceiveLogsDirect.exe warning error > logs_from_rabbit.log

如果你想在屏幕上看到所有的日志,打开另一个终端输入:

1 $ ReceiveLogsDirect.exe info warning error
2  [*] Waiting for logs. To exit press CTRL+C

如果想发送‘error’级别的日志,举例来说,你只需要输入如下信息:

1 $ EmitLogDirect.exe error "Run. Run. Or it will explode."
2  [x] Sent 'error':'Run. Run. Or it will explode.' 

进入教程五,了解如何基于模式(pattern)监听消息。

 

原文链接:http://www.rabbitmq.com/tutorials/tutorial-four-dotnet.html

posted on 2015-11-13 17:31  hptony  阅读(2930)  评论(2编辑  收藏  举报