原文:http://www.cnblogs.com/tinmh/p/6134875.html

发布/订阅模式即生产者将消息发送给多个消费者。

下面介绍几个在发布/订阅模式中的关键概念--

1. Exchanges (转发器)

可能原来我们都是基于一个队列发送和接收消息。现在介绍一下完整的消息传递模式。

Rabbitmq消息模式的核心理念是:生产者没有直接发送任何消息到队列。实际上,生产者都不知道这个消息是发送给哪个队列的。相反,生产者只能发送消息给转发器。

转发器一方面接收生产者的消息,另一方面向队列推送消息。

转发器必须清楚的指导如何处理接收到的消息,需要附加队列吗?附加几个?或者是否丢弃?这些规则通过转发器的类型进行定义。类型有:Direct、Topic、Headers、Fanout。

这里我们关注最后一个。现在让我们创建一个Fanout类型的转发器,定义如下:

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

2. Nameless exchange(匿名转发)

之前我们对转发器可能一无所知,但还是可以将消息发送到队列,那是因为我们用了默认的转发器,转发器名为空字符串" "。之前我们发布消息的代码是:

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

第一个参数就是转发器的名字。空字符串表示匿名的转发器。消息通过队列的routingKey路由到指定的队列中去。

现在我们就可以指定转发器的名字了;

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

3.Temporary queues(临时队列)

当我们需要为消费者指定同一个队列的时候,队列有名字对我们来说是非常重要的。

但有时我们并不关心这个问题,我们只对当前流动的消息感兴趣。这个时候我们采取以下两个步骤解决:

1)当我们连接到RabbitMQ时,需要一个新的空队列,为此我们需要创建一个随机名字的空队列,或者更好的。让服务器选好一个随机名字的空队列直接给我们。

2)一旦消费者断开连接,队列将自动删除。

这里我们提供一个无参的queueDeclare()方法,创建一个非持久化、独立的、自动删除的队列,且名字是随机生成的。

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

queueName是一个随机队列名。

4.Bindings(绑定)

我们已经创建了一个广播的转发器和一个随机队列。现在需要告诉转发器转发消息到队列。这个关联转发器和队列我们叫他Binding。

  1. channel.queueBind(queueName, "logs", "");

这样,转发器附加到日志队列上去。

下面是一个关于日志系统的完整例子:

发送端代码(生产者)EmitLog.java

  1. package sublog;
  2.  
  3. import java.io.IOException;
  4.  
  5. import com.rabbitmq.client.Channel;
  6. import com.rabbitmq.client.Connection;
  7. import com.rabbitmq.client.ConnectionFactory;
  8.  
  9. public class EmitLog {
  10. private final static String EXCHANGE_NAME = "logs";
  11.  
  12. public static void main(String[] args) throws IOException {
  13. /**
  14. * 创建连接连接到MabbitMQ
  15. */
  16. ConnectionFactory factory = new ConnectionFactory();
  17. // 设置MabbitMQ所在主机ip或者主机名
  18. factory.setHost("115.159.181.204");
  19. factory.setPort(5672);
  20. factory.setUsername("admin");
  21. factory.setPassword("admin");
  22. // 创建一个连接
  23. Connection connection = factory.newConnection();
  24. // 创建一个频道
  25. Channel channel = connection.createChannel();
  26.  
  27. // 指定转发——广播
  28. ((com.rabbitmq.client.Channel) channel).exchangeDeclare(EXCHANGE_NAME, "fanout");
  29.  
  30. for(int i=0;i<3;i++){
  31. // 发送的消息
  32. String message = "Hello World!";
  33. ((com.rabbitmq.client.Channel) channel).basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
  34. System.out.println(" [x] Sent '" + message + "'");
  35. }
  36.  
  37. // 关闭频道和连接
  38. channel.close();
  39. connection.close();
  40. }
  41. }

