发布/订阅

在之前的案例中我们创建了一个工作队列,这个工作队列的实现思想就是一个把每一个任务平均分配给每一个执行者,在这个篇文章我们会做一些不一样的东西,把一个消息发送给多个消费者,这种模式就被称作"发布/订阅".

为了说明这个模式,我们将要创建一个简单的日志系统,一个负责发布消息,另外一个负责接收打印他们.

在我们的日志系统中,每一个运行中的接收者副本将都会获得消息,这种方式可以让我们在运行一个接收者直接把消息保存在磁盘的同时,另外一个消费者可以把消息打印到屏幕上.

本质上,发布一个日志消息将会广播给所有的接收者

交换机(Exchanges)

在之前的文章中,我们接受和发送消息都是通过一个队列来完成了,现在是时候引入RabbitMQ的全部工作模型了.

让我们快速回忆一下之前涉及到的模型

--生产者(发布者),是一个负责发送消息的用户应用程序.

--队列,负责存储消息

--消费者(接收者),负责接收消息的用户程序.

RabbitMQ的核心思想是生产者永远不会直接把消息发送给队列,事实上生产者甚至经常不知道一个发出去的消息是否可以有队列去接收它.

相应的,生产者只能消息发送给交换机,交换机的工作机制非常简单,一方面它从生产者那里接收到消息,另一方面它会把消息发送给相应的队列上.交换机必须要知道怎么处理接收到的消息,它应该被放入一个特殊的队列吗?它是否应该被放入多个队列?或者它是否需要被忽略.

处理这工作的方式是通过交换机类型来实现的.

这里有几个可用的交换机类型:direct,topic,headers,fanout 我们将会关注最后一个(fanout),让我们创建一个fanout的交换机,名字叫做'logs'

channel.ExchangeDeclare("logs", "fanout");

这个fanout的交换机功能非常简单(你也许已经从名字中猜到了他的方式),把接收到的消息广播给所有已知的队列,这个这是我们的日志系统需要的.

列出RabbitMQ已添加的交换机:

cmd:rabbitmqctl list_exchanges

无命名的交换机:在之前的案例中我们对于交换机一无所知,但是仍然可以把消息发送到队列上,这是因为我们使用的是一个默认的交互机,名字为空(""),回顾一下我们之前发送消息的方式

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

第一个参数就是交换机的名称,空字符串表示默认的无命名的交换机:消息通过存在的RoutingKey被发送到队列上.

现在我们发送命名的交换机代替:

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

临时队列

在之前的案例中,我们使用的队列是一个指定了名字的队列(记得hello 和task_queue 吗),给一个队名命名是严格的,我们需要执行者连接的同样的队列来工作,当你想在生产者和消费者之间共享队列的时候指定一个队列名是非常重要的.但是我们的日志系统则不在此列,

我们想要监听到所有的日志消息,而不仅仅是他们的子集,我们也仅仅对当前正在流转的消息感兴趣,而不是老的消息,结局这个问题我们需要2件事情.

首先,无论何时我们连接到队列,我们都需要一个新鲜的,空的队列,为了实现这个目标我们可以每次创建一个随机名称的队列,或者更加便捷的方式--让服务为我们的队列随机命名.

第二,一旦我们断开到消费者到队列的连接,我们需要自动删除队列.

在.Net客户端,我们使用无参的queueDeclare()方法来创建一个随机命名的非持久的,自动删除的排他队列.

var queueName = channel.QueueDeclare().QueueName;

queueName就是一个随机的队列名,如:amq.gen-JzTY20BRgKO-HjmUJj0wLg.

绑定

我们已经创建了一个fanout的交换机和一个队列,现在我们需要告诉我们交换机发送消息到我们的队列,交换机和队列之间的关系叫做绑定.

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

从现在开始logs 交换机将会把消息放入我们的队列当中.

列出队列cmd: rabbitmqctl list_bindings

汇总

负责发送消息的生产者可之前案例基本上是一样的,最大的不同是我们将消息发送到了我们的命名队列logs上而不是默认的队列上,发送的时候我们需要使用routingKey,但是它的值是被fanout交换机忽略的.

EmitLog.cs

class EmitLog
{
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: "logs", type: "fanout"); var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "logs", routingKey: "", basicProperties: null, body: body);
Console.WriteLine(" [x] Sent {0}", message);
}
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
} private static string GetMessage(string[] args)
{
return ((args.Length > )
? string.Join(" ", args)
: "info: Hello World!");
}
}

正如你看到的,我们在建立连接之后创建了一个队列,这一步是必须的,因为发送到一个不存在的交换机是不被允许的。

当队列还没有绑定到交换机是发送的消息将会丢失,但是这对我们日志系统来说没有问题,当没有消费者监听时我们可以安全的忽略这个消息。

ReceiveLogs.cs:

class ReceiveLogs
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "logs", type: "fanout"); var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,exchange: "logs",routingKey: ""); Console.WriteLine(" [*] Waiting for logs."); var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message);
};
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}

