From: http://lostechies.com/derekgreer/2012/04/02/rabbitmq-for-windows-direct-exchanges/

RabbitMQ for Windows: Direct Exchanges

Posted by Derek Greer on April 2, 2012

This is the fifth installment to the series: RabbitMQ for Windows.  In thelast installment, we took a look at the four exchange types provided by RabbitMQ: Direct, Fanout, Topic, and Headers.  In this installment we’ll walk through an example which uses a direct exchange type directly and we’ll take a look at the push API.

In the Hello World example from the second installment of the series, we used a direct exchange type implicitly by taking advantage of the automatic  binding of queues to the default exchange using the queue name as the routing key.  The example we’ll work through this time will be similar, but we’ll declare and bind to the exchange explicitly.

This time our example will be a distributed logging application.  We’ll create a Producer console application which publishes a logging message for some noteworthy action and a Consumer console application which displays the message to the console.

PunCha:作者之前的5个帖子,很简单,看的我是行云流水~,可是看到这个帖子,让我一头雾水从而研究了2个小时,这才有了RabbitMQ学习篇一。我并不觉得这篇文章的例子是个好例子!作者的意图很明显,因为DirectExchange太简单了,没有深度,所以他参差了一个BasicConsumer函数(Block调用),但是却把简单问题复杂化了。而且混淆了一些概念,我这里作一下说明:

1. Exchange只用于Producer,RoutingKey用来绑定Exchange和Queue,这个一般在Producer这端做的,但是作者却放到了Consumer这端。可以是可以,但是作者应该加一个说明。

2. 作者这个用法说明了一个问题,假如Producer把消息发到没有绑定Queue的Exchange消息会丢失!

3. 作者让我对RMQ有了深刻的了解,嘿嘿。

Beginning with our Producer app, we’ll start by establishing a connection using the default settings, create the connection, and create a channel:

  1. using RabbitMQ.Client;
  2.  
  3. namespace Producer
  4. {
  5.   class Program
  6.   {
  7.     static void Main(string[] args)
  8.     {
  9.       var connectionFactory = new ConnectionFactory();
  10.       IConnection connection = connectionFactory.CreateConnection();
  11.       IModel channel = connection.CreateModel();
  12.     }
  13.   }
  14. }

Next, we need to declare the exchange we’ll be publishing our message to.  We need to give our exchange a name in order to reference it later, so let’s use “direct-exchange-example”:

  1. channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);

The second parameter indicates the exchange type.  For the official RabbitMQ .Net client, this is just a simple string containing one of the values: direct, fanout, topic, or headers.  The type RabbitMQ.Client.ExchangeType defines each of the exchange types as a constant for convenience. [PunCha:这里为什么不用一个枚举型?没搞懂为什么要这样设计]

Next, let’s call some method which might produce a value worthy of interest.  We’ll call the method DoSomethingInteresting() and have it return a string value:

  1. string value = DoSomethingInteresting();

For the return value, the implementation of DoSomethingInteresting() can just return the string value of a new Guid:

  1. static string DoSomethingInteresting()
  2. {
  3.   return Guid.NewGuid().ToString();
  4. }

Next, let’s use the returned value to create a log message containing a severity level of Information:

  1. string logMessage = string.Format("{0}: {1}", TraceEventType.Information, value);

Next, we need to convert our log message to a byte array and publish the message to our new exchange:

byte[] message = Encoding.UTF8.GetBytes(logMessage); channel.BasicPublish("direct-exchange-example", "", null, message);

Here, we use an empty string as our routing key [PunCha: 所以,假如想成功发送消息,必然有一个名叫direct-exchange-example的Exchange对象和一个空的RoutingKey用来绑定某个Queue。In this case,是在Consumer这端实现的,这也是为什么Consumer一定要先运行的原因。] and null for our message properties.

We end our Producer by closing the channel and connection:

  1. channel.Close();
  2. connection.Close();

Here’s the full listing:

  1. using System;
  2. using System.Diagnostics;
  3. using System.Text;
  4. using System.Threading;
  5. using RabbitMQ.Client;
  6.  
  7. namespace Producer
  8. {
  9.   class Program
  10.   {
  11.     static void Main(string[] args)
  12.     {
  13.       Thread.Sleep(1000);
  14.       var connectionFactory = new ConnectionFactory();
  15.       IConnection connection = connectionFactory.CreateConnection();
  16.       IModel channel = connection.CreateModel();
  17.  
  18.       channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
  19.       string value = DoSomethingInteresting();
  20.       string logMessage = string.Format("{0}: {1}", TraceEventType.Information, value);
  21.  
  22.       byte[] message = Encoding.UTF8.GetBytes(logMessage);
  23.       channel.BasicPublish("direct-exchange-example", "", null, message);
  24.  
  25.       channel.Close();
  26.       connection.Close();
  27.     }
  28.  
  29.     static string DoSomethingInteresting()
  30.     {
  31.       return Guid.NewGuid().ToString();
  32.     }
  33.   }
  34. }

