【RabbitMQ】——三种Exchange模式(Fanout、Direct、Topic)
来源:互联网 发布:淘宝客网址 编辑:程序博客网 时间:2024/05/18 01:54
引言
在上一篇博客中介绍了RabbitMQ中两种队列模式,下面在改博文中将介绍比较常用的三种Exchange模式。
第一种、Fanout Exchange
这种Fanout模式不处理路由键,你需要简单的讲队列绑定到exchange上,一个发送到exchange的消息都会被转发到与该exchange绑定的所有队列上。很像广播子网,每台子网内的主机都获得了一份复制的消息。Fanout类型的Exchange转发消息是最快的。下面看一张图;
从上面这张图中我们发现,在这种模式中增加了Exchange的概念,这就说生产者会创建一个Exchange,并且将消息发送到exhange中,其余的事情不在关心,并且这种模式中,我们会发现拥有多个消费者,并且每个消费者都有一个独立的消息队列。 每个队列都需要绑定到exchange上面。生产者发送的消息,经过exchange到达队列,从而实现一个消息被多个消费者获取的目的。下面看一个比较简单的图:
在这种模式中我们需要注意的,如果将消息发送到一个没有队列绑定的exchange上面,那么该消息将会丢失,这是因为在rabbitmq中exchange是不具备存储消息的能力,只有队具备存储消息的能力。
示例代码:生产者
package cn.itcast.rabbitmq.ps;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;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 在这定义的交换机的类型是fanout 一共有三种类型的交换机channel.exchangeDeclare(EXCHANGE_NAME, "fanout");// 消息内容String message = "商品已经新增,id = 1000";channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());System.out.println(" [x] Sent '" + message + "'");channel.close();connection.close();}}
示例代码:消费者
package cn.itcast.rabbitmq.ps;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.QueueingConsumer;public class Recv { private final static String QUEUE_NAME = "test_queue_fanout_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); } }}
消费者2代码和消费者1的代码相似,只是声明的队列名称不同,因为在这种模式中,每个消费者都拥有自己的队列。
第二种、Direct Exchange
这种模式会处理路由键,也就是所谓的按需索取。需要将一个队列绑定到exchange上面,要求该消息与一个特定的路由键完全匹配。消息才会进入到该队列中。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog。
这种模式就要去我们生产者在发送消息的时候,为此消息添加一个路由键,这样在消费者获取的时候才能达到“按需索取”。例如:将不同的级别的错误消息传递给不同的消费者,这就需要用到这种模式:
示例代码:生成者
package cn.itcast.rabbitmq.routing;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;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 和发布订阅模式相比 exchange模式为“direct”channel.exchangeDeclare(EXCHANGE_NAME, "direct");// 消息内容String message = "删除商品, id = 1001";// 在路由模式中,我们在发布消息的时候需要指定路由keychannel.basicPublish(EXCHANGE_NAME, "delete", null, message.getBytes());System.out.println(" [x] Sent '" + message + "'");channel.close();connection.close();}}
示例代码:消费者1package cn.itcast.rabbitmq.routing;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.QueueingConsumer;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);}}}
消费者2:
package cn.itcast.rabbitmq.routing;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.QueueingConsumer;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); } }}
从上面的代码中我们可以看到,两个不同的消费者在将队列绑定到exchange上面的时候,指定了不同的路由键。这时候我们如果更改生产者端的路由键的时候,则消息会路由到不同的消费者上面。如果生产者指定路由键为“insert”则只有消费者2获取,如果指定路由键为“update”则两个消费者都会获取。如果指定路由键为“abc”,则两个消费者都不会获取,从而导致消息进入”黑洞“。
但是这种模式在一定程度上满足了我们的需求,但是在使用起来并不是非常的灵活,所以下面介绍一种更为灵活的模式。
第三种、Topic Exchange
这种模式和Direct模式的原理是一样的,都是根据路由键进行消息的路由,但是这种支持路由键的模糊匹配,此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。下面来看一张图:
上面这张图根据颜色进行了路由键匹配的划分,下面四个队列中第一个红色队列,我们会发现它的路由键为“usa.#”这说明凡是路由键以“usa.”开头的消息都会被路由到这个队列中。第二个黄色队列,说明凡是以“.news”结束的路由键都会被路由到这个队列中。这样的模式是我们在使用过程中更加的灵活。
示例代码:生产者
package cn.itcast.rabbitmq.topic;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;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 = 1001"; channel.basicPublish(EXCHANGE_NAME, "item.delete", null, message.getBytes()); System.out.println(" [x] Sent '" + message + "'"); channel.close(); connection.close(); }}
示例代码:消费者package cn.itcast.rabbitmq.topic;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.QueueingConsumer;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); } }}
消费者1是”按需索取“,所以他并不能使用通配符模式,而是使用完全匹配。示例代码:消费者2
package cn.itcast.rabbitmq.topic;import cn.itcast.rabbitmq.util.ConnectionUtil;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.QueueingConsumer;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); } }}
在消费者2中使用通配符模式,更加方便的编写代码,进行消息的路由,这中模式在消息类型比较负责,并且需要进行精细的处理的时候还是比较使用的。
三种队列对比
Fanout类型没有路由键的概念,只要队列绑定到了改exchange上面,就会接收到所有的消息。其余两种在此基础上面引进了路由键的概念,这样实现消费者”按需索取“的模式。
和前面的(简单模式和work模式)两种对比
前面的两种模式中 只有一个队列,消费者都是对一个队列进行消费的。同一个消息只能被一个消费者获取。而在exchange模式中,很好的实现了一个消息被多个消费者获取的需求。通过引入路由键从而更好的实现需求。
小结
这几种模式是rabbitmq中比较重要的个知识点,小编在这总结记录一下,方便日后回顾。如果有理解错误的地方,欢迎拍砖。
- 【RabbitMQ】——三种Exchange模式(Fanout、Direct、Topic)
- RabbitMQ三种Exchange模式(fanout,direct,topic)介绍
- RabbitMQ三种Exchange模式(fanout,direct,topic)
- RabbitMQ三种Exchange模式(fanout,direct,topic)
- rabbitmq direct、fanout、topic 三种Exchange java 代码比较
- RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
- RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
- RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
- RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
- RabbitMQ Exchange Type(Direct/Fanout/Topic)理解测试
- 【RabbitMQ】三种类型交换器 Fanout,Direct,Topic
- RabbitMQ Exchange 模式 - fanout
- RabbitMQ Exchange模式之Fanout
- Spring Boot 整合 RabbitMQ 之 Fanout Exchange模式 (三)
- RabbitMQ Exchange 模式 - topic
- RabbitMQ Exchange 模式 - direct
- RabbitMQ消息处理机制fanout,direct,topic,header
- RabbitMQ Exchange模式之Topic
- JSP获得当前页面名称后判断是否显示页面元素的方法
- https自签名证书_浏览器_Android_Tomcat实践
- 数据分析与数据挖掘
- Hadoop学习笔记(5)
- MapReduce多路径输出
- 【RabbitMQ】——三种Exchange模式(Fanout、Direct、Topic)
- MySql 基础 -_-"
- mysql事务和锁InnoDB
- python基础----随机数和md5加密
- deform.3D.v5.03.rar
- python2.7版本在win7、64位系统安装遇到的问题
- redis主从(自用)
- Hadoop学习笔记(6)
- BootStrap学习笔记,优缺点总结