在上一节中我们创建了一个工作队列,最好的情况是工作队列能够把任务恰到好处的分配给每一个worker。这一节中我们将做一些完全不同的事情——将消息传递给每一个消费者,这种模式被称为发布/订阅。

为了说明这种模式,我们构建一个日志系统,其包括两个部分,一个发出日志消息,另一个接收并打印出来。

在我们的日志系统里,每一个拷贝的接收者都将收到消息,这样我们就能够跑一个接收者将收到的日志消息写入磁盘,并在同时另外一个接收者将日志消息打印在屏幕上。

本质上来说,发布日志消息将要被广播到所有的接收器。

交换器(Exchanges)

在本教程之前我们在队列中发送和接收消息,现在我们将介绍Rabbit中完整的消息传递模式。

我们先回顾一下前面的教程:

生产者发送消息

队列是一个缓冲区存储消息

消费者接收消息

RabbitMQ消息模式的核心是生产者从来不会直接发送一个消息给队列,事实上,发送者都不知道消息将要被发送到一个队列。

相反,生产者发送消息到exchange,exchange是一个非常简单的东西,它一方面接收生产者的消息,另一方面推送消息到队列,exchange必须知道如何处理它接收到的消息,是应该将消息附加至一个特定的队列?还是附加到很多队列?或者而应该丢弃?这些规则由exchange来决定。

