应用Redis分布式锁解决重复通知的问题
研究背景:
这几天被支付宝充值后通知所产生的重复处理问题搞得焦头烂额, 一周连续发生两次重复充钱的杯具, 发事故邮件发到想吐。。为了挽回程序员的尊严, 我用了Redis的锁机制。
事故场景:
支付宝下单 -> 客户支付 -> 回调我方接口通知支付结果
服务器节点: 2个
事故发生原因: 回调我方接口后, 第一次通知还未处理完, 第二次通知又来了(间隔几秒),未对通知进行判定重复,导致两个节点均处理了通知, 给客户加了两次钱。。
解决方案:
1. 基于数据库的原子性操作原理控制数据只能被处理一次。
方法: 由于数据能被处理的条件是 pay_record.status = 'paying', 即支付状态为待支付中, 才能更新数据为支付成功。
修改前的更新SQL: update pay_record set status = 'succ' where id = #{id};
修改后的更新SQL: update pay_record set status = 'succ' where id = #{id} and status = 'paying';
通过对返回值是否 > 0判断是否有数据被更新成功, 如果有,则执行后续的给钱包加钱操作,否则不处理。
2. 基于Redis的分布式锁setnx方法控制同时只能有一个线程处理加钱逻辑
// 第一次通知,设置缓存
if(jedis.setnx(DONKEY_ALICALLBACK_NOTICE + outTradeNo, outTradeNo) > 0) {
// 设置生效时长(因为setnx没有生效时间的入参)
jedis.expire(DONKEY_ALICALLBACK_NOTICE + outTradeNo, 3600 * 3);
LOGGER.warn("key = {} 新增缓存成功, 进入处理..", DONKEY_ALICALLBACK_NOTICE + outTradeNo);
// 钱包加钱操作
addMoney(outTradeNo);
} else {
// 新增缓存失败, 不处理
LOGGER.warn("key = {} 新增缓存失败, 不处理", DONKEY_ALICALLBACK_NOTICE + outTradeNo);
return;
}
setnx: 当key不存在时设置成功,返回1, 否则返回0, 这个操作是线程安全的, 可以查看到它的源代码如下:
public Long setnx(String key, String value) {
this.checkIsInMultiOrPipeline();
this.client.setnx(key, value);
return this.client.getIntegerReply();
}
this.checkIsInMultiOrPipeline()方法源码:
protected void checkIsInMultiOrPipeline() {
if(this.client.isInMulti()) {
throw new JedisDataException("Cannot use Jedis when in Multi. Please use Transation or reset jedis state.");
} else if(this.pipeline != null && this.pipeline.hasPipelinedResponse()) {
throw new JedisDataException("Cannot use Jedis when in Pipeline. Please use Pipeline or reset jedis state .");
}
}
总结:
通过jedis.class源码分析可知, redis的大部分的方法均实现了线程安全,都是单线程操作, 故使用redis作为分布式锁效果很好, 也很轻量级。
完毕~~
应用Redis分布式锁解决重复通知的问题的更多相关文章
- Redis分布式锁解决抢购问题
转:https://segmentfault.com/a/1190000011421467 废话不多说,首先分享一个业务场景-抢购.一个典型的高并发问题,所需的最关键字段就是库存,在高并发的情况下每次 ...
- redis分布式锁解决超卖问题
redis事务 redis事务介绍: 1. redis事务可以一次执行多个命令,本质是一组命令的集合. 2.一个事务中的所有命令都会序列化,按顺序串行化的执行而不会被其他命令插入 作用:一个队列 ...
- 使用redis分布式锁解决并发线程资源共享问题
众所周知, 在多线程中,因为共享全局变量,会导致资源修改结果不一致,所以需要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操作 但是在分布式架构中,我们的服务可能会有n个实例,但线程锁只对同一 ...
- 利用redis 分布式锁 解决集群环境下多次定时任务执行
定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...
- 使用Redis分布式锁处理并发,解决超卖问题
一.使用Apache ab模拟并发压测 1.压测工具介绍 $ ab -n 100 -c 100 http://www.baidu.com/ -n表示发出100个请求,-c模拟100个并发,相当是100 ...
- springmvc单Redis实例实现分布式锁(解决锁超时问题)
一.前言 关于redis分布式锁, 查了很多资料, 发现很多只是实现了最基础的功能, 但是, 并没有解决当锁已超时而业务逻辑还未执行完的问题, 这样会导致: A线程超时时间设为10s(为了解决死锁问题 ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
- Redis 分布式锁的实现
0X00 测试环境 CentOS 6.6 + Redis 3.2.10 + PHP 7.0.7(+ phpredis 4.1.0) [root@localhost ~]# cat /etc/issue ...
- Redis分布式锁 (图解-秒懂-史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
随机推荐
- ABP中文网的一些BUG
之前一些翻译了的文档没有及时更新.比如 IAsyncCrudAppService接口在很久之前的版本就已经改为了ICrudAppService,如果是在官网下载的最新实例中IAsyncCrudAppS ...
- C# - VS2019调用AForge库实现调用摄像头拍照功能
前言 作为一名资深Delphi7程序员,想要实现摄像头扫描一维码/二维码功能,发现所有免费的第三方库都没有简便的实现办法,通用的OpenCV或者ZXing库基本上只支持XE以上的版本,而且一维码的识别 ...
- Linux文本文件——管理文本的命令
Linux文本文件——管理文本的命令 摘要:本文主要学习了在Linux中管理文本的命令. cat命令 cat命令用来显示文本文件的内容,也可以把几个文件内容附加到另一个文件中,即连接合并文件,是Con ...
- JPA使用Specification构建动态查询
封装Specification查询条件,在Spring Data JPA 2.0以前使用 Specifications 这个辅助类来操作where.not.and和or连接,在2.0版本以后这个类会被 ...
- 腾讯WeTest&TesterHome深圳线下沙龙
腾讯官方的一站式品质开放平台「腾讯WeTest」携手知名测试社区「TesterHome」以及3家金融相关企业为我们带来的金融专场. 本次活动内容主要以金融公司的测试落地和测试技术为主,我们希望你是一个 ...
- Qt压缩和解压 zip
zlib编译详见 https://blog.csdn.net/zhangxuechao_/article/details/85049711 下载quazip https://github.com/st ...
- flink Transitive Closure算法,实现寻找新的可达路径
flink 使用Transitive Closure算法实现可达路径查找. 1.Transitive Closure是翻译闭包传递?我觉得直译不准确,意译应该是传递特性直至特性关闭,也符合本例中传递路 ...
- 详解JavaScript的任务、微任务、队列以及代码执行顺序
摘要: 理解JS的执行顺序. 作者:前端小智 原文:详解JavaScript的任务.微任务.队列以及代码执行顺序 思考下面 JavaScript 代码: console.log("scrip ...
- vue路由跳转传参的两种方法
路由跳转: this.$router.push({ name: '工单列表', params: {p_camera_dev_name: 'xxx'} }); 使二级菜单呈点击状态: $('[index ...
- jmeter连接并使用mysql数据
一.下载数据库驱动,放至D:\apache-jmeter-2.13\lib\ext目录下 二.打开jmeter,右键添加->配置文件->JDBC Connection Configurat ...