这是悟空的第 170 篇原创文章
官网:www.passjava.cn
你好,我是悟空。
本文主要内容如下:
最近项目的生产环境遇到一个奇怪的问题:
现象:每天早上客服人员在后台创建客服事件时,都会创建失败。当我们重启这个微服务后,后台就可以正常创建了客服事件了。到第二天早上又会创建失败,又得重启这个微服务才行。
初步排查:创建一个客服事件时,会用到 Redis 的递增操作来生成一个唯一的分布式 ID 作为事件 id。代码如下所示:
return redisTemplate.opsForValue().increment("count", 1);
而恰巧每天早上这个递增操作都会返回 null
,进而导致后面的一系列逻辑出错,保存客服事件失败。当重启微服务后,这个递增操作又正常了。
那么排查的方向就是 Redis 的操作为什么会返回 null 了,以及为什么重启就又恢复正常了。
根据上面的信息,我们先来看看 Redis 的自增操作在什么情况下会返回 null。
根据重启后就恢复正常,我们推测晚上执行了大量的 job,大量 Redis 连接未释放,当早上再来执行 Redis 操作时,执行失败。重启后,连接自动释放了。
但是其他有使用到 Redis 的业务功能又是正常的,所以推测一的方向有问题,排除。
可能是 Redis 事务造成的问题。这个推测的依据是根据下面的代码来排查的。
直接看 redisTemplate
递增的方法 increment
,如下所示:
官方注释已经说明什么情况下会返回 null:
事务提供了一种将多个命令打包,然后一次性、有序地执行机制.
多个命令会被入列到事务队列中,然后按先进先出(FIFO)的顺序执行。
事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕之后,事务才会结束。(内容来自 Redis 设计与实现)
继续看代码,发现在操作 Redis 的 ServiceImpl 实现类的上面添加了一个 @Transactional 注解,