RabbitMQ-学习笔记

单词

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协议

创建虚拟主机

image-20210902232523234主机名需要以 / 开头

image-20210902232651965

创建虚拟主机之后还需要添加一个用户,设置权限为admin管理员,用户名随意

image-20210902232905796

创建完的用户无权访问任何虚拟主机。需要添加一个可以访问的虚拟主机

点对点消息

生产者编码 (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类型的ExchangeDirect相比,都是可以根据Routinghey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符 ! 这种模型Routingkey一般都是由一个或多个单词组成,多个单词之间以""分割,例如:item.insert

image-20210906233421511

【* 匹配一个单词】

【# 匹配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 一样保证数据的一致性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值