单词
basic-基本的、direct-直连、durable-持久、Virtual-虚拟的
四大核心
-
生产者
-
交换机
- 交换机是RabbitMQ.非常重要的一个部件,一方面它接收来自生产者的消息,另一方面它将消息推送到队列中。交换机必须确切知道如何处理它接收到的消息,是将这些消息推送到特定队列还是推送到多个队列,亦或者是把消息丢弃,这个得有交换机类型决定
-
队列
-
消费者
Broker
接收和分发消息的应用,RabbitMQ Server 就是 Message Broker
安装文件
(分别按照以下顺序安装)
-
rpm -ivh erlang-21.3-1.el7.x86_64.rpm
-
yum install socat -y
-
rpm -ivh rabbitmq-server-3.7.18-1.el7.noarch.rpm
1.将rabbitmq安装包上传到linux系统中
erlang-22.0.7-1.el7.x86_64.rpm #l7表示是Centosl7,Centosl8表示Centos8
rabbitmq-server-3.7.18-1.el7.noarch.rpm
2.安装Erlang依赖包
rpm -ivh erlang-22.0.7-1.el7.x86_64.rpm
3.安装RabbitMQ安装包(需要联网)
yum install -y rabbitmq-server-3.7.18-1.el7.noarch.rpm
注意:默认安装完成后配置文件模板在:/usr/share/doc/rabbitmq-server-3.7.18/rabbitmq.config.example目录中,需要
将配置文件复制到/etc/rabbitmq/目录中,并修改名称为rabbitmq.config
4.复制配置文件
cp /usr/share/doc/rabbitmq-server-3.7.18/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
5.查看配置文件位置
ls /etc/rabbitmq/rabbitmq.config
6.修改配置文件(参见下图:)
vim /etc/rabbitmq/rabbitmq.config
59 %% Uncomment the following line if you want to allow access to the
60 %% guest user from anywhere on the network.
61 {loopback_users, []} %% 百分号为注释,去掉该行注释和最后面的逗号 (允许网络上任意位置的来宾用户访问)
62
63
64 %% TLS configuration.
常用命令
-
chkconfig rabbitmq-server on ——(添加开机启动RabbitMQ服务)
-
systemctl start rabbitmq ——(启动服务)
- stop停止
-
systemctl status rabbitmq ——(查看服务状态)
-
rabbitmq-plugins enable rabbitmq_management ——(开启web管理插件)
- 默认端口号 前台 15672 后台端口 5672
- 默认账号密码 ( guest )
-
rabbitmqctl help ——(列出所有rabbitmq可用命令 )
登录web页面会提示 ( 用户只能通过 localhost 登录 )
所以需要创建一个用户,并且赋予超级管理员的权限
#添加一个新的用户
创建账号
rabbitmqctl add_user admin 123
#设置用户角色
rabbitmqctl set_user_tags admin administrator
#设置用户权限
#set_permissions [-p <vhostpath>] <user> <conf> <write> <read>
rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
#用户user_admin具有/vhost1这个virtual host 中所有资源的配置、写、读权限
#当前用户和角色
rabbitmqctl list_users
协议
rabbitmq采用的是AMQP协议
创建虚拟主机
主机名需要以 / 开头
创建虚拟主机之后还需要添加一个用户,设置权限为admin管理员,用户名随意
创建完的用户无权访问任何虚拟主机。需要添加一个可以访问的虚拟主机
点对点消息
生产者编码 (queue)
public class ProducerQ {
public static final String QUEUE_NAME = "HELLO";
public static void main(String[] args) throws Exception {
// 连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 设置ip 账户名 密码
connectionFactory.setHost("192.168.177.130");
// 设置虚拟主机
connectionFactory.setVirtualHost("/ems");
// 设置可以访问虚拟主机的用户名和密码 admin也可以
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
Connection connection = connectionFactory.newConnection();
// 创建一个信道
Channel channel = connection.createChannel();
/*
* 生明一个队列
1.队列名称
2.队列里面的消息是否持久化(磁盘)默认情况消息存储在内存中 true:持久化 false:不持久化
3.是否独占队列,当前队列只供当前连接使用,true:独占,只能自己使用 false:不独占
4.是否自动删除最后一个消费者断开连接以后该队一句是否自动删除 true自动删除 false不自动删除
5.其它参数
*/
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送消息
String massage = ("hello rabbitmq");
/*
* 发送一个消息
1.发送到哪个交换机 , 如果交换机参数传入空串,会使用默认的交换机AMQP DEFAULT,默认交换机回合所有的队列建立隐含绑定关系,绑定的路由key就是队列名称
2.路由的Key值是哪个本次是队列的名称
3.其它参数信息
4.发送消息的消息体 (需要转换成字节类型)
*/
channel.basicPublish("",QUEUE_NAME,null,massage.getBytes());
System.out.println("发送完成");
channel.close();
connection.close();
}
}
消费者编码
public class ConsumerQ {
public static final String QUEUE_NAME = "HELLO";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.177.130");
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null)
/*
* 消费者消费消息
1.消费哪个队列
2.消费成功之后是否要自动应答 true 代表的自动应答 false 代表手动应答
3.消费者时的回调接口
*/
channel.basicConsume(QUEUE_NAME, true,new DefaultConsumer(channel){
@Override // 最后一个参数,从消息队列中取出的消息
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到的消息-->" + new String(body));
}
});
//消费者不建议close()关闭连接,因为需要一直监听,等待一些接口的回调或其他
}
}
channel.queueDeclare(QUEUE_NAME, false, false, false, null) // 这里的QUEUE_NAME 是创建一个队列,不管发送
channel.basicPublish("",QUEUE_NAME,null,massage.getBytes()); // 这里的QUEUE_NAME 是往哪一个队列中发送消息,不管创建
持久化
channel.queueDeclare(QUEUE_NAME, true, false, false, null)
// 第二个durable(持久)参数为true 队列持久化 重启MQ服务器 队列依旧存在,消息不存在
channel.basicPublish("",QUEUE_NAME,MessageProperties.PERSISTENT_TEXT_PLAIN,massage.getBytes());
//MessageProperties.PERSISTENT_TEXT_PLAIN 消息持久化 重启MQ 消息依旧存在 (如果设置了这个参数,n那么队列也要设置持久化,不然队列都没了,消息也就没了)
| **这两个方法的所有参数,生产者和消费者需要保持一致 **|
( 比如:生产者创建一个持久化的消息,消费者去找一个不持久化的消息肯定是找不到的 )
work queues
一个生产者,多个消费者,消息是如何分配的呢?
默认采用循环方式,消息平均分配
但是这样会产生一个问题,如果某些消费者,消费的比较慢,在这种情况下,如果消息依旧平均分配
那么消息就会在队列中产生大量的堆积
默认的循环方式
一个消费者生产10条消息,两个消费者平均消费
1收到的消息-->1---> work队列消息
1收到的消息-->3---> work队列消息
1收到的消息-->5---> work队列消息
1收到的消息-->7---> work队列消息
1收到的消息-->9---> work队列消息
2收到的消息-->2---> work队列消息
2收到的消息-->4---> work队列消息
2收到的消息-->6---> work队列消息
2收到的消息-->8---> work队列消息
2收到的消息-->10---> work队列消息
消费者 1
能者多劳,消费的快就多消费一些,消费的慢就少消费,不再平均分配
public class WorkConsumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.basicQos(1);// 指该消费者在接收到队列里的消息但没有返回确认结果之前,队列不会将新的消息分发给该消费者。队列中没有被消费的消息不会被删除,还是存在于队列中
channel.queueDeclare("work",true,false,false,null);
// 关闭(autoAck)自动应答
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("1收到的消息-->"+new String(body));
// 参数 1 :确认队列中哪个具体的消息
// 参数 2 :是否开启多个消息同时确认
channel.basicAck(envelope.getDeliveryTag(), true);
// channel.basicQos(1);和channel.basicAck(...);是配套使用,只有在channel.basicQos被使用的时候channel.basicAck(...)才起到作用。
}
});
}
}
消费者 2
加上睡眠,模拟消费缓慢
...
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(2000l);
System.out.println("2收到的消息-->"+new String(body));
channel.basicAck(envelope.getDeliveryTag(),true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
fanout 广播
fanout 直译:扇出
在广播模式下,消息发送流程是这样的:
- 可以有多个消费者
- 每个消费者有自己的queue (队列)
- 每个队列都要绑定到Exchange(交换机)
- 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
- 交换机把消息发送给绑定过的所有队列
- 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
生产者编码
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
// 参数 1 :自定义交换机名字
// 参数 2 :交换机类型 =fanout= (固定的 fanout 类型)
channel.exchangeDeclare("logs","fanout");
channel.basicPublish("logs","",null,"fanout 消息发布".getBytes());
RabbitMqUtils.close(channel,connection);
System.out.println(" 发布完成");
}
消费者编码
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("logs","fanout");
// 创建一个临时队列
String queue = channel.queueDeclare().getQueue();
//队列和交换机绑定,第三个参数是路由键 暂时用不到
channel.queueBind(queue,"logs","");
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("收到的消息--->"+new String(body));
}
});
}
Routing
Direct
Routing 之 订阅模型 direct (直连)
Routing ( 路由 ) 之订阅模型-Direct ( 直连 )
在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange
在Direct模型下:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey
(路由key)- 消息的发送方在向Exchange发送消息时,也必须指定消息的
RoutingKey
。- Exchange不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key
进行判断,只有队列的Routingkey
与消息的Routing key
完全一致,才会接收到消息简单来讲就是不同的路由键去匹配不同的队列,让消费者去消费,从而达到,消息按需分配的目的
生产者编码
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "log_direct";
// 声明交换机: 交换机名称,交换机类型(固定的 direct 类型)
channel.exchangeDeclare(exchangeName,"direct");
// 定义一个路由key 为info (这是自定义的) 让routing key选择性的分配队列
String routingKey = "info";
channel.basicPublish(exchangeName,routingKey,null,"路由key 信息发布 ".getBytes());
RabbitMqUtils.close(channel,connection);
System.out.println("发布完成");
}
}
消费者1 编码
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "log_direct";
// 声明交换机
channel.exchangeDeclare(exchangeName,"direct");
// 临时队列
String queue = channel.queueDeclare().getQueue();
// 基于routing key 绑定队列和交换机 , 从而,通过不同的routing key来接收消息
// 接受路由key为info分配的队列消息
channel.queueBind(queue,exchangeName,"info");
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1 收到的消息--->" + new String(body));
}
});
}
消费者2 编码
...
// 接受路由key为error 和 warning 分配的队列消息
channel.queueBind(queue,"log_direct","error");
channel.queueBind(queue,"log_direct","warning");
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2 收到的消息 ---> " + new String(body));
}
});
Topic
topic 动态路由,订阅模式
Topic
类型的Exchange
与Direct
相比,都是可以根据Routinghey
把消息路由到不同的队列。只不过Topic
类型Exchange
可以让队列在绑定Routing key
的时候使用通配符 ! 这种模型Routingkey
一般都是由一个或多个单词组成,多个单词之间以""分割,例如:item.insert
【* 匹配一个单词】
【# 匹配0个或多个单词】
生产者编码
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
// 声明交换机 topic 类型
String exchangeName = "topics";
channel.exchangeDeclare(exchangeName,"topic");
// 路由key
String routingKey = "user.account.password";
channel.basicPublish(exchangeName,routingKey,null,"topic类型发布消息".getBytes());
RabbitMqUtils.close(channel,connection);
System.out.println("发布完成");
}
消费者1编码
相比direct 类型 多了可以使用通配符
【* 匹配一个单词】
【# 匹配0个或多个单词】
加在单词的前后都可以
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
// 声明交换机 topic类型
String exchangeName = "topics";
channel.exchangeDeclare(exchangeName,"topic");
// 临时队列
String queue = channel.queueDeclare().getQueue();
// 绑定队列和交换机 user.* 匹配user后面再加一个单词
channel.queueBind(queue,exchangeName,"user.*"); // 原路由key:"user.account.password" 所以匹配不到
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1 收到的消息--->" + new String(body));
}
});
}
消费者2编码
...
// 队列和交换机绑定, 匹配路由key account前面匹配一个单词,后面匹配0个或多个单词
channel.queueBind(queue,exchangeName,"*.account.#");// 原路由key:"user.account.password" 所以可以匹配到
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2 收到的消息--->" + new String(body));
}
});
}
消息被拒
channel.basicReject(envelope.getDeliveryTag(),false); // 接收消息是拒绝接收 false:不返回发送时的队列
//拒绝什么消息可以做一些判断if(.)
整合springboot
点对点queue
rabbitmq 的配置信息
spring:
application:
name: springboot-rabbitmq
rabbitmq:
port: 5672
host: 192.168.177.130
virtual-host: /ems
username: ems
password: 123
生产者编码
测试类中写上生产者
只有在有消费者的情况下,才可以成功创建队列
@Autowired
RabbitTemplate rabbitTemplate;
@Test
void contextLoads() {
rabbitTemplate.convertAndSend("hello","boot消息发布");
}
}
消费者编码
@Component
// 监听表示一个消费者,并声明一个队列 @Queue是创建一个队列的意思,还可以在括号中加上其他参数,比如持久化,独占...
// 该注解也可以加在方法上,就不用在方法上加Handler注解了
@RabbitListener(queuesToDeclare = @Queue ("hello"))
public class rabbitmqTest {
// 回调注解,处理接收到的消息
@RabbitHandler
public void receive(String massage){
System.out.println("接收到的消息-->"+massage);
}
}
fanout
生产者编码
@Test
void fanout(){
rabbitTemplate.convertAndSend("logs","","fanout消息发布");
}
消费者编码
@Component
public class FConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value="logs",type = "fanout")//绑定的交换机
)
})
public void receive1(String massage){
System.out.println("1 收到的消息--->"+massage);
}
routing
生产者编码
@Test
void routingTest(){
rabbitTemplate.convertAndSend("route","info","routing key消息发布");
}
消费者编码
---------------------------消费者1------------------------------------------
@Component
public class RConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 临时队列
exchange = @Exchange(value = "route",type = "direct"),// 绑定交换机 direct类型
key = {"info","error"} // 路由键 接收 info error
)
})
public void receive1(String massage){
System.out.println("1 接收到的消息--->"+massage);
}
---------------------------消费者2------------------------------------------
...
key = "warning"
...
topic
生产者编码
@Test
void topicTest(){
rabbitTemplate.convertAndSend("topics","user.account.password","topic消息发布");
}
消费者编码
@Component
public class TConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//临时队列
exchange = @Exchange(value = "topics",type = "topic"),//绑定交换机 类型topic
key = {"user.*", "*.order.#"}
)
})
public void receive1(String massage){
System.out.println("1 收到的消息--->"+massage);
}
----------------------------------------消费者2--------------------------------------------------
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,// 临时队列
exchange = @Exchange(value = "topics",type = "topic"),//绑定交换机 topic类型
key = {"user.#"}
)
})
public void receive2(String massage){
System.out.println("2 收到的消息--->"+massage);
}
}
狂神教学版本
不采用@RabbitListener注解的方式,而是采用配置类的方式,将声明交换机、队列、绑定等信息,统一写在配置类中
后续也可以在这个配置类中做一些更详细的配置,比如死信队列 TTL(过期时间)等… 所以也更推荐使用这种配置类的方式声明和绑定
配置类的绑定关系,生产者消费者哪边绑定都可以,但是最好在生产者一方绑定,因为生产者是最先启动的服务,如果消费者监听的队列不存在的话,就会出现异常
1、先编写一个消息生产的类
@Component
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 模拟用户下单
* @param id
*/
public void makeOrder(int id){
String orderId = UUID.randomUUID().toString();
System.out.println("订单编码--->"+orderId);
String exchangeName = "order_exchange";
String routeKey = "";
rabbitTemplate.convertAndSend(exchangeName,routeKey,orderId+id);
}
}
2、测试类中去调用消息生产
@Autowired
private OrderService orderService;
@Test
public void orderSend(){
orderService.makeOrder(888);
}
3、写一个配置类,配置交换机、队列、绑定
@Configuration
public class RConfiguration {
//声明交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("order_exchange",true,false);
}
// 声明队列
@Bean
public Queue smsQueue(){
return new Queue("sms",true,false,false);
}
// 绑定
@Bean
public Binding smsBinding(){
return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
}
}
----------------direct类型 加上路由key的-----------------
1、 修改交换机方法返回值,
2、 绑定是with "路由key"
@Bean
public DirectExchange directExchange(){
return new DirectExchange("direct_exchange",true,false);
}
@Bean
public Binding smsBinding1(){
return BindingBuilder.bind(smsQueue()).to(directExchange()).with("info");
}
4、最后消费
@Service
@RabbitListener(queuesToDeclare = @Queue("sms"))
public class FanoutConsumer {
@RabbitHandler
public void receive(String massage){
System.out.println("收到的消息--->"+massage);
}
}
TTL 过期时间
过期时间TTL表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取;过了之后消息将自动被删除
如果设置了死信队列,也可以将过期的消息移除到死信队列中
RabbitMQ可以对消息和队列设置TTL。目前有两种方法可以设置。
队列过期时间使用较多
队列过期时间
配置类
@Configuration
public class TTLConfiguration {
// ----TTL-------
@Bean
// 过期时间,设置什么类型的交换机无所谓
public DirectExchange ttlDirectExchange(){
return new DirectExchange("ttl_direct_exchange",true,false);
}
@Bean
public Queue ttlQueue(){
//设置队列过期时间 key是固定的 value是过期时间,单位毫秒
// //消息进入到队列,过期后会被自动删除
Map<String,Object> args = new HashMap();
args.put("x-message-ttl",5000);
return new Queue("ttl_direct_queue",true,false,false,args);
}
@Bean
public Binding ttlBinding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
}
生产类
@Component
public class TTLService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void ttlOrder(int id){
String exchangeName = "ttl_direct_exchange";
String routeKey = "ttl";
String massage = "ttl消息发布"+id;
rabbitTemplate.convertAndSend(exchangeName,routeKey,massage);
}
}
队列创建完成之后,前台管理页面会在队列后面显示 TTL 标识
消息过期时间
在消息生产时,就给消息设置过期时间即可,其他随意
public void ttlMassageOrder(int id){
String exchangeName = "ttl_massage_exchange";
String routeKey = "ttlMassage";
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("5000");//设置消息的过期时间
message.getMessageProperties().setContentEncoding("UTF-8");// 消息编码
return message;
}
};
String massage = "ttlMassage消息发布"+id;
// 发送消息,并在最后加上设置消息的一些参数属性
rabbitTemplate.convertAndSend(exchangeName,routeKey,massage,messagePostProcessor);
}
死信队列
DLX,全称为Dead-Letter-Exchange,可以称之为死信交换机,也有人称之为死信邮箱。当消息在一个队列中变成死依
(dead message)之后,它能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就称之为死信队列.
消息变成死信
只要以下三点有一点被满足,消息就会成为死信
消息被拒绝
消息TTL(存活时间)过期
队列达到最大长度
创建了死信交换机后,将本交换机中产生的死信,全部转到死信交换机中管理,后续也可以再创建一个消费者去消费死信队列
创建死信交换机,队列…
@Configuration
// 创建死信队列
public class DeadConfiguration {
@Bean
public DirectExchange deadDirectExchange(){
return new DirectExchange("dead_direct_exchange",true,false);
}
@Bean
public Queue deadQueue(){
return new Queue("dead_queue",true);
}
@Bean
public Binding deadBinding(){
return BindingBuilder.bind(deadQueue()).to(deadDirectExchange()).with("dead");
}
}
配置原有队列
配置需要将死信转移到死信交换机的队列配置上,与死信交换机产生交集
@Bean
public Queue ttlQueue(){
//设置队列过期时间 key是固定的 value是过期时间,单位毫秒
// //消息进入到队列,过期后会被自动删除
Map<String,Object> args = new HashMap();
args.put("x-message-ttl",5000);
args.put("x-dead-letter-exchange","dead_direct_exchange");// 将死信交换机配置上
args.put("x-dead-letter-routing-key","dead");// 路由key,死信交换机如果是fanout模式就不需要配置
//还有其他的配置,例如队列的最大长度等等...
return new Queue("ttl_direct_queue",true,false,false,args);
}
事务
2PC 分布式事务管理,如果出现异常,就会回滚数据库,但是如果有大量的请求,就会造成堵塞。
TCC(补偿事务) 加了确认机制.不能像2PC 一样保证数据的一致性