利用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 ...
随机推荐
- ClickHouse在监控系统中的应用
一.背景 这个项目是一个监控系统,主要监控主机.网络设备.应用等.主机监控的数量有1500台左右,数量还在不断增长,加上网络设备和应用,目前总共监控的指标达到近40万个. 二.问题 一开始为了快速交付 ...
- python @property用法(转载)
偶然碰到一篇讲解 @property 比较清晰的文章 记录下来 日常复习 # @property'''@property是python的一种装饰器,是用来修饰方法的 作用:我们可以使用@propert ...
- Window10系统修改hosts文件的方法
背景: 调试smtp程序时遇到问题,度娘说需要修改hosts文件 使用老方法修改了很久,始终无法保存 又百度了一下,在此重温,以加深记忆 方法: Step1.同时按住Windows+X Step2.选 ...
- win 10 遇到某文件一直在占用导致无法关闭,或者去任务管理器找不到服务怎么办?具体解决
1. 打开 cmd 指令框 ,输入 perfmon 回车 就会出来这个 点击 打开资源监视器, 在句柄搜索框搜索 那个占用资源的文件或软件关键词 ,如下 搜索酷狗 将有关的选项,右键选中后 打开菜单 ...
- GORM学习指南
orm是一个使用Go语言编写的ORM框架.它文档齐全,对开发者友好,支持主流数据库. 一.初识Gorm Github GORM 中文官方网站内含十分齐全的中文文档,有了它你甚至不需要再继续向下阅读本文 ...
- SSM简单实现文件上传和下载
一.配置spring-mvc <!-- 配置多媒体文件解析器 --> <bean id="multipartResolver" class="org.s ...
- 获取iframe外的document
在iframe中点击弹出层外部分弹出层消失,但是点击iframe外部分就操作不了弹出层了,被这个问题困扰了不少时间,今天得以解决,代码如下: 说明:$(top.document,document).c ...
- MySQL使用时间作为判断条件
背景:在开发过程中,我们经常需要根据时间作为判断条件来查询数据,例如:当月,当日,当前小时,几天内...... 1. 当月 我们只需要使用一个mysql的MONTH(date)函数即可实现.(注意判断 ...
- Hadoop 代码实现文件上传
本项目主要实现Windows下利用代码实现Hadoop中文件上传至HDFS 实现上传文本文件中单词个数的计数 1.项目结构 2.相关代码 CopyFromLocalFile 1 package com ...
- Golang 通过创建临时结构体实现 struct 内 interface struct 的 json 反序列化
原文链接 背景 type AData struct { A string `json:"a"` } type BData struct { B string `json:" ...