研究背景:

这几天被支付宝充值后通知所产生的重复处理问题搞得焦头烂额, 一周连续发生两次重复充钱的杯具, 发事故邮件发到想吐。。为了挽回程序员的尊严, 我用了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分布式锁解决重复通知的问题的更多相关文章

  1. Redis分布式锁解决抢购问题

    转:https://segmentfault.com/a/1190000011421467 废话不多说,首先分享一个业务场景-抢购.一个典型的高并发问题,所需的最关键字段就是库存,在高并发的情况下每次 ...

  2. redis分布式锁解决超卖问题

    redis事务 redis事务介绍:    1. redis事务可以一次执行多个命令,本质是一组命令的集合. 2.一个事务中的所有命令都会序列化,按顺序串行化的执行而不会被其他命令插入 作用:一个队列 ...

  3. 使用redis分布式锁解决并发线程资源共享问题

    众所周知, 在多线程中,因为共享全局变量,会导致资源修改结果不一致,所以需要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操作 但是在分布式架构中,我们的服务可能会有n个实例,但线程锁只对同一 ...

  4. 利用redis 分布式锁 解决集群环境下多次定时任务执行

    定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...

  5. 使用Redis分布式锁处理并发,解决超卖问题

    一.使用Apache ab模拟并发压测 1.压测工具介绍 $ ab -n 100 -c 100 http://www.baidu.com/ -n表示发出100个请求,-c模拟100个并发,相当是100 ...

  6. springmvc单Redis实例实现分布式锁(解决锁超时问题)

    一.前言 关于redis分布式锁, 查了很多资料, 发现很多只是实现了最基础的功能, 但是, 并没有解决当锁已超时而业务逻辑还未执行完的问题, 这样会导致: A线程超时时间设为10s(为了解决死锁问题 ...

  7. 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

    首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...

  8. Redis 分布式锁的实现

    0X00 测试环境 CentOS 6.6 + Redis 3.2.10 + PHP 7.0.7(+ phpredis 4.1.0) [root@localhost ~]# cat /etc/issue ...

  9. Redis分布式锁 (图解-秒懂-史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

随机推荐

  1. [转载].NET ASP.NET 中web窗体(.aspx)利用ajax实现局部刷新

    之前开发的一套系统中用到了大量的 checkboxList 控件,但是每次选定之后都会刷新整个页面,用户体验很差,百度了之后查到这篇文章,尝试了一下可以实现,所以转载了过来,记录一下,也给其他有相同困 ...

  2. Winform中设置ZedGraph的曲线为散点图

    场景 Winform中设置ZedGraph的曲线符号Symbol以及对应关系: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...

  3. Flask笔记:文件上传

    文件上传 enctype:在HTML中的form表单中form标签默认是`enctype="application/x-www-form-urlencoded"`,在文件上传时需要 ...

  4. python3测试网站网速

    一.运行环境 1.Windows 10 2.python 3.8 二.安装第三方库pycurl 1.先安装 pip install wheel 2.在安装pycurl https://download ...

  5. 十七:迭代器模式详解(foreach的精髓)

    定义:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示. 从定义中可以看出,迭代器模式是为了在不暴露该对象内部表示的情况下,提供一种顺序访问聚合对象中元素的方法.这种思想在JA ...

  6. 13 个 NPM 快速开发技巧

    摘要: 玩转npm. 作者:前端小智 原文:13 个 npm 快速开发技巧 Fundebug经授权转载,版权归原作者所有. 为了保证的可读性,本文采用意译而非直译. 每天,数以百万计的开发人员使用 n ...

  7. Linux-CentOS-Nginx安装

    原文转自 jerryhe326:https://www.cnblogs.com/jerrypro/p/7062101.html 一.安装准备 首先由于nginx的一些模块依赖一些lib库,所以在安装n ...

  8. 使用Visual Studio学习C语言

    注明:安装的是社区版,只写大部分步骤,做笔记之用.详细还需要看B站教程,https://www.bilibili.com/video/av59608520 一.安装软件 1.安装Visual Stud ...

  9. XSS-Stored

    存储型XSS (持久性XSS) 将恶意JavaScript代码存储在数据库,当下次用户浏览的时候执行 Low <?php if( isset( $_POST[ 'btnSign' ] ) ) { ...

  10. 虚拟机安装后配置IP地址

    BOOTPROTO=static   #启动的方式,dhcp表示自动获取,static表示静态ONBOOT=yes   #是否开机启动,yes表示开机启动IPADDR  #设置IP,和vmware在同 ...