数据库
首页 > 数据库> > 记一次 Redission 的 Lock.tryLock(500, TimeUnit.MillSecond) 导致的线上Bug。

记一次 Redission 的 Lock.tryLock(500, TimeUnit.MillSecond) 导致的线上Bug。

作者:互联网

    前不久产品给提了一个Bug,说他在说feed 流的时候,有时候会突然刷到空内容,而且之后无论如何刷新,总也刷不出内容。我心里一想,卧槽,这么严重,立马提升bug为高优,并着手去修复。

 

业务背景介绍: 业务背景大概这这样, 在一个 关注Tab 页面下,用户也可查看用户关注人的一些动态信息Feed流,。前端只是去请求固定的接口,后端需要调用另一个系统去阿里云TimeLine  引擎分页查询用户的Feed 流,查询结果进行返回。当然 线上是多台机器部署,并使用SLB 进行负载均衡。

大致了解接口逻辑之后,便着手进行查询,我首先查询线上日志信息是否有报错,从Info 日志开始查询,得益于项目该打的日志打的很全,所以一会的功夫,搜索了一遍并没有发现有任何的异常信息(当然找不到,因为本来就没有收到什么报警通知),然后只能我去模拟这个用户 进行多次的尝试, 发现偶尔确实可以复现,查看前端请求后端接口 返回的数据确实为空,但是后端我单独去查,确实是有数据的呀,这让我非常郁闷,没办法,只能看代码了,看了很久,感觉也没有什么收获,一看表, 都九点多了,没办法,赶紧走吧, 这时我突然注意到 这个地方使用了 redis 的分布式锁,是不是这里有问题呢,伪代码大概如下。

 

RLock lock = redissonClient.getLock("distrubuted_lock" + UserId);
        try {
            if(lock.tryLock(100, TimeUnit.MILLISECONDS)) {
                try {
                    
                    // http请求 TimeLine 获取Feed 流数据
                   return Result;
                }catch (RuntimeException e){
                    logger.warn("【服务异常】",e);
                } finally {
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            logger.error("【分布式锁服务异常】",e);

        }
       
        return SuccessResponseData.getSuccessResponseData(null);

看到这里,我隐隐约约觉得哪里好像不太对,但是又说不上来,哎。 突然脑子一闪,为什么获取不到 分布式锁就返回为空呢?

稍微思考了一下,首先 虽然一个请求只能被SLB 转发到一台机器上,但此处用了分布式锁,意义应该就是有一些操作同时只能有一个地方进行操作,是为了保证该地方的强一致性,所以添加了分布式锁,并使用了 有超时时间的tryLock 。

深入思考了一些,此处应该存在以下的问题:

  1. jvm 执行垃圾回收,导致 虚拟机暂停该线程,或者 更极端的导致 STW 情况出现, 从而导致无法正常获取锁。
  2. 由于 redis 是单线程的,如果在获取锁的时候,此时有一个需要执行时间颇长的任务,例如 拉取一个大对象,存储一个大对象,复杂逻辑操作等等, 都会导致无法及时的相应当前获取分布式锁的操作,从而导致获取失败,返回空数据列表。
  3. 考虑到极端情况,如果用户同时发出了两个请求,一个请求先到一台服务器,获取到了分布式锁,另一个请求到了另一台请求,尝试获取分布式锁,获取失败,返回空数据,并且此请求返回的速度要快于获取到分布式锁的请求,所以前端会接收到空数据,显示空白页。

考虑以上种种请求,解决方案有如下几种:

考虑实现的难度以及成本,最后选择了第二条, 观察一段时间,如果还能出现的话,那么可能就会重视起来,要从根本是解决了。

观察中。。。。。。。。。。。。。。。。。。。。。。。

 

标签:返回,请求,MillSecond,Lock,查询,获取,Redission,lock,分布式
来源: https://blog.csdn.net/ZHAO_Beer/article/details/100165324