spring boot 最佳实践(十一)--使用RabbitMQ

Quick Start

RabbitMQ是AMQP协议的一个实现,spring boot提供了快速的接入方案.参考

配置build.gradle

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
}

配置application.yml

spring:
  rabbitmq:
    host: 192.168.56.101
    username: admin
    password: pw
    virtual-host: first_vhost

发送消息

public class FireEventController {
    @Autowired
    private  RabbitTemplate rabbitTemplate;

    @GetMapping("/event")
    public void visit(@RequestParam("name") String name) {
        rabbitTemplate.convertAndSend("spring-boot-exchange", "foo.bar.baz",name);
    }
}

接收消息

我们可以直接使用@RabbitListener注解声明MQ消费者,同springMVC一样,spring可以自动帮我们处理参数转换的工作.

    @RabbitListener(queues = "spring-boot")
    public void receive(String message){
        System.out.println("收到消息:"+ message);
    }

生产级消息队列配置

AMQP模型在观察者模式基础上多做了一层抽象“交换器 ”(Exchange). 在合理配置RabbitMQ时,我们需要正确理解AMQP的概念.参考

AMQP重要的概念有:虚拟主机,交换机,队列,和绑定。

  • 虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单,RabbitMQ当中,用户只能在虚拟主机的粒度进行权限控制。 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。
  • 交换机:Exchange 用于转发消息,但是它不会做存储 ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。 这里有一个比较重要的概念:路由键 。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。
  • 绑定:也就是交换机需要和队列相绑定,这其中如下图所示,是多对多的关系。
    在这里插入图片描述

理想情况下,我们需要一个消息队列做信息解耦,配置一个队列(Queue)就够了,生产者向这个队列发送消息,消费者订阅这个队列消费.但在实际生存环境,为了保证消息的不丢失,我们应该针对一些异常场景做一系列的配置.
参考
在这里插入图片描述

  • exchange: 配置业务交换机和备用交换机

    属性业务交换机AE交换机死信交换机
    Nameexchange.bizTopicexchange.bizTopic.fanoutexchange.bizTopic.dlx
    Typetopicfanouttopic
    DurabilityDurableDurableDurable
    Auto deletenonono
    Arguments“alternate-exchange” = “exchange.bizTopic.fanout”
  • queue:配置消息队列

    属性业务队列AE队列死信队列
    Namequeue.bizTopic.case1queue.bizTopic.fanoutqueue.bizTopic.dlx
    DurabilityDurableDurableDurable
    Auto deletenonono
    Arguments“x-dead-letter-exchange” = “exchange.bizTopic.dlx”

    一个消费场景一个消费队列,如果有多个场景消费,建议配置多个业务队列.可以基于发送的routingKey分发到不同的队列,无法投递的转到AE队列.死信队列根据对错误的关注程度不同,可以统一输出到一个死信队列,或者不同的业务队列输出到对应的死信队列.

  • binding:配置exchange路由

    属性业务路由AE路由死信路由
    Exchangeexchange.bizTopicexchange.bizTopic.fanoutexchange.bizTopic.dlx
    To queuequeue.bizTopic.case1queue.bizTopic.fanoutqueue.bizTopic.dlx
    Routing keyqueue.bizTopic.case1##

可靠消息投递

在大多数互联网消息解耦的场景下,以上方案可以实现99%的消息送达和消费.但在业务一致性和完整性要求非常高的情况下,1%的消息丢失也是不可接受的.我们需要对消息的送达和消费做更严格的管理.
配置消息发送到交换机确认机制:

spring:
  rabbitmq:
    publisher-confirms: true #  消息发送到交换机确认机制,是否确认回调
    publisher-returns: true  #  消息发送到交换机确认机制,是否返回回馈
    template.mandatory: true

申明消息回调handler:

    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
    }

    public void publishCreated(Order order) {
        rabbitTemplate.convertAndSend(properties.bizExchange(), properties.createRoutingKey(), order,
                new CorrelationData("" + order.getOrderId()));
    }

当消息发送到交换机(exchange)时,MsgSendConfirmCallBack#confirm()被调用.

  • 1.如果消息没有到exchange,则 ack=false
  • 2.如果消息到达exchange,则 ack=true
public class MsgSendConfirmCallBack implements RabbitTemplate.ConfirmCallback {
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        log.info("MsgSendConfirmCallBack  , 回调correlationData:" + correlationData);
        if (ack) {
            log.info("消息发送到exchange成功");
            // TODO 删除 msgId 与 Message 的关系
        } else {
            log.info("消息发送到exchange失败");
            // TODO 消息发送到exchange失败 , 重新发送
        }
    }
}

当消息从交换机到队列失败时,MsgSendReturnCallback#returnedMessage()被调用。(若成功,则不调用)
**需要注意的是:该方法调用后,{@link MsgSendConfirmCallBack}中的confirm方法也会被调用,且ack = true **

public class MsgSendReturnCallback implements RabbitTemplate.ReturnCallback {

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("MsgSendReturnCallback [消息从交换机到队列失败]  message:" + message);
        // TODO 消息从交换机到队列失败,重新发送
    }
}

可靠消息消费

Spring boot 中进行 AOP拦截 自动帮助做重试,消息会自动重试参考.为了防止死循环,我们需要做一些配置.参考

spring:
  rabbitmq:
    listener:
      simple:
        retry:
        ####开启消费者重试,默认开启
          enabled: true
         ####最大重试次数(默认无数次)
          max-attempts: 2
        ####重试间隔
          initial-interval: 1
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值