Note that our logging example’s Producer differs from our Hello World’s Producer in that we didn’t declare a queue this time.[PunCha: 特别注意,我们没有声明Queue! 这个不同于HelloWorld。]  In our Hello World example, we needed to run our Producer before the Consumer since the Consumer simply retrieved a single message and exited.  Had we published to the default exchange without declaring the queue first, our message would simply have been discarded by the server before the Consumer had an opportunity to declare and bind the queue.

Next, we’ll create our Consumer which starts the same way as our Producer code:

  1. using RabbitMQ.Client;
  2.  
  3. namespace Consumer
  4. {
  5.   class Program
  6.   {
  7.     static void Main(string[] args)
  8.     {
  9.       var connectionFactory = new ConnectionFactory();
  10.       IConnection connection = connectionFactory.CreateConnection();
  11.       IModel channel = connection.CreateModel();
  12.  
  13.       channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
  14.     }
  15.   }
  16. }

Next, we need to declare a queue to bind to our exchange.  Let’s name our queue “logs”:

  1.  
  2. channel.QueueDeclare("logs", false, false, true, null);

To associate our logs queue with our exchange, we use the QueueBind() method providing the name of the queue, the name of the exchange, and the binding key to filter messages on: [PunCha:这里就产生了一个绑定,Exchange是direct-exchange-example,Queue是logs,纽带是空的RoutingKey。这个与Producer代码相匹配。]

  1. channel.QueueBind("logs", "direct-exchange-example", "");

At this point we could consume messages using the pull API method BasicGet() as we did in the Hello World example, but this time we’ll use the push API.  To have messages pushed to us rather than us pulling messages, we first need to declare a consumer:

  1. var consumer = new QueueingBasicConsumer(channel);

To start pushing messages to our consumer, we call the channel’s BasicConsume() method and tell it which consumer to start pushing messages to: [PunCha:注意,取数据永远只需要Queue,没有Exchange和RoutingKey。]

  1. channel.BasicConsume(“logs”, true, consumer);

Here, we specify the queue to consume messages from, a boolean flag instructing messages to be auto-acknowledged (see discussion in the Getting the Message section of Hello World Review), and the consumer to push the messages to.

Now, any messages placed on the queue will automatically be retrieved and placed in a local in-memory queue.  To dequeue a message from the local queue, we call the Dequeue() method on the consumer’s Queue property:

  1. var eventArgs = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

This method call blocks until a message is available to be dequeued, or until an EndOfStreamException is thrown indicating that the consumer was cancelled, the channel was closed, or the connection otherwise was terminated.[PunCha:说实话,我的感觉,无论是返回参数(需要强转),还是这个函数的取名,都很奇怪。。Dequeue的时候Block。。。]

Once the Dequeue() method returns, the BasicDeliverEventArgs contains the bytes published from the Producer in the Body property, so we can convert this value back into a string and print it to the console:

  1. var message = Encoding.UTF8.GetString(eventArgs.Body);
  2. Console.WriteLine(message);

We end our Consumer by closing the channel and connection:

  1. channel.Close();
  2. connection.Close();

Here’s the full listing:

  1. using System;
  2. using System.Text;
  3. using RabbitMQ.Client;
  4. using RabbitMQ.Client.Events;
  5.  
  6. namespace Consumer
  7. {
  8.   class Program
  9.   {
  10.     static void Main(string[] args)
  11.     {
  12.       var connectionFactory = new ConnectionFactory();
  13.       IConnection connection = connectionFactory.CreateConnection();
  14.       IModel channel = connection.CreateModel();
  15.  
  16.       channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
  17.       channel.QueueDeclare("logs", false, false, true, null);
  18.       channel.QueueBind("logs", "direct-exchange-example", "");
  19.  
  20.       var consumer = new QueueingBasicConsumer(channel);
  21.       channel.BasicConsume("logs", true, consumer);
  22.  
  23.       var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();
  24.  
  25.       string message = Encoding.UTF8.GetString(eventArgs.Body);
  26.       Console.WriteLine(message);
  27.  
  28.       channel.Close();
  29.       connection.Close();
  30.       Console.ReadLine();
  31.     }
  32.   }
  33. }

If we run the resulting Consumer.exe at this point, it will block until a message is routed to the queue.  Running the Producer.exe from another shell produces a message on the consumer console similar to the following:

  1. Information: 610fe447-bf31-41d2-ae29-414b2d00087b
Note: For a convenient way to execute both the Consumer and Producer from within Visual Studio, go to the solution properties and choose “Set StartUp Projects …”.  Select the “Multiple startup projects:”[PunCha:这个功能蛮好的,我以前竟然不知道~] option and set both the Consumer and Producer to the Action: Start.  Use the arrows to the right of the projects to ensure the Consumer is started before the Producer.  In some cases, this can still result in the Producer publishing the message before the Consumer has time to declare and bind the queue, so putting a Thread.Sleep(1000) at the start of your Producer should ensure things happen in the required order.  After this, you can run your examples by using Ctrl+F5 (which automatically prompts to exit).

