SpringBooot整合RabbitMq加spring的ApplicationEvent实现订阅消息的异步处理

首先简单介绍一下ACK原理

参考:https://my.oschina.net/gaoguofan/blog/776057

rabbitmq中文文档:http://rabbitmq.mr-ping.com/AMQP/amqp-0-9-1-quickref.html

概念性解读(Ack的灵活)

         首先啊,有的人不是太理解这个Ack是什么,讲的接地气一点,其实就是一个通知,怎么说呢,当我监听消费者,正常情况下,不会出异常,但是如果是出现了异常,甚至是没有获取的异常,那是不是这条数据就会作废,但是我们肯定不希望这样的情况出现,我们想要的是,如果在出现异常的时候,我们识别到,如果确实是一个不良异常,肯定希望数据重新返回队列中,再次执行我们的业务逻辑代码,此时我就需要一个Ack的通知,告诉队列服务,我是否已经成功处理了这条数据,而如果不配置Ack的话呢,我测试过他会自动的忽略,也就是说此时的服务是no_ack=true的模式,就是说只要我发现你是消费了这个数据,至于异常不异常的,我不管了。通知Ack机制就是这么来的,更加灵活的,我们需要Ack不自动,而是手动,这样做的好处,就是使得我们开发人员更加人性化或者灵活的来处理我们的业务罗杰代码,更加方便的处理异常的问题以及数据的返回处理等。下面是通话机制的四条原则:

  • Basic.Ack 发回给 RabbitMQ 以告知,可以将相应 message 从 RabbitMQ 的消息缓存中移除。
  • Basic.Ack 未被 consumer 发回给 RabbitMQ 前出现了异常,RabbitMQ 发现与该 consumer 对应的连接被断开,之后将该 message 以轮询方式发送给其他 consumer (假设存在多个 consumer 订阅同一个 queue)。
  • 在 no_ack=true 的情况下,RabbitMQ 认为 message 一旦被 deliver 出去了,就已被确认了,所以会立即将缓存中的 message 删除。所以在 consumer 异常时会导致消息丢失。

以下是rabbitmq中的topic模式的事例:

RabbitMq的安装参考本人这篇文章(https://blog.csdn.net/wsywb111/article/details/78942382)

1.生产者producer和consumer两边都配置rabbitmq的连接信息也就是即将用到的(ConnectionFactory)

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: admin
    virtualHost: bobo

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2.(生产者)producer定义发送消息

@Service
public class PublishService {
    private final AmqpTemplate amqpTemplate;

    @Autowired
    public PublishService(AmqpTemplate amqpTemplate) {
        this.amqpTemplate = amqpTemplate;
    }

    /**
     * publish market books
     *
     * @param marketBooksDO
     */
    public void fetchedMarketBooks(String exchange, PairMarketBooksDO marketBooksDO) {
        String routeKey = "pair.books." + exchange.toLowerCase();
        byte[] value = ProtoStuffUtil.serialize(marketBooksDO);
        amqpTemplate.convertAndSend(AmqpConfig.PAIR_MARKET_TICKET_EXCHANGE, routeKey, value);
    }

3.(消费者)consumer定义exchange(交换机)以及需要绑定的Queue和对应的RouteKey

(1)编写交换机名称的配置类

public class AmqpConfig {
    //配置类中定义交换机名称
     public static final String PAIR_MARKET_TICKER_EXCHANGE = "pair.market.ticker";
}

(2)定义rabbitmq的配置类

讲解:对于文中的序列化工具ProtoStuffUtil.deserialize()的使用参考本人文章

(https://blog.csdn.net/wsywb111/article/details/79612081)

定义配置类
@Configuration
public class PairMarketTradeReceiver {
    private final ConnectionFactory connectionFactory;//rabbitmq的连接
    private final ApplicationContext applicationContext;//spring的容器

    private static final String PAIR_MARKET_HUOBIPRO_TRADE_QUEUE = "pair.market.huobipro.trade";
   
    @Autowired
    public PairMarketTradeReceiver(ConnectionFactory connectionFactory, ApplicationContext applicationContext) {
        this.connectionFactory = connectionFactory;
        this.applicationContext = applicationContext;
    }

    /**
     * send event
     *定义触发事件的函数
     * @param data
     */
    private void publishPairMarketTradeEvent(byte[] data) {
        PairMarketTradeDO ticker = ProtoStuffUtil.deserialize(data, PairMarketTradeDO.class);
        
        //spring容器触发事件
        applicationContext.publishEvent(new PairMarketTradeUpdateEvent(this, ticker));
    }
    
    //初始化topic类型的交换机(命名)
     @Bean
    TopicExchange pairMarketTradeExchange() {
        return new TopicExchange(AmqpConfig.PAIR_MARKET_TRADE_EXCHANGE);
    }

    // ================ 定义队列并且将其绑定到交换机上 ================
    @Bean
    public Queue pairMarketHuobiProTradeQueue() {
        return new Queue(PAIR_MARKET_HUOBIPRO_TRADE_QUEUE, true, false, false);
    }

将队列绑定到交换机上(配置对应的RouteKey)

@Bean
Binding bindingPairMarketHuobiProTrade(Queue pairMarketHuobiProTradeQueue, TopicExchange pairMarketTradeExchange) {
    return BindingBuilder.bind(pairMarketHuobiProTradeQueue).to(pairMarketTradeExchange)
            .with("pair.trade.huobipro");
}

义监听当监听到rabbitmq的订阅消息的触发publishPairMarketTradeEvent(事件)

//定义监听当监听到rabbitmq的订阅消息的触发publishPairMarketTradeEvent(事件)
@Bean
public SimpleMessageListenerContainer huobiProTradeContainer() {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(this.connectionFactory);
    container.setQueues(pairMarketHuobiProTradeQueue());
    container.setPrefetchCount(20);
    container.setMessageListener(
            (ChannelAwareMessageListener) (message, channel) -> publishPairMarketTradeEvent(message.getBody()));
    return container;
}

4.定义该事件:

public class PairMarketTradeUpdateEvent extends ApplicationEvent {
    private PairMarketTradeDO trade;

    public PairMarketTradeUpdateEvent(Object source, PairMarketTradeDO trade) {
        super(source);
        this.trade = trade;
    }

    public PairMarketTradeDO getTrade() {
        return trade;
    }
}

5.定义异步的监听该时间的Listener

@Async   //异步处理
@EventListener   //事件监听
public void disposeBigTradeCalcEvent(PairMarketTradeUpdateEvent event) {
  //对rabbitmq中传过来的数据做具体的处理
    TradeDo trade= event.getTrade();
}

猜你喜欢

转载自blog.csdn.net/wsywb111/article/details/79752292