先学习一下RabbitMQ中的六种队列,只学习前五种,具体的官方文档地址是:http://next.rabbitmq.com/getstarted.html

导入maven依赖:

 <dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.4.1</version>
</dependency>

一、简单队列

1、图示

P:消息的生产者

C:消息的消费者

红色:队列

生产者将消息发送到队列,消费者从队列中获取消息。

2、获取MQ的连接

 public static Connection getConnection() throws Exception {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("localhost");
//端口
factory.setPort(5672);
//设置账号信息,用户名、密码、vhost
factory.setVirtualHost("/taotao");
factory.setUsername("taotao");
factory.setPassword("taotao");
// 通过工程获取连接
Connection connection = factory.newConnection();
return connection;
}

3、生产者发送消息到队列

 public class Send {

     private final static String QUEUE_NAME = "test_queue";

     public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
// 从连接中创建通道
Channel channel = connection.createChannel(); // 声明(创建)队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 消息内容
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'"); //关闭通道和连接
channel.close();
connection.close();
}
}

4、管理工具中查看消息

点击上面的队列名称,查询具体的队列中的信息:

5、消费者从队列中获取消息

 public class Recv {

     private final static String QUEUE_NAME = "test_queue";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列
channel.basicConsume(QUEUE_NAME, true, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
}
}
}

二、 Work模式

1、图示

一个生产者、2个消费者。

一个消息只能被一个消费者获取。

2、消费者1

 public class Recv {

     private final static String QUEUE_NAME = "test_queue_work";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 同一时刻服务器只会发一条消息给消费者
//channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
//休眠
Thread.sleep(10);
// 返回确认状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

3、消费者2

 public class Recv2 {

     private final static String QUEUE_NAME = "test_queue_work";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 同一时刻服务器只会发一条消息给消费者
//channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成状态
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
// 休眠1秒
Thread.sleep(1000); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

4、生产者

 public class Send {

     private final static String QUEUE_NAME = "test_queue_work";

     public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); //向队列中发送50条消息
for (int i = 0; i < 50; i++) {
// 消息内容
String message = "" + i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'"); Thread.sleep(i * 10);
} channel.close();
connection.close();
}
}

5、测试结果

测试结果:

1、  消费者1和消费者2获取到的消息内容是不同的,同一个消息只能被一个消费者获取。

2、  消费者1和消费者2获取到的消息的数量是相同的,一个是奇数一个是偶数。

其实,这样是不合理的,应该是消费者1要比消费者2获取到的消息多才对。

6、Work模式的“能者多劳”

测试:

消费者1比消费者2获取的消息更多。

这种是比较符合实际情况的,能者多劳,RabbitMQ客户端同一时刻只会给消费者发送一条消息,消费者拿到消息后,重新向客户端拿消息,做的快的消费者要的消息多,同理,做的慢的消费者要的消息少,就体现出来能者多劳的机制了。

7、消息的确认模式

消费者从队列中获取消息,服务端如何知道消息已经被消费呢?

模式1:自动确认

只要消息从队列中获取,无论消费者获取到消息后是否成功消息,都认为是消息已经成功消费。

模式2:手动确认

消费者从队列中获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态。

7.1 手动模式

7.2 自动模式

三、订阅模式

1、图示

解读:

1、1个生产者,多个消费者

2、每一个消费者都有自己的一个队列

3、生产者没有将消息直接发送到队列,而是发送到了交换机

4、每个队列都要绑定到交换机

5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的

注释:Fanout Exchange – 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。

2、消息的生产者(看作是后台系统)

向交换机中发行消息

 public class Send {

     private final static String EXCHANGE_NAME = "test_exchange_fanout";

     public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); // 消息内容
String message = "商品已经被更新,id=1001";
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println(" 后台系统: '" + message + "'"); channel.close();
connection.close();
}
}

注意:消息发送到没有队列绑定的交换机时,消息将丢失,因为,交换机没有存储消息的能力,消息只能存在在队列中。

3、消费者1(看作是前台系统)

 public class Recv {

     private final static String QUEUE_NAME = "test_queue_ps_1";

     private final static String EXCHANGE_NAME = "test_exchange_fanout";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 前台系统: '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

4、消费者2(看作是搜索系统)

 public class Recv2 {

     private final static String QUEUE_NAME = "test_queue_ps_2";

     private final static String EXCHANGE_NAME = "test_exchange_fanout";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 搜索系统: '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

5、测试

测试结果:

同一个消息被多个消费者获取。

在管理工具中查看队列和交换机的绑定关系:

6、使用订阅模式能否实现商品数据的同步?

答案:可以的。

后台系统就是消息的生产者。

前台系统和搜索系统是消息的消费者。

后台系统将消息发送到交换机中,前台系统和搜索系统都创建自己的队列,然后将队列绑定到交换机,即可实现。

消息,新增商品、修改商品、删除商品。

前台系统:修改商品、删除商品。

搜索系统:新增商品、修改商品、删除商品。

所以使用订阅模式实现商品数据的同步并不合理。

四、路由模式

1、图示

注释:Direct Exchange – 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog。

2、生产者

 public class Send {

     private final static String EXCHANGE_NAME = "test_exchange_direct";

     public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "direct"); // 消息内容
String message = "商品删除,id=1002";
channel.basicPublish(EXCHANGE_NAME, "delete", null, message.getBytes());
System.out.println(" 后台系统: '" + message + "'"); channel.close();
connection.close();
}
}

