1. fanout模式

1.1 Publish/Subscribe(发布/订阅)结构图

  

  上图表示一个消费者消费消息之后,不讲消息直接存储到队列,而是使用两个消费者各自声明一个队列,将各自的对应的队列与交换机绑定。这样每个消费者都读取的是自身所对应的队列的所有消息,大达到了一个生产者生产消息,所有消费者都能消费的目的。

  将交换机类型设置为fanout即可实现Publish/Subscribe

1.2 生产者代码

  1. package com.wangx.rabbitmq.sp;
  2.  
  3. import com.rabbitmq.client.BuiltinExchangeType;
  4. import com.rabbitmq.client.Channel;
  5. import com.rabbitmq.client.Connection;
  6. import com.rabbitmq.client.ConnectionFactory;
  7.  
  8. import java.io.IOException;
  9. import java.util.concurrent.TimeoutException;
  10.  
  11. public class Producer {
  12.  
  13. private static final String EXCHANGE_NAME = "exchange";
  14. public static void main(String[] args) throws IOException, TimeoutException {
  15.  
  16. //创建连接工厂
  17. ConnectionFactory factory = new ConnectionFactory();
  18. //设置服务器主机
  19. factory.setHost("127.0.0.1");
  20. //设置用户名
  21. factory.setUsername("wangx");
  22. //设置密码
  23. factory.setPassword("wangx");
  24. //设置VirtualHost
  25. factory.setVirtualHost("/wangx");
  26. Connection connection = null;
  27. Channel channel = null;
  28. try {
  29. connection = factory.newConnection();
  30. channel = connection.createChannel();
  31.  
  32. //声明交换机
  33. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
  34. String message = "Hello World!";
  35. //发送消息
  36. for (int i = 0; i < 10; i++) {
  37. //发送消息
  38. channel.basicPublish(EXCHANGE_NAME, "", null, (message + i).getBytes());
  39. System.out.println(" [x] Sent '" + message + i + "'");
  40. }
  41. }catch (Exception e) {
  42.  
  43. }finally {
  44. channel.close();
  45. connection.close();
  46. }
  47. }
  48. }

  与普通消息生产者不同的地方在于,发布订阅时必须要显示的声明一个交换机,并且在发送消息的时候,没有队列,必须设置交换机名称。

1.3 消费者实现代码

  1. package com.wangx.rabbitmq.sp;
  2.  
  3. import com.rabbitmq.client.*;
  4.  
  5. import java.io.IOException;
  6. import java.util.concurrent.TimeoutException;
  7.  
  8. public class Consumer1 {
  9. /**
  10. * 队列名字
  11. */
  12. private static String QUEUE_NAME = "queue1";
  13. private static final String EXCHANGE_NAME = "exchange";
  14. public static void main(String[] args){
  15.  
  16. //创建连接工厂
  17. ConnectionFactory factory = new ConnectionFactory();
  18. //设置服务器主机
  19. factory.setHost("127.0.0.1");
  20. //设置用户名
  21. factory.setUsername("wangx");
  22. //设置密码
  23. factory.setPassword("wangx");
  24. //设置VirtualHost
  25. factory.setVirtualHost("/wangx");
  26. Connection connection = null;
  27. try {
  28. //创建连接
  29. connection = factory.newConnection();
  30. //创建消息通道
  31. final Channel channel = connection.createChannel();
  32. //声明交换机
  33. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
  34. //声明队列
  35. channel.queueDeclare(QUEUE_NAME,false, false, false, null);
  36. //绑定队列与交换机
  37. channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
  38. //消息服务器每次只向消费者发送一条消息
  39. // channel.basicQos(1);
  40. Consumer consumer = new DefaultConsumer(channel){
  41. //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
  42. @Override
  43. public void handleDelivery(String consumerTag, Envelope envelope,
  44. AMQP.BasicProperties properties, byte[] body) throws IOException{
  45. try {
  46. //消息沉睡一秒
  47. Thread.sleep(1000);
  48. String message = new String(body, "UTF-8");
  49. System.out.println("consumer1 收到消息 '" + message + "'");
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }finally {
  53. System.out.println("consumer1 消息消费完成....");
  54. }
  55.  
  56. }
  57. };
  58. //监听消息
  59. channel.basicConsume(QUEUE_NAME, true,consumer);
  60. }catch (Exception e) {
  61. e.printStackTrace();
  62. }finally {
  63. }
  64. }
  65. }

  这里仍然使用的是两个不同的消费者,并且将两个不同的消费者分别声明不同的消息队列,然后将声明的队列与交换机进行绑定,使用channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "fanout");方法。启动两个消费者,将两个不同的消息队列注册并绑定上去。启动消息生产者发送消息,将会看到,两个不同的消费者均能接收到生产者发送的所有消息。

