04. RabbitMQ的几种工作模式
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🍟🍟🍟RabbitMQ提供了6种消息模型,但是第6种其实是RPC,并不是MQ,因此我们只学习前5种
🍗🍗🍗在线手册:https://www.rabbitmq.com/getstarted.htm
5种消息模型,大体分为两类:
- 1和2属于点对点
- 3、4、5属于发布订阅模式(一对多)
1. 模式概括
1.1 点对点模式:
P2P(point to point)模式包含三个角色:
-
消息队列(queue),生产者(sender),消费者(receiver)
-
每个消息发送到一个特定的队列中,消费者从中获得消息
-
队列中保留这些消息,直到他们被消费或超时
-
特点:
-
- 每个消息只有一个消费者,一旦消费,消息就不在队列中了。
- 生产者和消费者之间没有依赖性,生产者只管生产消息,消费者只能消费消息。
-
- 接收者成功接收消息之后需向对象应答成功(确认|ACK机制)
- 如果希望发送的每个消息都会被成功处理,那需要P2P
1.2 发布订阅模式:
publish(Pub)/subscribe(Sub):
-
pub/sub模式包含三个角色:交换机(exchange),发布者(publisher),订阅者
(subcriber)(没有队列是因为,队列只需要在消费者绑定路由即可) -
多个发布者将消息发送交换机,系统将这些消息传递给多个订阅者
-
特点:
-
- 每个消息可以有多个订阅者
- 发布者和订阅者之间在时间上有依赖,对于某个交换机的订阅者,必须创建一个订阅
后,才能消费发布者的消息
-
- 为了消费消息,订阅者必须保持运行状态;
- 如果希望发送的消息被多个消费者处理,可采用本模式
2. 模式分类
2.1点对点模式-简单模式
下面引用官网的一段介绍:
Introduction
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as
a post office: when you put the mail that you want posting in a post box, you can be sure
that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy,
RabbitMQ is a post box, a post office and a postman.
译文:RabbitMQ是一个消息代理:它接收和转发消息。你可以把它想象成一个邮局:当你把你想要
寄的邮件放到一个邮箱里,你可以确定邮递员先生或女士最终会把邮件送到你的收件人那里。在
这个类比中,RabbitMQ是一个邮箱、一个邮局和一个邮递员。
RabbitMQ本身只是接收,存储和转发消息,并不会对信息进行处理!
类似邮局,处理信件的应该是收件人而不是邮局!
- RabbitMQ本身只是接收,存储和转发消息,并不会对信息进行处理!
- 类似邮局,处理信件的应该是收件人而不是邮局!
生产者代码:
package simplemode;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;
/**
* @author WeiHong
* @date 2021 - 09 - 06 16:39
* 简单模式
* 消息生产者
*/
public class Sender {
public static void main(String[] args) throws Exception {
String msg ="学习rabbitMQ,ACK手动确认!";
//1.获取连接
Connection connection = ConnectionUtil.getConnection();
//2.在连接中创建通道
Channel channel = connection.createChannel();
//3.创建消息队列(1、2、3、4、5)
/**
* 参数1:队列的名称
* 参数2:队列中的数据是否持久化
* 参数3:是否排外(是否支持扩展,当前队列只能自己用,不能给别人用)
* 参数4:是否自动删除(当队列连接数为0时,队列会销毁,不管队列是否存有保存数据)
* 参数5:队列参数(没有参数为null)
*/
channel.queueDeclare("queue1",false,false,false,null);
//4.向指定的队列发送消息
/**
* 参数1:交换机名称,当前为简单模式,也就是p2p模式,没有交换机,所以名称为“”;
* 参数2:目标队列的名称
* 参数3:设置消息的属性(没有属性则为null)
* 参数4:消息的内容(只接收字节数组)
*/
channel.basicPublish("","queue1",null,msg.getBytes());
System.out.println("发送:"+msg);
//5.释放资源
channel.close();
connection.close();
}
}
启动生产者,即可前往管理端查看队列中的信息,会有一条信息没有处理和确认
消费者代码:
package simplemode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 06 17:00
* 消息接收者
*/
public class Recer {
public static void main(String[] args)throws Exception{
// 1.获取连接
Connection connection = ConnectionUtil.getConnection();
// 2.在连接中创建通道(信道)
Channel channel = connection.createChannel();
// 3.从信道中获得信息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override//交付处理(收件人信息,信封(包裹上的快递标签),协议的配置,消息)
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//body就是从队列中获取的消息
String s= new String(body);
System.out.println("接收="+s);
}
};
// 4.监听队列 true:自动消息确认
channel.basicConsume("queue1",true,consumer);
}
}
启动消费者,前往管理端查看队列中的信息,所有信息都已经处理和确认,显示0
消息确认机制ACK:
- 通过刚才的案例可以看出,消息一旦被消费,消息就会立刻从队列中移除
- RabbitMQ如何得知消息被消费者接收?
-
- 如果消费者接收消息后,还没执行操作就抛异常宕机导致消费失败,但是RabbitMQ无从得知,这样消息就丢失了
- 因此,RabbitMQ有一个ACK机制,当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收
-
- ACK:(Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误我们在使用http请求时,http的状态码200就是告诉我们服务器执行成功
- 整个过程就想快递员将包裹送到你手里,并且需要你的签字,并拍照回执
-
- 不过这种回执ACK分为两种情况:
-
-
- 自动ACK:消息接收后,消费者立刻自动发送ACK(快递放在快递柜)
- 手动ACK:消息接收后,不会发送ACK,需要手动调用(快递必须本人签收)
-
-
- 两种情况如何选择,需要看消息的重要性:
-
-
- 如果消息不太重要,丢失也没有影响,自动ACK会比较方便
- 如果消息非常重要,最好消费完成手动ACK,如果自动ACK消费后,RabbitMQ就会把
-
-
-
- 消息从队列中删除,如果此时消费者抛异常宕机,那么消息就永久丢失了
-
- 修改手动消息确认
// 4.监听队列 false:手动消息确认
channel.basicConsume("queue1", false,consumer);
- 结果如下
确认ACK代码如下:
package simplemode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @BelongsProject: lagou-rabbitmq
* @Author: GuoAn.Sun
* @CreateTime: 2020-08-10 15:08
* @Description: 消息接收者,加入ACK确认机制
*/
public class RecerByACK {
public static void main(String[] args) throws Exception {
// 1.获得连接
Connection connection = ConnectionUtil.getConnection();
// 2.获得通道(信道)
final Channel channel = connection.createChannel();
// 3.从信道中获得消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override //交付处理(收件人信息,包裹上的快递标签,协议的配置,消息)
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// body就是从队列中获取的消息
String s = new String(body);
System.out.println("接收 = " + s);
// 手动确认(收件人信息,是否同时确认多个消息)
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
// 4.监听队列 false:手动消息确认
channel.basicConsume("queue1", false,consumer);
}
}
ACK确认模式:
*// 4.监听队列 false:手动消息确认,必须为手动消息确认,ACK确认机制才生效。
*channel.basicConsume(“queue1”,fasle,consumer);
没执行channel.basicAck(envelope.getDeliveryTag(),false);该语句时消息是未被确认的。
2.2 工作队列模式
-
之前我们学习的简单模式,一个消费者来处理消息,如果生产者生产消息过快过多,而消费者的能力有限,就会产生消息在队列中堆积(生活中的滞销)
-
一个烧烤师傅,一次烤50支羊肉串,就一个人吃的话,烤好的肉串会越来越多,怎么处理?
-
多招揽客人进行消费即可。当我们运行许多消费者程序时,消息队列中的任务会被众多消费者共享,但其中某一个消息只会被一个消费者获取(100支肉串20个人吃,但是其中的某支肉串只能被一个人吃)
生产者代码:
package workmode;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;
/**
* @author WeiHong
* @date 2021 - 09 - 06 19:23
* 工作模型
*/
public class Sender {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work_queue",false,false,false,null);
for(int i = 1;i<=100;i++) {
String msg = "羊肉串 --> " + i;
channel.basicPublish("", "work_queue", null, msg.getBytes());
System.out.println("新鲜出炉:" + msg);
}
channel.close();
connection.close();
}
}
消费者1代码:
package workmode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 06 21:22
*/
public class Recer1 {
static int i =1;
public static void main(String[] args)throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列、双重含义
// 如果队列不存在,就创建;如果队列存在,则获取
channel.queueDeclare("work_queue",false,false,false,null);
channel.basicQos(1);//系统不会给消费者发送超过1个消息以上,只有确认ACK后,才回继续发下一个消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//body就是从队列中获取的消息
String s= new String(body);
System.out.println("【顾客1】吃掉 " + s+" ! 总共吃【"+i+++"】串!"+"信封收件人:"+envelope.getDeliveryTag());
//模拟网络延迟
try {
Thread.sleep(200);
}catch (Exception e){
e.printStackTrace();
}
//手动确认(收件人信息,是否确认多个消息)
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
// 4.监听队列 false:手动消息确认
channel.basicConsume("work_queue", false,consumer);
}
}
消费者2代码:
package workmode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @BelongsProject: lagou-rabbitmq
* @Author: GuoAn.Sun
* @CreateTime: 2020-08-10 15:08
* @Description: 消息接收者,加入ACK确认机制
*/
public class Recer2 {
static int i =1;
public static void main(String[] args) throws Exception {
// 1.获得连接
Connection connection = ConnectionUtil.getConnection();
// 2.获得通道(信道)
final Channel channel = connection.createChannel();
channel.queueDeclare("work_queue",false,false,false,null);
channel.basicQos(1);
// 3.从信道中获得消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override //交付处理(收件人信息,包裹上的快递标签,协议的配置,消息)
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String s = new String(body);
System.out.println("【顾客2】吃掉 " + s+" ! 总共吃【"+i+++"】串!");
// 模拟网络延迟
try{
Thread.sleep(900);
}catch (Exception e){
}
// 手动确认(收件人信息,是否同时确认多个消息)
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
// 4.监听队列 false:手动消息确认
channel.basicConsume("work_queue", false,consumer);
}
}
结果:
生产者产生100个羊肉串
消费者1消费了81条消息
消费者2消费了19条消息
channel.basicQos:系统不会给消费者发送超过1个消息以上,只有确认ACK后,才回继续发下一个消息
- 不加channel.basicQos(1);
- 不加channel.basicQos(1),消费者是相互竞争的关系,但是会把消息进行平分。
- 例:1个生产者;2个消费者。生产者发送100条信息,消费者尽管消费速度不一样,这两个消费者还是会将这100条数据各分50条。
- 加channel.basicQos(1);
- 遵循能者多劳机制,消费者是相互竞争的关系,但是不会把消息进行平分。消费速度快的会继续消费信息,直至所有消息被消费完。
能者多劳必须要配合手动的ACK机制才生效:
- 面试题:避免消息堆积?
-
workqueue,多个消费者监听同一个队列
-
接收到消息后,通过线程池,异步消费
2.3 发布订阅模式
发布-订阅
在上一篇教程中,我们创建了一个工作队列。工作队列背后的假设是,每个任务都被准确地交
付给一个工作者。在这一部分中,我们将做一些完全不同的事情——将消息传递给多个消费者。
此模式称为“发布/订阅”。
为了演示这个模式,我们将构建一个简单的日志记录系统。它将由两个程序组成——第一个将
发送日志消息,第二个将接收和打印它们。
在我们的日志系统中,接收程序的每一个正在运行的副本都将获得消息。这样我们就可以运行
一个接收器并将日志指向磁盘;与此同时,我们可以运行另一个接收器并在屏幕上看到日志。
基本上,发布的日志消息将广播到所有接收方
生活中的案例:就是玩抖音快手,众多粉丝关注一个视频主,视频主发布视频,所有粉丝都可以得到视
频通知
上图中,X就是视频主,红色的队列就是粉丝。binding是绑定的意思(关注)
P生产者发送信息给X路由,X将信息转发给绑定X的队列
X队列将信息通过信道发送给消费者,从而进行消费
整个过程,必须先创建路由
路由在生产者程序中创建
因为路由没有存储消息的能力,当生产者将信息发送给路由后,消费者还没有运行,所以没
有队列,路由并不知道将信息发送给谁
运行程序的顺序:
1.先运行生产者sender;创建路由
2在运行消费者Recer1、Recer2。
生产者代码
package publishSubscribeMode;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;
/**
* @author WeiHong
* @date 2021 - 09 - 07 14:11
* 发布订阅模式
*/
public class Sender {
public static void main(String[] args) throws Exception {
// 1.获取连接
Connection connection = ConnectionUtil.getConnection();
// 2.在连接中创建信道
Channel channel = connection.createChannel();
// 3.声明路由(4种路由)
// fanout:不处理路由键(只需要将队列绑定到路由上,发送到路由的消息都会被转发到与该路由绑定的所有队列上)
channel.exchangeDeclare("test_exchange_fanout","fanout");
// 4.发布消息
String msg = "这是发布订阅模式的fanout路由模型的消息";
channel.basicPublish("test_exchange_fanout","",null,msg.getBytes());
System.out.println("发送:"+msg);
// 5.释放资源
channel.close();
connection.close();
}
}
消费者1代码
package publishSubscribeMode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 07 15:29
* 发布订阅模式-消费者1
*/
public class Recer1 {
public static void main(String[] args)throws Exception{
// 1.获取连接
Connection connection = ConnectionUtil.getConnection();
// 2.在链接中创建信道
Channel channel = connection.createChannel();
//2.1声明队列
channel.queueDeclare("test_exchange_fanout_queue1",false,false,false,null);
//2.2绑定路由
channel.queueBind("test_exchange_fanout_queue1","test_exchange_fanout","");
// 3.定义内部类重写方法接收消息\从信道中获得信息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String s = new String(body);
System.out.println("[消费者1] = "+ s);
}
};
// 4.监听队列 true:自动消息确认
channel.basicConsume("test_exchange_fanout_queue1",true,consumer);
}
}
消费者2代码
package publishSubscribeMode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 07 15:29
* 发布订阅模式-消费者2
*/
public class Recer2 {
public static void main(String[] args)throws Exception{
// 1.获取连接
Connection connection = ConnectionUtil.getConnection();
// 2.在链接中创建信道
Channel channel = connection.createChannel();
//2.1声明队列
channel.queueDeclare("test_exchange_fanout_queue2",false,false,false,null);
//2.2绑定路由
channel.queueBind("test_exchange_fanout_queue2","test_exchange_fanout","");
// 3.定义内部类重写方法接收消息\从信道中获得信息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String s = new String(body);
System.out.println("[消费者2] = "+ s);
}
};
// 4.监听队列 true:自动消息确认
channel.basicConsume("test_exchange_fanout_queue2",true,consumer);
}
}
2.4 路由模式
- 路由会根据类型进行定向分发消息给不同的队列,如图所示
生产者代码
package routerMode;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;
/**
* @author WeiHong
* @date 2021 - 09 - 09 14:38
* 路由模式-生产者
* 路由会根据类型进行定向分发消息给不同的队列
*/
public class Sender {
public static void main(String[] args) throws Exception {
//1.获取连接
Connection connection = ConnectionUtil.getConnection();
//2.在连接中创建信道
Channel channel = connection.createChannel();
//3.路由声明(路由名,路由类型)
//direct:会根据路由键进行定向分发消息
channel.exchangeDeclare("router_exchange_direct","direct");
//4.向队列发送信息
for(int i = 0; i < 10 ; i++){
String msg = "查询的数据第"+i+"条消息!";
//selecte:此时该参数为路由键不是队列名
channel.basicPublish("router_exchange_direct","selecte",null,msg.getBytes());
System.out.println("生产者发送="+msg);
}
//5.释放连接
channel.close();
connection.close();
}
}
消费者1代码
package routerMode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 09 15:05
*/
public class Recer1 {
public static void main(String[] args) throws Exception {
//1.获取连接
Connection connection = ConnectionUtil.getConnection();
//2.在链接中创建信道
Channel channel = connection.createChannel();
//2.1声明队列
channel.queueDeclare("exchange_direct_queue1",false,false,false,null);
//2.2绑定路由(如果路由键的类型是插入、修改绑定到这个队列1上)
channel.queueBind("exchange_direct_queue1","router_exchange_direct","insert");
channel.queueBind("exchange_direct_queue1","router_exchange_direct","update");
//3.定义内部类获取信息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String res = new String(body);
System.out.println("【消费者1】="+ res);
}
};
//4.监听队列 true:自动消息确认
channel.basicConsume("exchange_direct_queue1",true,consumer);
}
}
消费者2代码
package routerMode;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 09 15:05
*/
public class Recer2 {
public static void main(String[] args) throws Exception {
//1.获取连接
Connection connection = ConnectionUtil.getConnection();
//2.在链接中创建信道
Channel channel = connection.createChannel();
//2.1声明队列
channel.queueDeclare("exchange_direct_queue2",false,false,false,null);
//2.2绑定路由(如果路由键的类型是查询绑定到这个队列2上)
channel.queueBind("exchange_direct_queue2","router_exchange_direct","selecte");
//3.定义内部类获取信息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String res = new String(body);
System.out.println("【消费者2】="+ res);
}
};
//4.监听队列 true:自动消息确认
channel.basicConsume("exchange_direct_queue2",true,consumer);
}
}
运行顺序
-
记住运行程序的顺序,先运行一次sender(创建路由器),
-
有了路由器之后,在创建两个Recer1和Recer2,进行队列绑定
-
再次运行sender,发出消息
消费者接收信息由路由键确定,当生产者发送信息到路由上时,因为指定了路由键,所以消息会被指派到对应的消息队列里。消费者通过队列绑定路由接收到不同路由键的消息。
//selecte:此时该参数为路由键不是队列名
channel.basicPublish("router_exchange_direct","selecte",null,msg.getBytes());
//2.2绑定路由(如果路由键的类型是查询绑定到这个队列2上)
channel.queueBind("exchange_direct_queue2","router_exchange_direct","selecte");
2.5 通配符模式
和路由模式90%是一样的。
唯独的区别就是路由键支持模糊匹配
匹配符号
*:只能匹配一个词(正好一个词,多一个不行,少一个也不行)
#:匹配0个或更多个词
看一下官网案例:
Q1绑定了路由键 .orange. Q2绑定了路由键 ..rabbit 和 lazy.#
下面生产者的消息会被发送给哪个队列?
quick.orange.rabbit # Q1 Q2
lazy.orange.elephant # Q1 Q2
quick.orange.fox # Q1
lazy.brown.fox # Q2
lazy.pink.rabbit # Q2
quick.brown.fox # 无
orange # 无
quick.orange.male.rabbit # 无
生产者代码
package topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;
/**
* @author WeiHong
* @date 2021 - 09 - 14 14:03
*/
public class Sender {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明路由(路由名,路由类型)
// topic:模糊匹配的定向分发
channel.exchangeDeclare("test_exchange_topic", "topic");
String msg = "商品降价";
channel.basicPublish("test_exchange_topic", "produce.price.test", null,
msg.getBytes());
System.out.println("[用户系统]:" + msg);
channel.close();
connection.close();
}
}
消费者1代码
package topic;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 14 14:10
*/
public class Recer1 {
public static void main(String[] args) throws Exception {
//1.建立连接
Connection connection = ConnectionUtil.getConnection();
//2.在链接中建立信道
Channel channel = connection.createChannel();
//3.声明队列
channel.queueDeclare("test_exchange_topic_queue1",false,false,false,null);
//4.绑定路由
channel.queueBind("test_exchange_topic_queue1","test_exchange_topic","user.*");
//5.定义内部类接收消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println("[消费者1]接收到的信息:"+msg);
}
};
//6.监听队列 true:消息自动确认
channel.basicConsume("test_exchange_topic_queue1", true,consumer);
}
}
消费者2代码
package topic;
import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;
/**
* @author WeiHong
* @date 2021 - 09 - 14 14:10
*/
public class Recer2 {
public static void main(String[] args) throws Exception {
//1.建立连接
Connection connection = ConnectionUtil.getConnection();
//2.在链接中建立信道
Channel channel = connection.createChannel();
//3.声明队列
channel.queueDeclare("test_exchange_topic_queue2",false,false,false,null);
//4.绑定路由
channel.queueBind("test_exchange_topic_queue2","test_exchange_topic","produce.#");
//5.定义内部类接收消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println("[消费者2]接收到的信息:"+msg);
}
};
//6.监听队列 true:消息自动确认
channel.basicConsume("test_exchange_topic_queue2", true,consumer);
}
}
几种常见路由件说明:
- 发布/订阅模式
*// fanout:不处理路由键(只需要将队列绑定到路由上,发送到路由的消息都会被转发到与该路由绑定的所有队列上)
*channel.exchangeDeclare(“test_exchange_fanout”,“fanout”);
- 路由模式
*//direct:会根据路由键进行定向分发消息
*channel.exchangeDeclare(“router_exchange_direct”,“direct”);
- 通配符模式
*// topic:模糊匹配的定向分发
*channel.exchangeDeclare(“test_exchange_topic”, “topic”);