SpringBoot使用Rabbit多消费者削峰

文章目录

    • 场景
    • 配置
    • 生产者发送消息
    • 消费者处理消息

场景

前端系统推送大批量数据进入我方系统进行处理, 为了减轻我方系统的压力, 并且充分发挥服务器的性能, 提高处理效率, 于是使用 Rabbit 做了限流处理, 同时有多线程运行多个消费者处理任务, 来提高效率

配置

Rabbit配置类, 其余的基础配置配置都维护在配置文件或者配置中心


/***
 * Rabbit配置类
 * @author yanqiang.jiang
 * @version 1.0
 * @date 2019/08/26
 **/

@Configuration
@Slf4j
public class RabbitConfig {


    @Autowired
    private CachingConnectionFactory connectionFactory;

    @Autowired
    private SimpleRabbitListenerContainerFactoryConfigurer factoryConfigurer;

    /**
     * 数据队列
     *
     * @return 队列
     */
    @Bean
    public Queue accoflowHs() {
        return new Queue("testQueue");
    }


    /**
     * 单一消费者
     *
     * @return SimpleRabbitListenerContainerFactory
     */
    @Bean(name = "singleListenerContainer")
    public SimpleRabbitListenerContainerFactory listenerContainer() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        factory.setConcurrentConsumers(1);
        factory.setMaxConcurrentConsumers(1);
        factory.setPrefetchCount(1);
        factory.setTxSize(1);
        factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
        return factory;
    }

    /**
     * 多个消费者
     *
     * @return SimpleRabbitListenerContainerFactory
     */
    @Bean(name = "multiListenerContainer")
    public SimpleRabbitListenerContainerFactory multiListenerContainer() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factoryConfigurer.configure(factory, connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        // 多消费者进行手工确认
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        // 默认消费者数量
        factory.setConcurrentConsumers(10);
        // 最大消费者数量
        factory.setMaxConcurrentConsumers(15);
        // 最大投递数
        factory.setPrefetchCount(5);
        return factory;
    }

    /**
     * RabbitTemplate 配置
     *
     * @return RabbitTemplate
     */
    @Bean
    public RabbitTemplate rabbitTemplate() {
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause) -> {
                    log.info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause);
                }
        );
        rabbitTemplate.setReturnCallback((Message message, int replyCode, String replyText, String exchange, String routingKey) -> {
                    log.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey, replyCode, replyText, message);
                }
        );
        return rabbitTemplate;
    }


}

此处注意点:
factory.setMessageConverter(new Jackson2JsonMessageConverter()); 发送的消息将会使用它来序列化
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); 必须开启手动确认模式
factory.setConcurrentConsumers(10);factory.setMaxConcurrentConsumers(15); 这个表示消费者的数量, 也就是消费多线程运行的线程数目.
factory.setPrefetchCount(5); 每次取的消息的数目, 数目大效率高, 但是顺序越得不到保证

生产者发送消息

/***
 * rabbit 生产者
 * @author yanqiang.jiang
 * @version 1.0
 * @date 2019/08/26
 **/
@Component
@Slf4j
public class AccoflowHsProducer {
    @Autowired
    private AmqpTemplate rabbitTemplate;

    /**
     * 发送信息
     *
     * @param batchNum 流水号
     */
    public void stringSend(String batchNum) {
        log.info("消息队列:{},发布消息:{}", "testQueue", batchNum);
        // 第一个参数为刚刚定义的队列名称
        this.rabbitTemplate.convertAndSend("testQueue",
                MessageBuilder.withBody(batchNum.getBytes(StandardCharsets.UTF_8)).build());
    }
}

消费者处理消息

/***
 * rabbit 消费者
 * @author yanqiang.jiang
 * @version 1.0
 * @date 2019/08/26
 **/
@Component
@Slf4j
public class AccoflowHsConsumer {

    @Autowired
    private AccoflowInfHsHandler accoflowInfHsHandler;

    /**
     * 监听消费用户日志
     *
     * @param msg 消息
     */
    @RabbitListener(queues = "testQueue" containerFactory = "multiListenerContainer")
    public void recievedString(Message msg, Channel channel) throws Exception {
        try {
            log.info("来源事务落地通用消费者{}收到消息", channel.getChannelNumber());
            PaTransactionTask task = JSON.parseObject(msg.getBody(), PaTransactionTask.class);
            log.info("来源事务落地通用消费者{}解析消息:{}", channel.getChannelNumber(), task.getPlanControlId());
            // 这里最好添加重复执行判断
            // 处理来源事务
            runner.excuteTransaction(task);
        } catch (Exception e) {
            log.info("来源事务落地通用消费者{}出错", channel.getChannelNumber());
            e.printStackTrace();
        }
        channel.basicAck(msg.getMessageProperties().getDeliveryTag(), true);
        log.info("来源事务落地通用消费者{}确认消", channel.getChannelNumber());
    }
}

注意:
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), true); 处理完毕后一定要确认消息, 不然不会继续处理下个消息. 同时考虑异常的情况,也要手工确认
采取手动确认后处理完成后才会确认,这里处理时间可能比较长, 这个时候消息超时server会向消费者再次发送消息, 所以这里建议添防重复处理。防止重复消费消息。

你可能感兴趣的:(rabbit)