2. Routing 模式

  结构图:

  

  发送消息时指定不同的key,交换机分发消息是根据key分发消息到不同的消费者队列中。

  将交换机模式改为DIRECT,在消费端设置了不同的key,相当于为消息分个类,以便于在交换机分发消息时,将消息分发给持有该key的消费者,生产者代码如下:

  1. package com.wangx.rabbitmq.routing;
  2.  
  3. import com.rabbitmq.client.BuiltinExchangeType;
  4. import com.rabbitmq.client.Channel;
  5. import com.rabbitmq.client.Connection;
  6. import com.rabbitmq.client.ConnectionFactory;
  7.  
  8. import java.io.IOException;
  9. import java.util.concurrent.TimeoutException;
  10.  
  11. public class Producer {
  12.  
  13. private static final String EXCHANGE_NAME = "exchange-routing";
  14. public static void main(String[] args) throws IOException, TimeoutException {
  15.  
  16. //创建连接工厂
  17. ConnectionFactory factory = new ConnectionFactory();
  18. //设置服务器主机
  19. factory.setHost("127.0.0.1");
  20. //设置用户名
  21. factory.setUsername("wangx");
  22. //设置密码
  23. factory.setPassword("wangx");
  24. //设置VirtualHost
  25. factory.setVirtualHost("/wangx");
  26. Connection connection = null;
  27. Channel channel = null;
  28. try {
  29. connection = factory.newConnection();
  30. channel = connection.createChannel();
  31.  
  32. //声明交换机
  33. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
  34. String message = "Hello World!";
  35. //发送消息
  36. for (int i = 0; i < 10; i++) {
  37. if ( i % 2 == 0) {
  38. //发送消息,指定key
  39. channel.basicPublish(EXCHANGE_NAME, "key2", null, (message + i).getBytes());
  40. System.out.println(" 偶数消息 '" + message + i + "'");
  41. } else {
  42. //发送消息
  43. channel.basicPublish(EXCHANGE_NAME, "key1", null, (message + i).getBytes());
  44. System.out.println(" 奇数消息 '" + message + i + "'");
  45. }
  46. }
  47. }catch (Exception e) {
  48.  
  49. }finally {
  50. channel.close();
  51. connection.close();
  52. }
  53. }
  54. }