3、消费者1(前台系统)

 public class Recv {

     private final static String QUEUE_NAME = "test_queue_direct_1";

     private final static String EXCHANGE_NAME = "test_exchange_direct";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "update");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "delete"); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 前台系统: '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

4、消费者2(搜索系统)

 public class Recv2 {

     private final static String QUEUE_NAME = "test_queue_direct_2";

     private final static String EXCHANGE_NAME = "test_exchange_direct";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "insert");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "update");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "delete"); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 搜索系统: '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

五、通配符模式

1、图示

注释:Topic Exchange – 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。我在RedHat的朋友做了一张不错的图,来表明topic交换机是如何工作的:

2、生产者

 public class Send {

     private final static String EXCHANGE_NAME = "test_exchange_topic";

     public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "topic"); // 消息内容
String message = "商品删除,id=1003";
channel.basicPublish(EXCHANGE_NAME, "item.delete", null, message.getBytes());
System.out.println(" 后台系统: '" + message + "'"); channel.close();
connection.close();
}
}

3、消费者1(前台系统)

 public class Recv {

     private final static String QUEUE_NAME = "test_queue_topic_1";

     private final static String EXCHANGE_NAME = "test_exchange_topic";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.update");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.delete"); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 前台系统: '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

4、消费者2(搜索系统)

 public class Recv2 {

     private final static String QUEUE_NAME = "test_queue_topic_2";

     private final static String EXCHANGE_NAME = "test_exchange_topic";

     public static void main(String[] argv) throws Exception {

         // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.#"); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 搜索系统: '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

RabbitMQ之六种队列模式的更多相关文章

  1. RabbitMQ六种队列模式-简单队列模式

    前言 RabbitMQ六种队列模式-简单队列 [本文]RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...

  2. RabbitMQ六种队列模式-工作队列模式

    前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列 [本文]RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...

  3. RabbitMQ六种队列模式-发布订阅模式

    前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅 [本文]RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...

  4. RabbitMQ六种队列模式-路由模式

    前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式 [本文]RabbitMQ六种队列模式-主 ...

  5. RabbitMQ六种队列模式-主题模式

    前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主题模式 [ ...

  6. RabbitMQ的六种工作模式

    一.基于erlang语言: 是一种支持高并发的语言 RabbitMQ的六种工作模式: 1.1 simple简单模式 消息产生着§将消息放入队列 消息的消费者(consumer) 监听(while) 消 ...

  7. RabbitMQ的六种工作模式总结

    最近学习RabbitMQ的使用方式,记录下来,方便以后使用,也方便和大家共享,相互交流. RabbitMQ的六种工作模式: 1.Work queues2.Publish/subscribe3.Rout ...

  8. SpringBoot整合RabbitMQ实现六种工作模式

    RabbitMQ主要有六种种工作模式,本文整合SpringBoot分别介绍工作模式的实现. 前提概念 生产者 消息生产者或者发送者,使用P表示: 队列 消息从生产端发送到消费端,一定要通过队列转发,使 ...

  9. RabbitMQ 七种队列模式

    (1)简单模式(Hello World) 做最简单的事情,一个生产者对应一个消费者,RabbitMQ相当于一个消息代理,负责将A的消息转发给B 应用场景: 将发送的电子邮件放到消息队列,然后邮件服务在 ...

随机推荐

  1. Swift4 协议

    创建: 2018/02/27 完成: 2018/02/28 更新: 2018/03/07 增加类采用协议时的注意 补充只有类, 结构体, 枚举型可以采用协议 增加为类定义准备的协议( protocol ...

  2. 第二篇(那些JAVA程序BUG中的常见单词)

    Cannot instantiate the type xxx 无法实例化类型xxx instantiate 实例化

  3. centos 允许远程连接mysql

    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;

  4. C语言之图像旋转

    最近用到了图像向左或者向右旋转90°的情况,纠结了好一会儿,写下来供大家参考. 1 向左旋转90° //unsigned char *pImgData: 输入图像指针 //int WidthIn, i ...

  5. BZOJ2333 棘手的操作

    Description ​ 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x ...

  6. Lightoj 1071 - Baker Vai (双线程DP)

    题目连接: http://lightoj.com/volume_showproblem.php?problem=1071 题目大意: 一个n*m的格子,Baker Vai要从(1,1)到(n,m)再回 ...

  7. 排序sort与qsort

    首先看sort函数见下表: 函数名 功能描述 sort 对给定区间所有元素进行排序 stable_sort 对给定区间所有元素进行稳定排序 partial_sort 对给定区间所有元素部分排序 par ...

  8. Windows环境下修改Oracle实例监听IP地址

    Windows环境下修改Oracle实例监听IP地址. 配置文件路径:<ORACLE_HOME>\NETWORK\ADMIN 如:C:\Oracle11gR2\product\11.2.0 ...

  9. 动态链接库(DLL) 分类: c/c++ 2015-01-04 23:30 423人阅读 评论(0) 收藏

    动态链接库:我们经常把常用的代码制作成一个可执行模块供其他可执行文件调用,这样的模块称为链接库,分为动态链接库和静态链接库. 对于静态链接库,LIB包含具体实现代码且会被包含进EXE中,导致文件过大, ...

  10. java IO流 复制图片

    (一)使用字节流复制图片 //字节流方法 public static void copyFile()throws IOException { //1.获取目标路径 //(1)可以通过字符串 // St ...