消费者1 ReceiveLogs2Console.java

  1. package sublog;
  2.  
  3. import java.io.IOException;
  4.  
  5. import com.rabbitmq.client.AMQP;
  6. import com.rabbitmq.client.AMQP.Connection;
  7. import com.rabbitmq.client.Channel;
  8. import com.rabbitmq.client.ConnectionFactory;
  9. import com.rabbitmq.client.Consumer;
  10. import com.rabbitmq.client.DefaultConsumer;
  11. import com.rabbitmq.client.Envelope;
  12.  
  13. public class ReceiveLogs2Console {
  14. private static final String EXCHANGE_NAME = "logs";
  15.  
  16. public static void main(String[] argv) throws IOException, InterruptedException {
  17. ConnectionFactory factory = new ConnectionFactory();
  18. factory.setHost("115.159.181.204");
  19. factory.setPort(5672);
  20. factory.setUsername("admin");
  21. factory.setPassword("admin");
  22. // 打开连接和创建频道,与发送端一样
  23. com.rabbitmq.client.Connection connection =factory.newConnection();
  24. final Channel channel = connection.createChannel();
  25.  
  26. channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
  27. // 声明一个随机队列
  28. String queueName = channel.queueDeclare().getQueue();
  29. channel.queueBind(queueName, EXCHANGE_NAME, "");
  30. System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
  31.  
  32. // 创建队列消费者
  33. final Consumer consumer = new DefaultConsumer(channel) {
  34. @Override
  35. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  36. String message = new String(body, "UTF-8");
  37. System.out.println(" [x] Received '" + message + "'");
  38. }
  39. };
  40. channel.basicConsume(queueName, true, consumer);
  41. }
  42. }

消费者2 ReceiveLogs2File.java

  1. package sublog;
  2.  
  3. import java.io.File;
  4. import java.io.FileNotFoundException;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Date;
  9.  
  10. import com.rabbitmq.client.AMQP;
  11. import com.rabbitmq.client.AMQP.Connection;
  12. import com.rabbitmq.client.Channel;
  13. import com.rabbitmq.client.ConnectionFactory;
  14. import com.rabbitmq.client.Consumer;
  15. import com.rabbitmq.client.DefaultConsumer;
  16. import com.rabbitmq.client.Envelope;
  17.  
  18. public class ReceiveLogs2File {
  19. private static final String EXCHANGE_NAME = "logs";
  20.  
  21. public static void main(String[] argv) throws IOException, InterruptedException {
  22. ConnectionFactory factory = new ConnectionFactory();
  23. factory.setHost("115.159.181.204");
  24. factory.setPort(5672);
  25. factory.setUsername("admin");
  26. factory.setPassword("admin");
  27. // 打开连接和创建频道,与发送端一样
  28. com.rabbitmq.client.Connection connection = factory.newConnection();
  29. final Channel channel = connection.createChannel();
  30.  
  31. channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
  32. // 声明一个随机队列
  33. String queueName = channel.queueDeclare().getQueue();
  34. channel.queueBind(queueName, EXCHANGE_NAME, "");
  35. System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
  36.  
  37. // 创建队列消费者
  38. final Consumer consumer = new DefaultConsumer(channel) {
  39. @Override
  40. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  41. String message = new String(body, "UTF-8");
  42. print2File(message);
  43. // System.out.println(" [x] Received '" + message + "'");
  44. }
  45. };
  46. channel.basicConsume(queueName, true, consumer);
  47. }
  48.  
  49. private static void print2File(String msg) {
  50. try {
  51. String dir = ReceiveLogs2File.class.getClassLoader().getResource("").getPath();
  52. String logFileName = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
  53. File file = new File(dir, logFileName + ".log");
  54. FileOutputStream fos = new FileOutputStream(file, true);
  55. fos.write((new SimpleDateFormat("HH:mm:ss").format(new Date())+" - "+msg + "\r\n").getBytes());
  56. fos.flush();
  57. fos.close();
  58. } catch (FileNotFoundException e) {
  59. e.printStackTrace();
  60. } catch (IOException e) {
  61. e.printStackTrace();
  62. }
  63. }
  64. }

我们用1个生产者用于发送log消息,2个消费者,一个用于打印接收到的消息,另一个除了打印接收到的消息还写有日志信息的文件。

生产者声明了一个广播模式的转换器,订阅这个转换器的消费者都可以收到每一条消息。可以看到在生产者中,没有声明队列。这也验证了之前说的。生产者其实只关心exchange,至于exchange会把消息转发给哪些队列,并不是生产者关心的。

2个消费者,一个打印日志,一个写入文件,除了这2个地方不一样,其他地方一模一样。也是声明一下广播模式的转换器,而队列则是随机生成的,消费者实例启动后,会创建一个随机实例,这个在管理页面可以看到(如图)。而实例关闭后,随机队列也会自动删除。最后将队列与转发器绑定。

注:运行的时候要先运行2个消费者实例,然后在运行生产者实例。否则获取不到实例。