![](http://images2015.cnblogs.com/blog/658141/201608/658141-20160818001628671-592532767.png)

exchange包含以下几种类型:direct, topic, headers 和 fanout。我们将重点放在最后一个——fanout,我们传建一个这种类型的exchange,命名为logs:

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

fanout类型的exchange非常简单,我们可以从名字就猜出来,它只是广播接收到的所有消息至所有的队列。而这正是我们的日志系统所需要的。

列出exchange

我们在服务器上将所有的exchange列出来

$ sudo rabbitmqctl list_exchanges

Listing exchanges ...

direct

amq.direct direct

amq.fanout fanout

amq.headers headers

amq.match headers

amq.rabbitmq.log topic

amq.rabbitmq.trace topic

amq.topic topic

logs fanout

...done.

在这些list里面我们可看到一些amq.* exchanges,这些是默认的exchange。

无名的exchange

在上一节中,我们对于exchange什么都不知道,但是我们依旧可以发送消息至队列,这是因为我们使用了使用了默认的exchange,它被定义为一个空字符串“”。

channel.basicPublish("", "hello", null, message.getBytes());

第一个参数就是exchange,空字符串表示默认或者无名交换。如果routingkey存在的话,消息会根据routingkey被路由到队列。

现在我们发布到有名的exchange:

channel.basicPublish( "logs", "", null, message.getBytes());

临时队列(Temporary queues)

我们在之前的教程中给队列起了名字(helll和task_queue),给一个队列命名对于我们来说是很重要的,我们需要消费者指向同一个队列。

但是这不是我们目前的日志程序所需要的,我们希望能接受所有的消息,而不是这些消息的子集。当然我们也只是关注当前流动的消息,而不需要关注那些过期的消息。这样,我们需要做两件事情。

第一,无论何时我们连接Rabbit,我们需要一个新的空的队列,这样需要用随机的名字来创建一个队列,更好地,我们让服务器来随机的生成一个队列名字。

第二,我们一旦和消费者断开,队列必须马上删除。

在Java Client中,我们不提供参数给queueDeclare() 方法,就能生成一个非持久,独特,自动删除队列的名称:

String queueName = channel.queueDeclare().getQueue();

这样队列拥有一个随机的队列名称,比如:amq.gen-JzTY20BRgKO-HjmUJj0wLg。

绑定(Bindings)

现在我们已经建立了一个exchange和一个队列,现在我们需要告诉exchange将消息发送到我们的队列中,exchange和队列之间的关系我们成为Binding。

![](http://images2015.cnblogs.com/blog/658141/201608/658141-20160818001716265-574824828.png)
channel.queueBind(queueName, "logs", "");

从现在开始日志程序中的exchange就可以将消息附加到队列上了。

列出binding的列表

你可以列出现有的Binding列表,命令是:rabbitmqctl list_bindings.

代码整合

生产者和之前的教程中相差不多,最大的改变是我们现在需要将消息发布到logs exchange,而不是之前的未命名exchange,当发送消息的时候我们需要提供一个routingKey,但是在fanout的消息模式中被忽略了。

![](http://images2015.cnblogs.com/blog/658141/201608/658141-20160818001729640-1228087741.png)

EmitLog.java:

import java.io.IOException;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel; public class EmitLog { private static final String EXCHANGE_NAME = "logs"; public static void main(String[] argv)
throws java.io.IOException { ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'"); channel.close();
connection.close();
}
//...
}

建立连接后,我们定义了一个exchange,因为发布到一个不存在的exchange是被禁止的。

如果没有队列绑定到exchange,信息会丢失,但是也没关系。如果没有消费者在监听,我们可以安全的丢弃消息。

ReceiveLogs.java:

import com.rabbitmq.client.*;

import java.io.IOException;

public class ReceiveLogs {
private static final String EXCHANGE_NAME = "logs"; public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, ""); System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}

原文地址:https://www.rabbitmq.com/tutorials/tutorial-three-java.html

代码地址:https://github.com/aheizi/hi-mq

相关:

1.RabbitMQ之HelloWorld

2.RabbitMQ之任务队列

3.RabbitMQ之发布订阅

4.RabbitMQ之路由(Routing)

5.RabbitMQ之主题(Topic)

6.RabbitMQ之远程过程调用(RPC)

RabbitMQ之发布订阅【译】的更多相关文章

  1. RabbitMQ的发布订阅模式(Publish/Subscribe)

    一.发布/订阅(Publish/Subscribe)模式 发布订阅是我们经常会用到的一种模式,生产者生产消息后,所有订阅者都可以收到.RabbitMQ的发布/订阅模型图如下: 1.该模式下生产者并不是 ...

  2. 【译】RabbitMQ:发布-订阅(Publish/Subscribe)

    在前一篇教程中,我们创建了一个工作队列,我们假设在工作队列后的每一个任务都只被调度给一个消费者.在这一部分,我们将做一些完全不一样的事情,调度同一条消息给多个消费者,也就是有名的“发布-订阅”模式.为 ...

  3. RabbitMQ之发布订阅

    工作队列中,每个任务之分发给一个工作者.如果需要分发一个消息给多个消费者,这种模式被称为“发布/订阅” 交换器(Exchanges) RabbitMQ完整的消息模型 发布者(producer)是发布消 ...

  4. RabbitMQ入门-发布订阅模式

    兔子的Publish/Subscribe是这样的: 有个生产者P,X代表交换机,交换机绑定队列,消费者从队列中取得消息.每次有消息,先发到交换机中,然后由交换机负责发送到它已知的队列中. 生产者代码: ...

  5. 四.RabbitMQ之发布/订阅(Publish/Subscribe)

    一.基础知识点 在上述章节中,我们理解的RabbitMQ是基于如下这种模式运作的. 而事实上,这只是我们简单化了的模型的结果,真正的模型应该是这样的. P:Producer 生产者,生产消息,把它放进 ...

  6. rabbitmq (三) 发布/订阅

    rabbitmq的目的并不是让生产者把消息直接发到队列里面去, 这样不能实现解耦的目的,也不利于程序的扩展. 所以就有交换机(exchanges)的概念. 交换机有几种类型:direct, topic ...

  7. RabbitMQ 发布订阅

    互联网公司对消息队列是深度使用者,因此需要我们了解消息队列的方方面面,良好的设计及深入的理解,更有利于我们对消息队列的规划. 当前我们使用消息队列中发现一些问题: 1.实际上是异步无返回远程调用,由发 ...

  8. RabbitMQ入门_08_所谓的点对点与发布订阅模型

    A. JMS 模型 JMS 中定义了点对点和发布订阅两种消息模型,原来以为 AMQP 协议中 direct Exchange 对应点对点模型,topic Exchange 对应发布订阅模型,fanou ...

  9. RabbitMQ之任务队列【译】

    在第一个教程里面,我们写了一个程序从一个有名字的队列中发送和接收消息,在这里我们将要创建一个分发耗时任务给多个worker的任务队列. 任务队列核心思想就是避免执行一个资源密集型的任务,而程序要等待其 ...

随机推荐

  1. 微信公众帐号开发教程第4篇-----开发模式启用及接口配置Java

    欢迎加入群:347245650   345531810 进行讨论相互交流  我的微信号:572839485 我的微信公众账号  我的微社区欢迎关注 索取源码←请点击 图床:没有服务器 拖拽图片 外网即 ...

  2. MSCRM 2011 JavaScript 开发文档

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvem91eXVqaWUxMTI3/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  3. CentOS 6.5下二进制安装 MySQL 5.6

    CentOS 6.5 二进制安装MySQL 5.6 1:查看系统版本 [root@10-4-5-9 mysql]# cat /etc/redhat-release CentOS release 6.5 ...

  4. UML回想-通信图

        我们对软件project这一大块的学习事实上開始的还是挺早的,而且在后来的学习过程中也不断的涉及到了这些知识. 可是,经过软考的检验来看我对软工这一块的内容掌握的实在是慘不忍睹.基本上就是一出 ...

  5. OE context 传参数

    来自:http://shine-it.net/index.php/topic,16360.0.html 有个需求想many2one字段关联显示的value在各个模块显示不同的值. 如果直接该rel_n ...

  6. SlidingMenu(一)

    我们一般称之为侧边栏,今天下倒腾了一下,留点笔记... 源码来自:https://github.com/jfeinstein10/SlidingMenu 来张图把: 代码API注释看看这个吧 http ...

  7. Apache Ant和Apache Maven的区别

    Apache Ant和Apache Maven的区别 分类: ANT Maven 2013-12-10 18:47 1477人阅读 评论(26) 收藏 举报 ———摘自<maven权威指南> ...

  8. unity, RT .DiscardContents ()导致android上RT不显示

    RT .DiscardContents ()可以消除pc上的warning,但是会导致android上RT不显示.

  9. 【Android】15.5 例15-3—Notification的各种属性演示

    分类:C#.Android.VS2015: 创建日期:2016-02-29 一.简介 利用这个例子,可测试通知的各种属性以及这些不同属性选项呈现的效果. 另外,在这个例子中,还演示了如何读写SD中的图 ...

  10. 【Android】14.2 外部文件存储和读取

    分类:C#.Android.VS2015: 创建日期:2016-02-27 一.简介 1.基本概念 内部存储的私有可用存储空间一般都不会很大,对于容量比较大的文件,例如视频等,应该将其存储在外部存储设 ...