最近做了一个利用rabbitmq作为延迟队列给微信推送模板消息的业务
具体场景就是通过定时任务每天凌晨一点查到redis的记录,然后发送给mq,定时到指定时间消费,有个问题就是可能会有一天点击推送模板消息的用户会有很多,会存在消费者消费不过来的场景,了解到SimpleMessageListenerContainer这个容器,可以帮我动态新增消费者。
- 先定义在rabbitmqconfig的配置类中定义一个容器,容器指定监听监听的队列以及消费者的数量,可以根据自己的消息数量定义初始数量和最大消费者数量
@Bean
public SimpleMessageListenerContainer signinMessageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(templateMsgQueue()); // 监听的队列
container.setConcurrentConsumers(concurrentConsumers); // 当前的消费者数量
container.setMaxConcurrentConsumers(maxConcurrentConsumers); // 最大的消费者数量
container.setDefaultRequeueRejected(true); // 是否重回队列
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 签收模式
container.setExposeListenerChannel(true);
container.setConsumerTagStrategy(new ConsumerTagStrategy() { // 消费端的标签策略
@Override
public String createConsumerTag(String queue) { //为每个动态的消费者定义一个tag
return queue + "_" + UUID.randomUUID().toString();
}
});
container.setMessageListener(new MqConsumerHandle()); //设置监听类
return container;
}
可以看到我的定义的初始消费者的3个,当你的消息堵塞太多的时候,它会帮我自动扩展到我的最大消费者数量,是不是很方便
- 这里因为只能指定一个消费者类,所以我做了一个消息处理类,这个处理类里面再去处理消息,分发到具体的消费类,处理类需要实现ChannelAwareMessageListener,因为这个接口有channel,可以自己去选择签收到消息,我这里是自己封装了一下消息体,里面有一个具体转发的类名,这样一个处理类就可以处理很多的类
@Component
public class MqConsumerHandle implements ChannelAwareMessageListener{
@Override
public void onMessage(Message message,Channel channel) {
try {
//解析消息体
MqMessageVo messageVo=JSON.parseObject(message.getBody(),MqMessageVo.class);
//调用具体实现类
BaseConsumer baseConsumer=(BaseConsumer) SpringUtils.getBean(messageVo.getClassName());
baseConsumer.OnMessage(messageVo.getMessage());
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
System.out.println("mq消息转发具体实现异常"+e);
try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
- 顺便提一下遇到的一个问题,在处理类处理消息的时候看到Message的body字段是byte数组的,但是我的生产者又是发送的json消息,发现又要转换一下,后面查了一下发现是可以设置消息的content-type的,你设置的什么,它就会转成什么类型。设置成json格式以后,message的boye就会帮我们转成json,原来默认就是byte的
public void sendTemplateMsgDelayMsg(String msg, Integer delayTime) {
try {
//构建消息,设置content-type为json格式
Message message=MessageBuilder.withBody(msg.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON).build();
//发送延迟消息
rabbitTemplate.convertAndSend(MQEnum.DELAYED_TEMPLATEMSG_EXCHANGE_NAME, MQEnum.DELAYED_TEMPLATEMSG_ROUTING_KEY, message, a -> {
a.getMessageProperties().setDelay(delayTime);
return a;
});
} catch (Exception e) {
System.out.println("发送消息异常");
}
}