上文讲到幂等性问题(消息重复消费)、消息丢失的逻辑,这次就把实现代码分享一下
生产者
rabbitMq配置文件配置:
rabbitmq:
host: rabbitMq地址
port: rabbitMq端口
username: rabbitMq用户
password: rabbitMq密码
#这个配置是保证提供者确保消息推送到交换机中,不管成不成功,都会回调
publisher-confirm-type: correlated
#保证交换机能把消息推送到队列中
publisher-returns: true
#虚拟host 可以不设置,使用server默认host
virtual-host: /
#这个配置是保证消费者会消费消息,手动确认
listener:
simple:
acknowledge-mode: manual#消息确认方式,none、manual和auto
template:
mandatory: true
生产者实现代码
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMQ(){
//实体,封装信息
Enity enity = new Enity();
enity.setId(id);
enity.setUserName(UserName);
enity.setSex(sex);
enity.setOrderNo(orderNo);
String json=JSON.toJSONString(enity)
//这个correlationData是因为确认机制发送消息时,需要给每个消息设置一个全局唯一id,以区分不同消息,避免ack冲突。
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
//RabbitMQ提供了类似于回调函数的机制来告诉发送方消息是否发送成功。
//交换机(举例说明)
//RABBITMQ_EXCHANGE
//路由(举例说明)
//RABBITMQ_ROUTING
rabbitTemplate.convertAndSend(RABBITMQ_EXCHANGE, RABBITMQ_ROUTING, json, correlationData);
}
消费者
//RABBITMQ_TOPIC
@RabbitHandler
@RabbitListener(queuesToDeclare = @Queue(RABBITMQ_TOPIC))
public void businessMethods(String json, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
Enity enity = JSON.parseObject(json, Enity.class);
String orderNo = enity.getOrderNo();
boolean states=false;
try {
if (redise.exists(orderNo)) {
String states = redise.get(orderNo);
if (Func.equals(states,"true")) {
log.info("消息已被处理过,结束");
channel.basicAck(tag, true);
return;
}
}
//处理业务 MQ调用同步方法
if (Func.isNotEmpty(json)) {
//处理业务
statesType=service.businessMethods(json);
}
//设置过期时间
redise.setEx(orderNo,String.valueOf(statesType),600L);
//手动Ack
channel.basicAck(tag, true);
} catch (Exception e) {
log.error("消费失败:[{}]", json);
redise.setEx(orderNo,String.valueOf(statesType),600L);
//异常后消息会被送回队列再次被监听到继续消费,重试消费默认3次后便不再消费
Object countObject = redise.get(tag);
Integer count;
if (Func.isEmpty(countObject)) {
count = 0;
} else {
String str = JSON.toJSON(countObject).toString();
count = Integer.valueOf(str);
}
try {
//requeue = true 放入死信队列
if (count >= MAX_RETRIES) {
channel.basicNack(tag, false, false);
} else {
//requeue = false 放入消费队列重试消费
channel.basicNack(tag, false, true);
count=count + 1;
bladeRedis.setEx(tag, count , 60L);
}
} catch (IOException ioException) {
log.error(ioException.getMessage(), ioException);
}
}
}