利用redis+AOP簡單處理MQ冥等問題

instr發表於2022-02-11

思路:

1、利用redis內部的序列執行特性,使用getandset()處理分散式+併發問題;

2、註解提供入參選擇,通過資料抽取後計算MD5值,實現業務性值的冥等;

程式碼區:

1、註解

 1 /**
 2  * 功能描述:MQ簡單冥等性處理
 3  * 作者:唐澤齊
 4  */
 5 @Documented
 6 @Target({
 7         ElementType.METHOD
 8 })
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface MqPitfall {
11 
12     // 過期時長 預設30天  單位/秒(s)
13     long timeOut() default 30*24*60*60l;
14 
15     // 冥等效驗 引數 必須是能從onMessage()方法的入參中取出的屬性
16     String[] args() default {};
17 }

2、AOP

 1 /**
 2  * 功能描述:MQ資訊過濾
 3  * 作者:唐澤齊
 4  */
 5 @Aspect
 6 @Component
 7 public class MqPitfallInterceptor {
 8 
 9     static final String mqPitfallKey = "MqPitfall:";
10     static final Logger logger = LoggerFactory.getLogger(com.lechuang.common.redis.intercaptor.MqPitfallInterceptor.class);
11 
12     @Resource
13     RedisService redisService;
14 
15     @Around("@annotation(MqPitfall)")
16     public void around(ProceedingJoinPoint point) throws Throwable {
17         MqPitfall mqPitfall = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(MqPitfall.class);
18         String className = ((MethodSignature) point.getSignature()).getMethod().getDeclaringClass().getName();
19         Map<String,Object> map = new HashMap<>();
20         try {
21             for(Object arg: point.getArgs()) {
22                 JSONObject json = null;
23                 if(arg instanceof String) {
24                     json = JSON.parseObject(arg.toString());
25                 } else {
26                     json = JSON.parseObject(JSON.toJSONString(arg));
27                 }
28                 for(String key:mqPitfall.args()) {
29                     map.put(key,json.get(key));
30                 }
31             }
32             if(map.isEmpty()) {
33                 for(Object arg: point.getArgs()) {
34                     JSONObject json = null;
35                     if(arg instanceof String) {
36                         json = JSON.parseObject(arg.toString());
37                     } else {
38                         json = JSON.parseObject(JSON.toJSONString(arg));
39                     }
40                     for(String key: json.keySet()) {
41                         map.put(key,json.get(key));
42                     }
43                 }
44             }
45         } catch (Exception e) {
46             map.put("Args",Arrays.deepToString(point.getArgs()));
47         }
48         map.put("Aspect",className);
49         String thisMd5 = MD5.create().digestHex(map.toString());
50         String key = mqPitfallKey + thisMd5;
51 
52         //簡單的佔位鎖機制
53         Object value = redisService.getAndSet(key, -1l);
54         if(ObjectUtils.isEmpty(value)) {
55             redisService.set(key,1,mqPitfall.timeOut());
56             point.proceed();
57         } else {
58             logger.warn("MQ資訊重複消費 摘要["+thisMd5+"] ==》" + Arrays.deepToString(point.getArgs()));
59         }
60     }
61 }

3、使用

1 /**
2  * @Method 引入切面註解
3  */
4 @Configuration
5 @Import({MqPitfallInterceptor.class})
6 public class WebAppConfig implements WebMvcConfigurer {
7 
8 }
 1 /**
 2  * 作者:唐澤齊
 3  */
 4 @Slf4j
 5 @Service
 6 @RequiredArgsConstructor
 7 @RocketMQMessageListener(consumerGroup = GuildTopic.GUILD_ANCHOR_ATTEST+"_guildAnchorAttestListener", consumeMode = ConsumeMode.ORDERLY, topic = GuildTopic.GUILD_ANCHOR_ATTEST)
 8 public class GuildAnchorAttestListener implements RocketMQListener {
 9 
10     private final GuildAnchorAttestService guildAnchorAttestService;
11 
12     @Override
13     @MqPitfall(args = {"userId","guildId"})
14     public void onMessage(Object message) {
15         log.info("xxxxxx 開始 ==》" + message);
16         long millis = System.currentTimeMillis();
17         try {
18             GuildTopicEnum guildTopicEnum = GuildTopic.find(GuildTopic.GUILD_ANCHOR_ATTEST);
19             if(!guildTopicEnum.valid(message)) {
20                 log.error("xxxxxx  異常 ==> 資訊效驗不合格 : "+message);
21                 return;
22             }
23             GuildAnchorAttest attest = guildTopicEnum.getData().toJavaObject(GuildAnchorAttest.class);
24             guildAnchorAttestService.save(attest);
25             log.info("xxxxxx  成功 ==》" + message);
26         } catch (Exception e) {
27             log.error("xxxxxx  失敗 ==》 "+ message,e);
28         } finally {
29             log.info("xxxxxx  耗時 "+(System.currentTimeMillis()-millis)+"ms ==》" + message);
30         }
31 
32     }
33 }

 

相關文章