研究背景:

这几天被支付宝充值后通知所产生的重复处理问题搞得焦头烂额, 一周连续发生两次重复充钱的杯具, 发事故邮件发到想吐。。为了挽回程序员的尊严, 我用了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. 千万级MySQL数据库建立索引,提高性能的秘诀

    实践中如何优化MySQL 实践中,MySQL的优化主要涉及SQL语句及索引的优化.数据表结构的优化.系统配置的优化和硬件的优化四个方面,如下图所示: SQL语句及索引的优化 SQL语句的优化 SQL语 ...

  2. java基本程序设计结构总结

    学习一门语言:(1)掌握它的表现形式(2)这些语言什么应用. 1.1关键字 1.关键字是被赋予了特殊含义的单词. 2.关键字特点:关键字所有字母都小写. 3.类名的每一个单词开头必须大写. 1.2标识 ...

  3. C#关键字 const与readonly

    ====const==== const关键字来声明某个常量字段或常量局部变量.常量字段和常量局部变量不是变量而且不能修改.常量可以为数字.布尔值.字符串或null引用. 常数声明的类型指定声明引入的成 ...

  4. Django 练习班级管理系统八 -- 上传文件

    Form表单上传文件 修改 views.py import os def upload(request): if request.method == 'GET': img_list = models. ...

  5. Rust的多线程程序

    一定要用闭包? 感觉有点过了. use std::thread; use std::time::Duration; fn main() { let v = vec![, , ]; let handle ...

  6. 201871010121-王方-《面向对象程序设计java》第十六周实验总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p/ ...

  7. 201871010125-王玉江《面向对象程序设计(java)》第十五周学习总结

    项目 内容 这个作业属于哪个课程 <任课教师博客主页链接> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址> ht ...

  8. UiPath:Log Message: Column 'user_role' does not belong to table .非说这个列不存在,记录一下看看怎么解决

    如图 确实是不存在...因为Excel的取值范围没有囊括user_role列...

  9. 阿里云serverless使用笔记

    1.使用api网关服务,创建完api后,测试时,需要传“请求签名”,否则会报401 ‘Empty Signature’错误.相关文档<错误编码表,请求签名>.(错误信息放置与响应头的‘x- ...

  10. shell的变量以及常见符号

    shell的变量以及常见符号 常见变量 不同于其它语言需要先声明变量,shell的变量直接使用 eg:      a=15 调用变量的话 $a 或者 ${a} $? #判断上一条命令执行的是否成功 0 ...