思路:

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 }

利用redis+AOP简单处理MQ冥等问题的更多相关文章

  1. 手把手教你用redis实现一个简单的mq消息队列(java)

    众所周知,消息队列是应用系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有 ActiveMQ,RabbitMQ,Zero ...

  2. 利用Redis cache优化app查询速度实践

    注意:本篇文章译自speeding up existing app with a redis cache,如需要转载请注明出处. 发现问题 在应用解决方法之前,我们需要对我们面对的问题有一个清晰的认识 ...

  3. 利用redis写webshell

    redis和mongodb我之所见 最近自己在做一些个人的小创作.小项目,其中用到了mongodb和redis,最初可能对这二者没有深入的认识.都是所谓的“非关系型数据库”,有什么区别么? 实际上,在 ...

  4. 利用redis实现分布式锁

    分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于ZooKeeper的分布式锁: 3. 基于Redis的分布式锁: 这里大概说一下三种方式的优缺点,数据库乐观锁优点是实现简单,只需要for ...

  5. redis 的简单命令

    以下实例讲解了如何启动 redis 客户端: 启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis > re ...

  6. Watchdogs利用Redis实施大规模挖矿,常见数据库蠕虫如何破?

    背景 2月20日17时许,阿里云安全监测到一起大规模挖矿事件,判断为Watchdogs蠕虫导致,并在第一时间进行了应急处置. 该蠕虫短时间内即造成大量Linux主机沦陷,一方面是利用Redis未授权访 ...

  7. 后端利用Redis队列及哈希实现定时推送提醒的三个思路

    周煦辰 2016年8月31日 本文介绍了一下本人在开发过程中遇到"定时推送提醒"的需求的时候所思考的三种解决方案. 明确问题 首先明确一下这个需求可能包含的几个"坑&qu ...

  8. Tomcat8利用Redis配置Session共享

    同一个应用在运行多个tomcat实例的时候,经常需要共享Session.tomcat配置共享session有多种方式 1.利用tomcat自身集群特性进行配置: 2.利用Memcache第三方缓存进行 ...

  9. 如何更好的利用redis

    原文地址http://oldblog.antirez.com/post/take-advantage-of-redis-adding-it-to-your-stack.html @(syoka)[re ...

随机推荐

  1. [vscode] os.getcwd(),调试和命令行运行的结果不一致

    问题描述: 调试和命令行运行的时候工作目录不一致 这会导致一个问题,我想从上级目录导入模块的话,F5调试就会找不到模块,而命令行则没问题 那么我该如何调试呢? 目录结构: top  └ folder_ ...

  2. Jsp页面中常见的page指令

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6513327323628962312/ 1.<JSP页面实际上就是Servlet> 2.<JSP页 ...

  3. java 8 - java 17 升级指北

    2014年发布的java SE 8和2017年发布的java EE 8,至今还是使用最广泛的java版本,大部分java开发者对于java 8之后的升级总是敬而远之,这跟java 9以后的破坏性升级和 ...

  4. test_6 python的列表去重

    1.使用内置函数set() set() 函数创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可以计算交集.差集.并集等. 2.创建一个空的列表进行比较,把不重复的元素添加到新的列表中 #co ...

  5. scanf报错问题

    VS2013 scanf()函数报错问题. 编译器就是说这个环境下用scanf()函数不安全,建议使用scanf_s()函数.如何要使用scanf()函数的话,就需要在头文件或者源文件开头加上:use ...

  6. leetcode 51. N皇后 及 52.N皇后 II

    51. N皇后 问题描述 n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 上图为 8 皇后问题的一种解法. 给定一个整数 n,返回所有不同的 n 皇后 ...

  7. Windows蓝牙失效超全攻略

    新电脑蓝牙出现问题,我捣鼓了很久,历经九九八十一难得以修复,说一说我在网上看到的各种方法. 一个功能正常使用,需要经过一个又一个的步骤.任何一个地方出问题,都有可能造成蓝牙失效.以下方法按出现概率从大 ...

  8. JavaCV推流实战(MP4文件)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. VS code远程连接Linux 开发C++ 配置详细介绍

    VS code 远程连接服务器,编译C++ 一.前期准备 1.VS code安装 Remote-SSH插件 2.Windows安装SSH. 3.Linux服务器连接测试. a.接通测试使用ping命令 ...

  10. phpstudy后门POC分析和EXP开发

    POC 2019年9月20日,网上传出 phpStudy 软件存在后门,随后作者立即发布声明进行澄清,其真实情况是该软件官网于2016年被非法入侵,程序包自带PHP的php_xmlrpc.dll模块被 ...