使用动态代理实现消费端幂等性验证和消费确认(ack)
动态代理要求目标对象是实现接口的目标对象,所以先写一个消费接口
public interface BaseConsumer{
void consumer(Message message, Channel channel);
}
实现类:
@Component
@Slf4j
public class MailConsumer implements BaseConsumer{
@Autowired
private MailUtil mailUtil;
@Override
public void consumer(Message message, Channel channel) {
Mail mail = MessageHelper.msgToObj(message, Mail.class);
log.info("收到消息: {}", mail.toString());
boolean success = mailUtil.send(mail);
if (!success){
throw new ServiceException("send mail error");
}
}
}
代理工厂:
public class BaseConsumerProxy{
//被代理对象
private Object target;
private MsgLogService msgLogService;
public BaseConsumerProxy(Object target, MsgLogService msgLogService) {
this.target = target;
this.msgLogService = msgLogService;
}
//Proxy.newProxyInstance
/* ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h*/
public Object getProxy(){
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
Object proxy = Proxy.newProxyInstance(classLoader,interfaces,(proxy1,method,args)->{
Message message = (Message)args[0];
Channel channel = (Channel)args[1];
String correlationId = getCorrelationId(message);
if (isConsumed(correlationId)) {// 消费幂等性, 防止消息被重复消费
log.info("重复消费, correlationId: {}", correlationId);
return null;
}
MessageProperties properties = message.getMessageProperties();
long tag = properties.getDeliveryTag();
try {
Object result = method.invoke(target, args);
msgLogService.updateStatus(correlationId, Constant.MsgLogStatus.CONSUMED_SUCCESS);
channel.basicAck(tag,false);
return result;
}catch (Exception e){
log.error("getProxy error", e);
channel.basicNack(tag,false,true);
return null;
}
});
return proxy;
}
private String getCorrelationId(Message message){
String correlationId = null;
MessageProperties properties = message.getMessageProperties();
Map<String, Object> headers = properties.getHeaders();
for (Map.Entry<String, Object> entry : headers.entrySet()) {
String key = (String) entry.getKey();
String value = (String)entry.getValue();
if (key.equals("spring_returned_message_correlation")){
correlationId = value;
}
}
return correlationId;
}
private boolean isConsumed(String correlationId) {
MsgLog msgLog = msgLogService.selectByMsgId(correlationId);
if (null == msgLog || msgLog.getStatus().equals(Constant.MsgLogStatus.CONSUMED_SUCCESS)) {
return true;
}
return false;
}
}
RabbitMQ监听类:
@Component
public class MailListener {
@Autowired
private MailConsumer mailConsumer;
@Autowired
private MsgLogService msgLogService;
@RabbitListener(queues = RabbitConfig.MAIL_QUEUE_NAME)
public class consumer(Message message, Channel channel){
BaseConsumerProxy baseConsumerProxy = new BaseConsumerProxy();
BaseConsumer proxy = (BaseConsumer)baseConsumerProxy.getProxy();
if (null!=proxy){
proxy.consumer(message,channel);
}
}
}