That concludes our direct exchange example.  Next time, we’ll take a look at the Fanout exchange type.

http://blog.csdn.net/puncha/article/details/8449395

RabbitMQ学习之:(六)Direct Exchange (转贴+我的评论)的更多相关文章

  1. rabbitmq学习(六) —— 主题

    主题交换(Topic exchange) 使用 topic 类型的交换器,不能有任意的绑定键,它必须是由点隔开的一系列的标识符组成.标识符可以是任何东西,但通常它们指定与消息相关联的一些功能.其中,有 ...

  2. RabbitMQ学习笔记六:RabbitMQ之消息确认

    使用消息队列,必须要考虑的问题就是生产者消息发送失败和消费者消息处理失败,这两种情况怎么处理. 生产者发送消息,成功,则确认消息发送成功;失败,则返回消息发送失败信息,再做处理. 消费者处理消息,成功 ...

  3. rabbitMQ学习(六)

    请求模式 客户端: import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.Connection; impor ...

  4. rabbitMQ学习笔记(六) topic类型消息。

    上一节中使用了消息路由,消费者可以选择性的接收消息. 但是这样还是不够灵活. 比如某个消费者要订阅娱乐新闻消息 . 包括新浪.网易.腾讯的娱乐新闻.那么消费者就需要绑定三次,分别绑定这三个网站的消息类 ...

  5. RabbitMQ学习总结 第六篇:Topic类型的exchange

    目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...

  6. RabbitMQ学习系列(四): 几种Exchange 模式

    上一篇,讲了RabbitMQ的具体用法,可以看看这篇文章:RabbitMQ学习系列(三): C# 如何使用 RabbitMQ.今天说些理论的东西,Exchange 的几种模式. AMQP协议中的核心思 ...

  7. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  8. PHP 下基于 php-amqp 扩展的 RabbitMQ 简单用例 (一) -- 安装 AMQP 扩展和 Direct Exchange 模式

    Windows 安装 amqp 扩展 RabbitMQ 是基于 amqp(高级消息队列协议) 协议的.使用 RabbitMQ 前必须为 PHP 安装相应的 amqp 扩展. 下载相应版本的 amqp ...

  9. RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

    在上一章中,我们构建了一个简单的日志系统,我们可以把消息广播给很多的消费者.在本章中我们将增加一个特性:我们可以订阅这些信息中的一些信息.例如,我们希望只将error级别的错误存储到硬盘中,同时可以将 ...

随机推荐

  1. 手动编译用于i.MX6系列ARM的交叉编译SDK

    前言: 在前一节中,在使用别的机器(系统:UBUNTU14.04)上编译好的交叉编译SDK,配置在我的电脑(系统:UBUNTU16.04)上,用于bazel编译Tensorflow时会报arm-pok ...

  2. CentOS 6.10 系统安装

    本章内容: CentOS 6.10 的安装 一.安装光盘,选择 Install or upgrade an existing system 二.选择 skip 跳过光盘检查 三.选择 Next 四.选 ...

  3. 2.01_Python网络爬虫概述

    一:什么是网络爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取网络信息的程序或者脚本: 二:为什么要做网络爬虫? 大数据时代 ...

  4. windows下虚拟python环境

    Windows虚拟环境 cd %HOMEDRIVE%%HOMEPATH%\Desktop python3  -m  venv venv 环境变量修改脚本bat,把脚本放到%HOMEDRIVE%%HOM ...

  5. Pc贪吃蛇

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. LoadRunner(5)

    一.在线综合场景测试:号称能更真实模拟实际生产环境 又称为:混合交易测试 (交易就是事务 Transaction) 1.三要素: 1)多用户:根据需求指定VU数 压力的来源 2)多任务:根据需求结合多 ...

  7. python_函数参数

    1.参数的基本知识 任意个数 任意类型 def func(a1,a2,a3): print(a1,a2,a3) # 参数可以是任意个数和任意类型 func(1,'waf',True) 2.位置传参数( ...

  8. 前端知识体系:JavaScript基础-原型和原型链-实现继承的几种方式以及他们的优缺点

    实现继承的几种方式以及他们的优缺点(参考文档1.参考文档2.参考文档3) 要搞懂JS继承,我们首先要理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链:每一 ...

  9. 第二个爬虫之爬取知乎用户回答和文章并将所有内容保存到txt文件中

    自从这两天开始学爬虫,就一直想做个爬虫爬知乎.于是就开始动手了. 知乎用户动态采取的是动态加载的方式,也就是先加载一部分的动态,要一直滑道底才会加载另一部分的动态.要爬取全部的动态,就得先获取全部的u ...

  10. axios请求提交的form data格式 明明是JSON格式的参数却转成了字符串格式

    问题:传的参数成为了字符数格式 解决:把参数的格式转换 const params = new URLSearchParams() params.append('USER_LOGIN', 'admin' ...