利用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 ...
随机推荐
- Kylin开启Kerberos安全认证
Kylin开启Kerberos安全认证, 由于Kylin是依赖Hbase启动的, Kylin启动脚本kylin.sh中就是调用的Hbase的启动脚本, 所以当Hbase开启了Keberos之后就等于K ...
- [特别篇] 记JZ冬令营(Finished)
1.16 走错班了, 去了全是大佬的1班, 然后灰溜溜滚回2班了. 去参加开营仪式. 然而昏昏欲睡... 实在太累了澡也没洗.. 群英云集, 多是感慨. 当时依依惜别和铮铮誓言, 在重逢中无语凝噎. ...
- SQL高级优化(五)之执行计划
一.explain 执行计划:在MySQL中可以通过explain关键字模拟优化器执行SQL语句,从而知道MySQL是如何处理SQL语句的. explain:MySQL执行计划的工具,查看MySQL如 ...
- Go语言实战-爬取校花网图片
一.目标网站分析 爬取校花网http://www.xiaohuar.com/大学校花所有图片. 经过分析,所有图片分为四个页面,http://www.xiaohuar.com/list-1-0.htm ...
- 利用python绘制分析路易斯安那州巴吞鲁日市的人口密度格局
前言 数据来源于王法辉教授的GIS和数量方法,以后有空,我会利用python来实现里面的案例,这里向王法辉教授致敬. 绘制普查人口密度格局 使用属性查询提取区边界 import numpy as np ...
- 注意,你所做的 A/B 实验,可能是错的!
对于 A/B 实验原理认知的缺失,致使许多企业在业务增长的道路上始终在操作一批"错误的 A/B 实验".这些实验并不能指导产品的优化和迭代,甚至有可能与我们的初衷背道而驰,导致&q ...
- Allwinner F1C100s coremark测试
ccu register base:0x01c20000 devmem 0x01c20000 The PLL output=(24MHz*N*K)/(M*P) N=31 K=1 M=1 P=/1 re ...
- linux系统Kibana安装 汉化
Elasticsearch官方系列软件Kibana,在控制台管理维护Elasticsearch. 这里注意Elasticsearch和Kibana的版本一定要一致. 官网下载地址 https://ww ...
- PWA 技术落地!让你的站点(Web)秒变APP(应用程序)
Web应用方兴未艾,我们已经十分习惯习惯了在电脑上进行以自己的工作,而随着众多功能强大的在线网站,我们的Windows的桌面也不再拥挤着各种快捷方式:不光是PC端,在移动端我们也不再在浩如烟海的应用市 ...
- vue体验
1.创建工作区 2.点击添加文件 3.新建一个html文件,并输入!+tab 自动生成html结构 4.输入div#app,生成头部 <!DOCTYPE html> <html l ...