研究背景:

这几天被支付宝充值后通知所产生的重复处理问题搞得焦头烂额, 一周连续发生两次重复充钱的杯具, 发事故邮件发到想吐。。为了挽回程序员的尊严, 我用了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. Python - 正则表达式2 - 第二十三天

    Python3 正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. ...

  2. Python破解js加密实例(有道在线翻译)

    在爬虫爬取网站的时候,经常遇到一些反爬虫技术,比如: 加cookie,身份验证UserAgent 图形验证,还有很难破解的滑动验证 js签名验证,对传输数据进行加密处理 对于js加密经过加密传输的就是 ...

  3. 前端页面 title keyword description的添加

    用法:<title>网站标题</title> 用法:<meta name=”Keywords” Content=”关键词1,关键词2,关键词3,关键词4″> 用法: ...

  4. iconfont采坑

    1. iconfont采坑 1.1. 前言 使用iconfont过程中踩过坑特此记录 不知道iconfont的这里也简单介绍一笔,阿里开放的一个图标素材库,用来快速找图标下载使用图标 iconfont ...

  5. python3偏函数

    Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function).要注意,这里的偏函数和数学意义上的偏函数不一样. 通过设定参数的默认值,可以降低函数调 ...

  6. openssl生成随机数

    #include <stdio.h> #include <openssl/bn.h> int main() { BIGNUM *bn; bn = BN_new(); //生成一 ...

  7. EFLAGS寄存器(标志寄存器)

    这篇文章不是从0开始的,前面还有一些汇编基础指令以及进制,我都没写,时间问题,还是今天空闲,我才想补一下博文,后面我陆续会把前面知识点渐渐补上.我不会重0基础讲起,中间会以.汇编.C.C++交叉的形式 ...

  8. (转)Unity与3ds Max的单位关系(使用FBX文件)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/a1780531/article/deta ...

  9. Google Analytics 学习笔记四 —— GA的Channels划分规则

    一.流量的Source.Medium和Campaigns 1.Source Source或traffic source通常表示流量的来源,一般通过urm_source参数跟踪 在GA中,流量来源的名字 ...

  10. [20190507]sga_target=0注意修改_kghdsidx_count设置.txt

    [20190507]sga_target=0注意修改_kghdsidx_count设置.txt --//昨天遇到一例视图定义太复杂导致长时间分析sql语句出现library cache lock等待事 ...