84.分布式消息中间件-RabbitMQ实战解决方案之死信队列

一、消息中间件产生了消息堆积如何解决

RabbitMQ如果产生了消息堆积如何处理?

产生的背景:如果没有及时的消费者消费消息,生产者一直不断往队列服务器存放消息
导致消息堆积

两种场景:

1.没有消费者消费的情况下:

1.死信队列
2.设置消息有效期。相当于对我们的消息设置有效期,在规定的时间内如果没有消费的话,自动过期,过期的时候会执行客户端回调监听的方法将消息存放到数据库记录,后期实现补偿。

2.有一个消费者消费的情况:

应该提高我们的消费者 消费实现集群
在这里插入图片描述

二、RabbitMQ如何保证消息不丢失

RabbitMQ如何彻底保证我们的消息不丢失?

1.MQ服务器端应该消息持久化到硬盘
2.生产者使用消息确认机制百分能够将消息投递到MQ成功
3.消费者使用手动ack机制确认消息百分百消费成功

如果队列容量满了,在继续投递可能会丢失 .—> 死信队列

死信队列:

称做为备胎队列,消息中间件队列因为某种消费拒绝存放该消息,可以转移到死信队列中存放。
在这里插入图片描述

死信队列产生的背景:

1.生产者投递消息到MQ中,消息过期了;
2.队列的已经最大长度(队列存放消息满了)MQ拒绝接受存放该消息
3.消费者多次消费该消息失败的情况,也会存放死信。(使用场景最多)

实现我们的死信队列

三、RabbitMQ整合死信队列

在这里插入图片描述

2.交换机 队列 绑定队列到交换机

订单:
死信:

@Component
public class DeadLetterMQConfig {

    /**
     * 订单交换机
     */
    @Value("${mayikt.order.exchange}")
    private String orderExchange;

    /**
     * 订单队列
     */
    @Value("${mayikt.order.queue}")
    private String orderQueue;

    /**
     * 订单路由key
     */
    @Value("${mayikt.order.routingKey}")
    private String orderRoutingKey;

    /**
     * 死信交换机
     */
    @Value("${mayikt.dlx.exchange}")
    private String dlxExchange;

    /**
     * 死信队列
     */
    @Value("${mayikt.dlx.queue}")
    private String dlxQueue;
    /**
     * 死信路由
     */
    @Value("${mayikt.dlx.routingKey}")
    private String dlxRoutingKey;

    /**
     * 声明死信交换机
     *
     * @return DirectExchange
     */
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange(dlxExchange);
    }

    /**
     * 声明死信队列
     *
     * @return Queue
     */
    @Bean
    public Queue dlxQueue() {
        return new Queue(dlxQueue);
    }

    /**
     * 声明订单业务交换机
     *
     * @return DirectExchange
     */
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(orderExchange);
    }

    /**
     * 声明订单队列
     *
     * @return Queue
     */
    @Bean
    public Queue orderQueue() {
        Map<String, Object> arguments = new HashMap<>(2);
        // 绑定我们的死信交换机
        arguments.put("x-dead-letter-exchange", dlxExchange);
        // 绑定我们的死信路由key
        arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
        return new Queue(orderQueue, true, false, false, arguments);

    }

    /**
     * 绑定订单队列到订单交换机
     *
     * @return Binding
     */
    @Bean
    public Binding orderBinding() {
        return BindingBuilder.bind(orderQueue())
                .to(orderExchange())
                .with(orderRoutingKey);
    }


    /**
     * 绑定死信队列到死信交换机
     *
     * @return Binding
     */
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(dlxQueue())
                .to(dlxExchange())
                .with(dlxRoutingKey);
    }

}

在这里插入图片描述
删掉队列,重新启动:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、RabbitMQ死信队列效果演示

消息过期存放到死信队列:

@RestController
public class DeadLetterController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 订单交换机
     */
    @Value("${mayikt.order.exchange}")
    private String orderExchange;

    @Autowired
    private OrderMapper orderMapper;
    /**
     * 订单路由key
     */
    @Value("${mayikt.order.routingKey}")
    private String orderRoutingKey;

    @RequestMapping("/sendOrderMsg")
    public String sendOrderMsg() {
        // 1.生产订单id
        String orderId = System.currentTimeMillis() + "";
        String orderName = "好好学习";
        OrderEntity orderEntity = new OrderEntity(orderName, orderId);
        String msg = JSONObject.toJSONString(orderEntity);
        sendMsg(msg);
        return orderId;
        // 后期客户端主动使用orderId调用服务器接口 查询该订单id是否在数据库中存在数据 消费成功 消费失败
    }

    @Async
    public void sendMsg(String msg) {
        rabbitTemplate.convertAndSend(orderExchange, orderRoutingKey, msg,
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        message.getMessageProperties().setExpiration("10000");
                        return message;
                    }
                });
        // 消息投递失败
    }

    /**
     * 主动查询接口
     * 先查询该订单的消息是否投递失败
     * 在查询数据库
     */

    @RequestMapping("getOrder")
    public Object getOrder(String orderId) {
        OrderEntity orderEntity = orderMapper.getOrder(orderId);
        if (orderEntity == null) {
            return "消息正在异步的处理中";
        }
        return orderEntity;
    }
}

在这里插入图片描述
在这里插入图片描述

@Component
public class OrderDlxConsumer {

    /**
     * 监听我们的死信队列
     *
     * @return
     */
    @RabbitListener(queues = "mayikt_order_dlx_queue")
    public void orderConsumer(String msg) {
        System.out.println("死信队列获取消息:" + msg);
    }
}

在这里插入图片描述

五、死信队列消费者部署流程

死信队列不能够和正常队列存放在同一个服务器中,应该分开服务器存放。
不然就团灭了。

六、RabbitMQ如何获取消费者结果

Rabbitmq异步如何获取消费结果

1.多线程+主动查询
消费的结果根据业务来定

七、主动调用接口查询消费结果

在这里插入图片描述
升级:
先生成orderid
分2步:1.异步投递消息 2.反出orderid
在这里插入图片描述

在这里插入图片描述

发布了119 篇原创文章 · 获赞 12 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44722978/article/details/103519835