微服务利用redission实现Redis分布式锁的一次问题定位

微服务利用redission实现Redis分布式锁的一次问题定位

背景

系统是微服务架构,一个服务部署多个节点。

某个业务由消息触发,可能出现多个消息触发多个节点同时处理同一个业务情况,最终导致这个业务表现不正常。

多个节点同时处理同一个业务,本质上是并发问题,一般处理方法是对临界资源加锁(这里同一个业务即是临界资源)。

因为是分布式系统,所以采用Redis实现分布式锁。

利用redission库封装的API实现,

String key = "业务唯一key";
return redissonClient.getFairLock(key).tryLock(0,10L, TimeUnit.SECONDS);

问题

加锁逻辑上线后,我发现偶尔会出现不同节点都加锁成功的情况。

定位问题

代码问题?
我仔细走查代码,加锁解锁正常;梳理逻辑,没有发现问题。

redission实现分布式锁问题?
我通过Google查询。发现没人对redission实现分布式锁有疑问。没人有疑问,不代表没有问题,对不对?
我继续查找redission问题一天,无结果…

第二天,找同事沟通这个问题,和他说了我的疑问。
他反馈:代码看不出来问题,redission应该没有问题,我们好多系统都用,没有出现问题。
同时,他提供了一个思路:会不会是这个业务处理快,前面节点很快释放锁,后面节点正常获取锁并处理?

带着这个思路,我利用arthas工具,在所有节点对这个业务加锁解锁操作进行watch,

watch cn.*.*.*.CarportReleaseCache lockCar '{params,returnObj}' -x 2 -n 999999 >> &

经过一段时间,找到一条被重复处理的业务,通过对比2个节点的业务日志和加锁解锁日志,我发现前面节点十几毫秒处理完业务,然后释放锁;后面节点获取锁,再次处理这个业务。
问题原因确实是业务处理快,锁释放快,不同节点先后都加锁成功,导致业务被重复处理

解决问题

分析业务流程
获取待处理数据 -> 加锁 -> 处理数据 -> 更新数据处理状态 -> 解锁

“获取待处理数据”不在锁范围内,不同节点都可以先获取到待处理数据,然后才竞争锁,这样有几率出现重复处理问题。

方案一,扩大锁范围,将“获取待处理数据”纳入锁范围。
修改后流程:加锁 -> 获取待处理数据 -> 处理数据 -> 更新数据处理状态 -> 解锁

这个方案不能再使用原来的记录锁,需要对“待处理数据”的共同标记加锁,能解决问题但会降低系统并发性能。

方案二,加锁后处理前,对数据状态判断。
修改后流程:获取待处理数据 -> 加锁 -> 获取数据处理状态 -> 状态是未处理 -> 处理数据 -> 更新数据处理状态 -> 解锁

这个方案改动小,“获取数据处理状态”需要访问DB,会有些损耗,但可以接受。

权衡利弊后,选择方案二。

修改上线后,业务正常。

收获

  • 分析问题,思路很重要。思路错了,就差之毫厘谬以千里。

  • 遇到问题,多与人沟通,说说自己思路。有利于拓宽思路。

  • 自己对并发问题理解不够深入。事后来看,分析业务流程,画出流程简图,比较容易找到思路,定位问题。

你可能感兴趣的:(Spring使用,Redis分布式锁,redission,重复处理,微服务,多实例)