前言
场景:当消费者消费消息出现异常时,利用注解和切面进行处理,写进数据库中,然后通过定时任务进行重发.
提示:以下是本篇文章正文内容,下面案例可供参考
一、保存和修改注解
在生产者中标注MqMsgSave注解,把发送的消息存在数据库中
在消费者中标注MqMsgEdit注解,改变消息发送成功的状态
@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Documented
public @interface MqMsgSave{
//需要记录的消息对象参数位置
int argsIndex();
//消息类型,表明是哪种消息,如果发送失败调相应的接口重发消息
int msgType();
}
@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Documented
public @interface MqMsgEdit{
//需要记录的消息对象参数位置
int argsIndex();
//消息类型,表明是哪种消息,如果发送失败调相应的接口重发消息
}
//新建一个消息类,用来报错消息发送状态和次数
public class MessageHistoryLog implements Serializable {
private Long id;
private String content;
private String sendResponse
private Integer status;
private Integer type;
@DateTimeFormat(
pattern = "yyyy-MM-dd HH:mm:ss"
)
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss",
timezone = "GMT+8"
)
private Date createTime;
@DateTimeFormat(
pattern = "yyyy-MM-dd HH:mm:ss"
)
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss",
timezone = "GMT+8"
)
private Data updateTime;
}
二、MqMsgSaceAspect
public class MqMsgSaceAspect{
@Autowired
private MqHistoryLogService logService;
@Pointcut("@annotation(xxx.MqMsgSave)")
private void pointCut(){
}
//MqMsgSave 就是标注在生产者方法上注解的信息,JoinPoint就是方法上参数的所有信息
@AfterReturning(value = "pointCut()&& @annotation(mqMsgSave)",returning = "rvt")
@Async("baseExecutor")
public void afterReturning(JoinPoint joinPoint ,MqMsgSave mqMsgSave,Object rvt ){
Object obj = joinPoint.getArgs()[0];
//0代表生产者发送中,消费者没消费
getMessageHistoryLog(obj,mqMsgSave.getmsgTYpe(),0)
}
@AfterThrowing(value = "pointCut()&& @annotation(mqMsgSave)",throwing= "exception")
@Async("baseExecutor")
public void afterThrowing(JoinPoint joinPoint ,MqMsgSave mqMsgSave,Exception exception ){
Object obj = joinPoint.getArgs()[mqMsgSave.argsIndex()];
//2代表发送消息失败
getMessageHistoryLog(obj,mqMsgSave.getmsgTYpe(),2)
}
private void getMessageHistoryLog(Object obj,int type,int status){
MessageHistoryLog log = new MessageHistoryLog();
long id = ReflectionUtil.getFieldValue(obj,"id");
String content = JSON.toJSONString(obj);
log.setId(id);
log.setStatus(status);
log.setContent(content);
log.setType(type);
try{
MessageHistoryLog logDTO = logService.selectById(id);
if(logDTO==null){
log.setCreateTime(new Date());
logService.save(log);
}else{
log.setCreateTime(logDTO.getCreateTime());
log.setUpdateTime(new Date());
logService.updateById(log);
}
}catch(Exception e){
log.errir("xxxxxxx")
}
}
}
三、MqMsgEditAspect
public class MqMsgEditAspect{
@Autowired
private MqHistoryLogService logService;
@Autowired
private MyErrorLogService errorLogService;
@PointCut("@annotation(xxx.MqMsgEdit)")
private void pointCut(){
}
@AfterReturning(value = "pointCut() && @annotation(mqMsgEdit)",returning = "res")
public void afterReturning(JoinPoint joinPoint,MqMsgEdit mqMsgEdit,Object res){
Object obj = joinPoint.getArgs()[mqMsgEdit.argsIndex()];
editMqHistoryLog(obj,res,1);
}
@AfterThrowing(value = "pointCut() && @annotation(mqMsgEdit)",returning = "res")
public void afterThrowing(JoinPoint joinPoint,MqMsgEdit mqMsgEdit,Object res){
Object obj = joinPoint.getArgs()[mqMsgEdit.argsIndex()];
editMqHistoryLog(obj,res,2);
}
private void editMqHistoryLog(Object obj,Object res,int status){
Object idObj = ReflectionUtil.getFieldValue(obj,"id");
long id = (long) idObj ;
MessageHistoryLog logDTO = logService.selectById(id);
if(logDTO !=null){
logDTO.setStatus(status);
if(status ==2 && isRetryMax(logDTO)){
//如果重试超过最大次数,直接设置为发送成功。避免资源浪费
logDTO.setStatus(1);
}
log.setUpdateTime(new Date());
logService.updateById(logDTO);
}
}
private boolean isRetryMax(MessageHistoryLog logDTO ){
//计算这个消息总共有多少次
int selectCount = errorLogService.selectCount(logDTO.getId);
//这边可以建一个类,消费者消息消息出现问题后,把信息存储到这个类中,然后通过定时任务发送
MyHistoryErrorLogDTO myHistoryErrorLogDTO = new MyHistoryErrorLogDTO();
myHistoryErrorLogDTO .setId(xx);
myHistoryErrorLogDTO.setContent(xxx);
myHistoryErrorLogDTO.setType(xxxx);
boolean isRetryMax = selectCount >=5;
if(!isRetryMax){
//如果小于5次,继续插入
errorLogService.add(myHistoryErrorLogDTO );
}
return isRetryMax ;
}
}
四、定时任务
定时任务逻辑:就是获取失败的消息,然后进行重发即可,我后面会写一遍springboot整合 elasticJob 的分布式定时任务。