同时运行两个receive,可以看到两个接收端可以同时接收到一个消息。

RabbitMQ 原文译03--发布和订阅的更多相关文章

  1. Part1.2 、RabbitMQ -- Publish/Subscribe 【发布和订阅】

    python 目录 (一).交换 (Exchanges) -- 1.1 武sir 经典 Exchanges 案例展示. (二).临时队列( Temporary queues ) (三).绑定(Bind ...

  2. RabbitMQ 原文译1.2--"Hello Word"

    本系列文章均来自官网原文,属于个人翻译,如有雷同,权当个人归档,忽喷. .NET/C# RabbitMQ 客户端下载地址:https://github.com/rabbitmq/rabbitmq-do ...

  3. RabbitMQ 原文译1.1--HelloWord

    本系列文章均来自官网原文,属于个人翻译,如有雷同,权当个人归档,忽喷. RabitMQ 是一个消息中间件,其实就是从消息生产者那里接受消息,然后发送给消息消费者.在这个传输过程中,可以定义一些缓存,持 ...

  4. RabbitMQ 原文译06--Remote procedure call(RPC)

    在第三篇文章中, 我们学习了怎么使用队列在多了消息消费者当中进行耗时任务轮询. 但是如果我们想要在远程电脑上运行一个方法,然后等待其执行结果,这就是一个不同的场景,这种就是我们一般讲的RPC(远程过程 ...

  5. RabbitMQ 原文译05--Topics

    在之前的系统中,我们改进了我们的日志系统,我们使用direct 交换机代替fanout交换机,可以实现选择性的接受日志. 虽然使用direct 交换机改进了我们的系统,但是对于多种条件的判断,依然存在 ...

  6. RabbitMQ 原文译04--路由

    在前一篇文章中我们构建了一个简单的日志系统,我们可以向多个接受者广播消息. 在这篇文章我,我们将要添加一些功能使得针对部分消息的接受成为可能,例如我们只对错误的消息进行磁盘记录,同时又可以把所有的消息 ...

  7. RabbitMQ 原文译02--工作队列

    工作队列: 在上一篇文章中我们我们创建程序发送和接受命名队列中的消息,在这篇文章我会创建一个工作队列,用来把耗时的操作分配给多个执行者. 工作队列(任务队列)的主要实现思想是避免马上执行资源密集型的任 ...

  8. 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ

    本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...

  9. Python-RabbitMQ消息队列的发布与订阅

    RabbitMQ消息队列的发布与订阅类似于广播,一端发送消息,多个客户端可以同时接收到消息 fanout:所有绑定到exchange的queue都可以接收消息 消息发布端 # -*- coding:u ...

随机推荐

  1. VMware 克隆虚拟机或加载新的已安装虚拟机时System eth0不能使用的解决方法

    近年来的大数据应用特别热,特别是Hadoop和Spark.但大家使用这些分布式文件系统和计算框架都需要一个分布式的集群环境,而大家手头一般没有多余的机器部署master和多个slave节点,就只能在V ...

  2. EventBus使用小记

    自从使用了EventBus,代码干净了好多. 从此你不用startActivityForResult了,从此你不用再写注册BroadcastReceiver了,从此你不用再写一些回调了. 只需要在需要 ...

  3. hdoj 2032 杨辉三角

    杨辉三角 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  4. linux中文设置

    近期出现用户发邮件乱码问题,前面服务器刚刚切换过来,忘了装中文字体了. 本地调试在windows环境下,所以每次看都好的,于是我看了下linux服务器的编码,果然默认的不是中文字体,所以在生成pdf的 ...

  5. Struts2中的session、request、respsonse获取方法

    个人对于struts有一种复杂的心情,平心而论,struts2是个人最早接触到的的框架,在学校的时候就已经开始学习了,大四毕业设计,无疑用的还是struct,那时候SSH还是很流行的,后来出来实习,直 ...

  6. Using breakpad in cocos2d-x 3.2,dump信息收集

    作者:HU 转载请注明,原文链接:http://www.cnblogs.com/xioapingguo/p/4037268.html 一.基本步骤 1.生成转换工具 2.把breakpad加入到项目 ...

  7. struts中的helloword(1)

    注:文章中的所有图片均在附件中明白表明   首先要安装jdk1.6以及tomcat6和myeclipse 对于这些配置的安装 这里不再细细说明  其次是下载struts2  第一步:去struts21 ...

  8. 【55】让自己熟悉Boost

    1.网址:http://boost.org 2.有很多C++组织和网站,但是Boost库有两个优势:a.和标准委员会关系密切:b.加入C++标准的各种功能的测试场.

  9. C#-datagridview设置列宽

    在使用datagridview的显示数据的过程中,常常会遇到需要设定datagridview的列宽,这就需要用到datagridview的属性: autosizemode

  10. 日志文件C++ 时间 文件 行数

    #include <stdio.h> #include<windows.h> #include <time.h> #define Line __LINE__ #de ...