消费者代码如下:

  1. package com.wangx.rabbitmq.routing;
  2.  
  3. import com.rabbitmq.client.*;
  4.  
  5. import java.io.IOException;
  6.  
  7. public class Consumer1 {
  8. /**
  9. * 队列名字
  10. */
  11. private static String QUEUE_NAME = "queue1";
  12. private static final String EXCHANGE_NAME = "exchange-routing";
  13. public static void main(String[] args){
  14.  
  15. //创建连接工厂
  16. ConnectionFactory factory = new ConnectionFactory();
  17. //设置服务器主机
  18. factory.setHost("127.0.0.1");
  19. //设置用户名
  20. factory.setUsername("wangx");
  21. //设置密码
  22. factory.setPassword("wangx");
  23. //设置VirtualHost
  24. factory.setVirtualHost("/wangx");
  25. Connection connection = null;
  26. try {
  27. //创建连接
  28. connection = factory.newConnection();
  29. //创建消息通道
  30. final Channel channel = connection.createChannel();
  31. //声明交换机
  32. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
  33. //声明队列
  34. channel.queueDeclare(QUEUE_NAME,false, false, false, null);
  35. //绑定队列与交换机,指定接收的消息的key
  36. channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key2");
  37. //消息服务器每次只向消费者发送一条消息
  38. // channel.basicQos(1);
  39. Consumer consumer = new DefaultConsumer(channel){
  40. //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
  41. @Override
  42. public void handleDelivery(String consumerTag, Envelope envelope,
  43. AMQP.BasicProperties properties, byte[] body) throws IOException{
  44. try {
  45. //消息沉睡一秒
  46. Thread.sleep(1000);
  47. String message = new String(body, "UTF-8");
  48. System.out.println("consumer1 收到消息 '" + message + "'");
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }finally {
  52. System.out.println("consumer1 消息消费完成....");
  53. }
  54.  
  55. }
  56. };
  57. //监听消息
  58. channel.basicConsume(QUEUE_NAME, true,consumer);
  59. }catch (Exception e) {
  60. e.printStackTrace();
  61. }finally {
  62. }
  63. }
  64. }

  两个消费者一个使用接收奇数,一个接收偶数消息(根据发送消息时判断奇偶设置不同的key),接收时接收相应的key即可。

  这样就可以指定接收想要接收的类型的消息了,相当于前面学习的mq的消息的过滤。

3. Topic模式

  将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。

   生产者实现:

  1. package com.wangx.rabbitmq.topic;
  2.  
  3. import com.rabbitmq.client.BuiltinExchangeType;
  4. import com.rabbitmq.client.Channel;
  5. import com.rabbitmq.client.Connection;
  6. import com.rabbitmq.client.ConnectionFactory;
  7.  
  8. import java.io.IOException;
  9. import java.util.concurrent.TimeoutException;
  10.  
  11. public class Producer {
  12.  
  13. private static final String EXCHANGE_NAME = "exchange-topic";
  14. public static void main(String[] args) throws IOException, TimeoutException {
  15.  
  16. //创建连接工厂
  17. ConnectionFactory factory = new ConnectionFactory();
  18. //设置服务器主机
  19. factory.setHost("127.0.0.1");
  20. //设置用户名
  21. factory.setUsername("wangx");
  22. //设置密码
  23. factory.setPassword("wangx");
  24. //设置VirtualHost
  25. factory.setVirtualHost("/wangx");
  26. Connection connection = null;
  27. Channel channel = null;
  28. try {
  29. connection = factory.newConnection();
  30. channel = connection.createChannel();
  31.  
  32. //声明交换机
  33. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
  34. String message = "Hello World!";
  35. //发送消息,绑定模式
  36. channel.basicPublish(EXCHANGE_NAME, "key.one", null, ("one -" + message).getBytes());
  37. channel.basicPublish(EXCHANGE_NAME, "key.two.msg", null, ("two -" + message).getBytes());
  38. }catch (Exception e) {
  39.  
  40. }finally {
  41. channel.close();
  42. connection.close();
  43. }
  44. }
  45. }

  这里发送消息时,绑定了key.one和key.two.msg两种模式。接下来使用*号和#号两种通配符得消费者,如下:

Consumer1

  1. package com.wangx.rabbitmq.topic;
  2.  
  3. import com.rabbitmq.client.*;
  4.  
  5. import java.io.IOException;
  6.  
  7. public class Consumer1 {
  8. /**
  9. * 队列名字
  10. */
  11. private static String QUEUE_NAME = "queue-topic";
  12. private static final String EXCHANGE_NAME = "exchange-topic";
  13. public static void main(String[] args){
  14.  
  15. //创建连接工厂
  16. ConnectionFactory factory = new ConnectionFactory();
  17. //设置服务器主机
  18. factory.setHost("127.0.0.1");
  19. //设置用户名
  20. factory.setUsername("wangx");
  21. //设置密码
  22. factory.setPassword("wangx");
  23. //设置VirtualHost
  24. factory.setVirtualHost("/wangx");
  25. Connection connection = null;
  26. try {
  27. //创建连接
  28. connection = factory.newConnection();
  29. //创建消息通道
  30. final Channel channel = connection.createChannel();
  31. //声明交换机
  32. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
  33. //声明队列
  34. channel.queueDeclare(QUEUE_NAME,false, false, false, null);
  35. //绑定队列与交换机,使用通配符key.* 表示只能匹配key下的一个路径,
  36. channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.*");
  37. //消息服务器每次只向消费者发送一条消息
  38. // channel.basicQos(1);
  39. Consumer consumer = new DefaultConsumer(channel){
  40. //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
  41. @Override
  42. public void handleDelivery(String consumerTag, Envelope envelope,
  43. AMQP.BasicProperties properties, byte[] body) throws IOException{
  44. try {
  45. //消息沉睡一秒
  46. Thread.sleep(1000);
  47. String message = new String(body, "UTF-8");
  48. System.out.println("consumer1 收到消息 '" + message + "'");
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }finally {
  52. System.out.println("consumer1 消息消费完成....");
  53. }
  54.  
  55. }
  56. };
  57. //监听消息
  58. channel.basicConsume(QUEUE_NAME, true,consumer);
  59. }catch (Exception e) {
  60. e.printStackTrace();
  61. }finally {
  62. }
  63. }
  64. }

  所以在本例中只能匹配到key.one的消息,控制台打印如下:

  1. consumer1 收到消息 'one -Hello World!'
  2. consumer1 消息消费完成....

  Consumer2实现:

  1. package com.wangx.rabbitmq.topic;
  2.  
  3. import com.rabbitmq.client.*;
  4.  
  5. import java.io.IOException;
  6.  
  7. public class Consumer2 {
  8. /**
  9. * 队列名字
  10. */
  11. private static String QUEUE_NAME = "queue-topic-2";
  12. private static final String EXCHANGE_NAME = "exchange-topic";
  13. public static void main(String[] args){
  14. //创建连接工厂
  15. ConnectionFactory factory = new ConnectionFactory();
  16. //设置服务器主机
  17. factory.setHost("127.0.0.1");
  18. //设置用户名
  19. factory.setUsername("wangx");
  20. //设置密码
  21. factory.setPassword("wangx");
  22. //设置VirtualHost
  23. factory.setVirtualHost("/wangx");
  24. Connection connection = null;
  25. try {
  26. //创建连接
  27. connection = factory.newConnection();
  28. //创建消息通道
  29. final Channel channel = connection.createChannel();
  30. channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
  31. //声明队列
  32. channel.queueDeclare(QUEUE_NAME,false, false, false, null);
  33. //这里使用#号通配符,表示能够匹配到key下的任意路径
  34. channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.#");
  35. Consumer consumer = new DefaultConsumer(channel){
  36. //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
  37. @Override
  38. public void handleDelivery(String consumerTag, Envelope envelope,
  39. AMQP.BasicProperties properties, byte[] body) throws IOException{
  40. try {
  41. //消息沉睡100ms
  42. Thread.sleep(100);
  43. String message = new String(body, "UTF-8");
  44. System.out.println("consumer2 收到消息 '" + message + "'");
  45. } catch (InterruptedException e) {
  46. e.printStackTrace();
  47. }finally {
  48. System.out.println("consumer2 消息消费完成....");
  49. }
  50.  
  51. }
  52. };
  53. //监听消息,第二个参数为true时表示自动确认
  54. channel.basicConsume(QUEUE_NAME, true,consumer);
  55. }catch (Exception e) {
  56. e.printStackTrace();
  57. }finally {
  58. }
  59. }
  60. }

  这里使用了key.#表示匹配所有key.下的所有路径,所以能够接收到所有以key开头的消息,控制台打印如下:

  1. consumer2 收到消息 'one -Hello World!'
  2. consumer2 消息消费完成....
  3. consumer2 收到消息 'two -Hello World!'
  4. consumer2 消息消费完成....

使用topic模式既可以轻易的实现fanout模式,也可以实现routing模式,同时提供了通配符的情况下,使得匹配更加灵活,使用方式更加简洁。

