利用redis+AOP简单处理MQ冥等问题
思路:
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冥等问题的更多相关文章
- 手把手教你用redis实现一个简单的mq消息队列(java)
众所周知,消息队列是应用系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有 ActiveMQ,RabbitMQ,Zero ...
- 利用Redis cache优化app查询速度实践
注意:本篇文章译自speeding up existing app with a redis cache,如需要转载请注明出处. 发现问题 在应用解决方法之前,我们需要对我们面对的问题有一个清晰的认识 ...
- 利用redis写webshell
redis和mongodb我之所见 最近自己在做一些个人的小创作.小项目,其中用到了mongodb和redis,最初可能对这二者没有深入的认识.都是所谓的“非关系型数据库”,有什么区别么? 实际上,在 ...
- 利用redis实现分布式锁
分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于ZooKeeper的分布式锁: 3. 基于Redis的分布式锁: 这里大概说一下三种方式的优缺点,数据库乐观锁优点是实现简单,只需要for ...
- redis 的简单命令
以下实例讲解了如何启动 redis 客户端: 启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis > re ...
- Watchdogs利用Redis实施大规模挖矿,常见数据库蠕虫如何破?
背景 2月20日17时许,阿里云安全监测到一起大规模挖矿事件,判断为Watchdogs蠕虫导致,并在第一时间进行了应急处置. 该蠕虫短时间内即造成大量Linux主机沦陷,一方面是利用Redis未授权访 ...
- 后端利用Redis队列及哈希实现定时推送提醒的三个思路
周煦辰 2016年8月31日 本文介绍了一下本人在开发过程中遇到"定时推送提醒"的需求的时候所思考的三种解决方案. 明确问题 首先明确一下这个需求可能包含的几个"坑&qu ...
- Tomcat8利用Redis配置Session共享
同一个应用在运行多个tomcat实例的时候,经常需要共享Session.tomcat配置共享session有多种方式 1.利用tomcat自身集群特性进行配置: 2.利用Memcache第三方缓存进行 ...
- 如何更好的利用redis
原文地址http://oldblog.antirez.com/post/take-advantage-of-redis-adding-it-to-your-stack.html @(syoka)[re ...
随机推荐
- 很漂亮的一个背景控件——ribbons.js
写博客的人都喜欢优化自己的博客主页,博主也一样,找了一些背景控件,像canvas-nest.js等等,最终选择了ribbons.js,并基于源码,稍作了一丁点的修改,这里分享出来 (function ...
- javascript中逻辑运算(||,&&,!)
作为一个后端开发的程序员,一直就对JavaScript情有独钟,作为一门前后端通吃的语言,必须赞一下.而且之前很长一段时间都有在做JavaScript,一路都是和ie8死磕,磕完又找低版本的谷歌磕,坑 ...
- Python中切片方法总结
对字符串或列表使用切片方法进行操作时 对包含[-1]的方法的使用经常用错 其实[-1]即指最后一个元素(同理[-2]指倒数第二个元素) 现总结如下 以便加深记忆 >>> li = [ ...
- 第10组 Beta冲刺 总结
1.基本情况 组长博客链接:https://www.cnblogs.com/cpandbb/p/14050808.html 答辩总结: ·因为alpha阶段的产品做得偏离了方向,所以beta冲刺大家非 ...
- 第10组 Alpha冲刺 (5/6)
1.1基本情况 ·队名:今晚不睡觉 ·组长博客:https://www.cnblogs.com/cpandbb/p/13996848.html ·作业博客:https://edu.cnblogs.co ...
- SYCOJ906瑞士轮
题目-瑞士轮 (shiyancang.cn) 模拟题 #include<bits/stdc++.h> using namespace std; const int N=1e5+520; i ...
- Git 基本操作指南
Git 基本操作指南 内容概要 这个作业属于哪个课程 2022面向对象程序设计 这个作业要求在哪里 2022面向对象程序设计寒假作业1 这个作业的目标 Git & Github 作业正文 如下 ...
- HTTP API认证授权方案
目录 一.需求背景 二.常用的API认证技术 2.1 App Secret Key + HMAC 2.2 OAuth 2.0 2.2.1 Authorization Code Flow 2.2.2 C ...
- Go 函数,包(二)
#### Go 函数,包(二)***百丈峰,松如浪,地势坤,厚德载物之像*** 今天又到周五啦,你们有没有激动呢,反正我很激动,又有两天的自由了; 上一节我们学习了Go 的函数和包的一些知识 , 今天 ...
- JavaWeb概念及Web资源的分类
一,JavaWeb概念 1,JavaWeb,所有通过java语言编写可以通过浏览器访问的程序的总称 JavaWeb是基于请求和响应来开发的. 2,什么是请求 请求:指客户端给服务端发送数据,Reque ...