RCA:分布式锁bug导致支付网关拿到同一个锁
Posted 云纵达摩院
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RCA:分布式锁bug导致支付网关拿到同一个锁相关的知识,希望对你有一定的参考价值。
研发中心 于增智 201803
背景:
dlock 是2013年我司自行开发的基于 Redis 的分布式锁 Java 实现。
问题现象:
支付网关昨天出现队列写库唯一索引冲突的异常,目前该问题不会影响支付业务,只会输出异常日志。
问题原因:
由于最近也没有上线,随怀疑是分布式锁没有锁住的问题。于是排查了下 dlock 的日志和代码,发现问题。
08:16:26,781 [resin-port-10340-52] ERROR D.LOG [//T1803070……00/] - [{"handleCost":0,"errorMsg":"can not get resource!","_serialId":1,"_method":"RedisConnectionFactory.getConnection()#1520……37#","url":"redis1-1.y……01"}]
dlock 可以配置多个 redis 实例,通过 failover 机制,当访问失败时自动切换主池子实例,这个机制在主 redis 实例完全挂掉的情况下没有问题,但是主 redis 实例没有挂掉,而只是业务服务的某台机器访问出现问题的情况下,就会出现类似于“脑裂”的现象。
比如支付网关两台节点A和B,两台 redis 实例分别为R1和R2,正常情况下,AB均访问主实例R1,
A->Dlock->主R1
B->Dlock->主R1
当B服务从R1获取 redis 连接失败时,会自动切换到可用的节点R2,并设置R2为当前实例,此时访问情况就变成了
A->Dlock->主R1
B->Dlock->主R2
由于 dlock 加锁是先通过主实例 setnx 判断并写入键值,然后再写入其他实例,这两步操作并非原子级操作,所以就会出现同时拿到锁的情况。
解决办法:
需要修复 dlock 的 failover 机制。
假如真的发生了脑裂,支付网关通过两条措施确保不会重复执行(幂等性):
1、队列唯一索引:通过交易号+队列类型+key+业务标识等进行 md5 计算出来的值,防止重复执行。
2、在写队列前,都会加分布式锁。
经验教训:
让客户端判断远端集群哪一个节点不可用,就会导致脑裂。当年 Cobar 就出现过这个问题。
-EOF-
欢迎关注云纵达摩院
不管什么行业,IT 技术已经渐渐成为支撑许多公司产品的骨架,但多数这些公司的领导对于 IT 人才的重视依然不够,没有技术积累。这类公司终究会遭受到短视的后果,最明显的是三点:1. 技术滞后拖累产品创新的步伐 2. 系统运营成本居高不下,侵蚀公司获利 3. 系统不稳甚至故障频发,为公司带来灾难。
——蔡学镛
以上是关于RCA:分布式锁bug导致支付网关拿到同一个锁的主要内容,如果未能解决你的问题,请参考以下文章