RabbitMQ学习笔记(4)----RabbitMQ Exchange(交换机)的使用的更多相关文章

  1. [RabbitMQ学习笔记] - 初识RabbitMQ

    RabbitMQ是一个由erlang开发的AMQP的开源实现. 核心概念 Message 消息,消息是不具名的,它由消息头和消息体组成,消息体是不透明的,而消息头则由 一系列的可选属性组成,这些属性包 ...

  2. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  3. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  4. RabbitMQ学习笔记1-hello world

    安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/   默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...

  5. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

  6. (转) Rabbitmq学习笔记

    详见原文: http://blog.csdn.net/shatty/article/details/9529463 Rabbitmq学习笔记

  7. RabbitMQ学习笔记五:RabbitMQ之优先级消息队列

    RabbitMQ优先级队列注意点: 1.只有当消费者不足,不能及时进行消费的情况下,优先级队列才会生效 2.RabbitMQ3.5以后才支持优先级队列 代码在博客:RabbitMQ学习笔记三:Java ...

  8. 消息队列——RabbitMQ学习笔记

    消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...

  9. RabbitMQ学习笔记(六) RPC

    什么RPC? 这一段是从度娘摘抄的. RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的 ...

随机推荐

  1. 共享内存、网络(day13)

    一.共享内存 .获取一个键值 ftok() .使用键值获取共享内存的id shmget() #include <sys/ipc.h> #include <sys/shm.h> ...

  2. [NOIP2018模拟赛]d

    d题大概是让有n个矩阵,可以随意平移,问删除m个矩阵后最大的面积交是多少. 其实思路很显然. 肯定删x个a最小的和m-x个b最小的. 然后我们先删m个a最小的,然后逐渐少删a,开始删b,用个堆维护b的 ...

  3. Qt5.11+opencv3.4的配置安装

    系统:Windows 10 64位 前期准备: 1.CMake下载安装 下载地址:https://cmake.org/download/ 选择msi安装文件,按照提示一步一步按照就好 可以参考:htt ...

  4. SPU、SKU、ARPU是什么,我来记录一下我的理解

    在电商系统里经常会提到“商品”.“单品”.“SPU”.“SKU”这几个词,那么这几个词到底是什么意思呢? 既然不知道是什么,那么我们就查一下:SPU = Standard Product Unit ( ...

  5. flask日期和时间

    本文翻译自The Flask Mega-Tutorial Part XII: Dates and Times 这是Flask Mega-Tutorial系列的第十二部分,我将告诉你如何以适配所有用户的 ...

  6. Hystrix 断流器

    一.分布式系统面临的问题 服务雪崩 多个服务之间调用的时候,假设微服务 A 调用微服务 B 和微服务 C,,微服务 B 和微服务 C 又调用其他的微服务,这就是所谓的“扇出”.如果扇出的链路上某个微服 ...

  7. MS SQL 迁移数据库文件

    MS SQL 数据库迁移文件,这里说的不是将数据库迁移到另外一台服务器,只是在服务器不同磁盘目录内做迁移.移动数据库文件的情况大致有下面一些: 1: 事先没有规划好,数据库文件或日志文件增长过快,导致 ...

  8. python操作JIRA的库简单操作

    因公司需要,我们开发的PRISM又需要和JIRA对接啦, 今天找了一个JIRA库撸了一发~~~ jira库地址: https://pypi.python.org/pypi/jira/1.0.3 简单操 ...

  9. java反射并不是什么高深技术,面向对象语言都有这个功能,而且功能也很简单,就是利用jvm动态加载时生成的class对象

    java反射并不是什么高深技术,面向对象语言都有这个功能. 面向对象语言都有这个功能,而且功能也很简单,就是利用jvm动态加载时生成的class对象,去获取类相关的信息 2.利用java反射可以调用类 ...

  10. C#实现动态调用Windows DLL

    调用方法: object obj = WinDllInvoke("Kernel32.dll", "Beep", , }, typeof(void)); 函数代码 ...