java/rabbitmp发布订阅示例(转)的更多相关文章

  1. RabbitMQ/JAVA (发布/订阅模式)

    发布/订阅模式即生产者将消息发送给多个消费者. 下面介绍几个在发布/订阅模式中的关键概念-- 1. Exchanges (转发器) 可能原来我们都是基于一个队列发送和接收消息.现在介绍一下完整的消息传 ...

  2. java 多线程 发布订阅模式:发布者java.util.concurrent.SubmissionPublisher;订阅者java.util.concurrent.Flow.Subscriber

    1,什么是发布订阅模式? 在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者).而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话 ...

  3. 最通俗易懂的Redis发布订阅及代码实战

    发布订阅简介 除了使用List实现简单的消息队列功能以外,Redis还提供了发布订阅的消息机制.在这种机制下,消息发布者向指定频道(channel)发布消息,消息订阅者可以收到指定频道的消息,同一个频 ...

  4. NetMQ发布订阅C#示例

    NetMQ (ZeroMQ to .Net),ØMQ号称史上最快中间件.它对socket通信进行了封装,使得我们不需要写socket函数调用就能完成复杂的网络通信.和一般意义上的消息队列产品不同的是, ...

  5. java实现 redis的发布订阅(简单易懂)

    redis的应用场景实在太多了,现在介绍一下它的几大特性之一   发布订阅(pub/sub). 特性介绍: 什么是redis的发布订阅(pub/sub)?   Pub/Sub功能(means Publ ...

  6. (转) RabbitMQ学习之发布/订阅(java)

    http://blog.csdn.net/zhu_tianwei/article/details/40887733 参考:http://blog.csdn.NET/lmj623565791/artic ...

  7. 我在生产项目里是如何使用Redis发布订阅的?(二)Java版代码实现(含源码)

    上篇文章讲了在实际项目里的哪些业务场景用到Redis发布订阅,这篇文章就讲一下,在Java中如何实现的. 图解代码结构 发布订阅的理论以及使用场景大家都已经有了大致了解了,但是怎么用代码实现发布订阅呢 ...

  8. java 发布订阅

    https://www.cnblogs.com/coderdxj/p/9627310.html java 观察这模式(发布订阅模式)   观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象 ...

  9. java 观察这模式(发布订阅模式)

    观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新. 发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息. 抽象 ...

随机推荐

  1. [python] 使用scikit-learn工具计算文本TF-IDF值

    在文本聚类.文本分类或者比较两个文档相似程度过程中,可能会涉及到TF-IDF值的计算.这里主要讲述基于Python的机器学习模块和开源工具:scikit-learn.        希望文章对你有所帮 ...

  2. Tool:Visual Studio

    ylbtech-Tool:Visual Studio Microsoft Visual Studio(简称VS)是美国微软公司的开发工具包系列产品.VS是一个基本完整的开发工具集,它包括了整个软件生命 ...

  3. EF中创建、使用Oracle数据库的Sequence(序列)功能

    ** 背景 ** 项目中订单号原来的生成规则由日期加随机数组成,后期需求决定将订单号生成规则更改为生成日期加当天当前订单数. 每天的订单数都是从0开始的,每生成一个订单,订单数就应该加1.订单数应该是 ...

  4. 福利向:几款给力的Unity脚本插件推荐

    转自:http://www.gamelook.com.cn/2016/09/264877 Unity的Asset Store中除了拥有非常强大的Unity编辑器扩展工具之外,还有一些让开发过程事半功倍 ...

  5. 浏览器发起Get,Post请求时候传递的参数编码问题

    浏览器发起Get,Post请求时候传递的参数编码问题 最近开发一个网站的时候,用了很多ajax方法,在页面发起Get,post请求,中间自然捎带有很多参数,有中文,有英文,英文一般是不存在编码问题的, ...

  6. ghostscript 远程命令执行漏洞复现

    影响的版本 <= 9.23(全版本.全平台) Ubuntu 开启 ghostscript sch01ar@ubuntu:~$ gs -q -sDEVICE=ppmraw -dSAFER -s0u ...

  7. 模板导入 {include 模块名}

    模板导入可以和上面讲的模板继承一起使用, 可以使用模板的批量复制和导入 下面举一个例子 我们先写一个需要导入模块的html  tp1 {% extends 'master.html' %} {% bl ...

  8. (halcon) derivate_vector_field

    derivate_vector_field: Convolve a vector field with derivatives of the Gaussian 用高斯导数卷积向量场 derivate_ ...

  9. 导入本体到Jena TDB数据库

    本体的存储方法或称本体持久化,大致分为基于内存的方式.基于文件的方式.基于数据库的方式和专门的管理工具方式4种(傅柱等, 2013).其中,基于数据库的方式又有基于关系数据库.基于面向对象数据库.基于 ...

  10. Elasticsearch-PHP 概述

    最近在学习使用Elasticsearch,并且是和PHP一起使用的,看到了Elasticsearch-PHP,其实是Elasticsearch为PHP提供的客户端,那么我们来学习一下API文档,如何在 ...