当前位置:首页 > 问答 > 正文

Redis队列消息突然取不到了,排查各种可能原因和解决办法分享

本来跑得好好的Redis队列,突然之间就取不到消息了,系统没有明显的报错,但队列里的任务就是卡住不动了,导致后续流程全部中断,经过一番折腾,终于把问题解决了,这里把排查过程中想到的和实际遇到的各种可能性以及解决办法分享一下,希望能帮到遇到类似情况的朋友。

第一点,最应该先看的地方:消费者是不是还活着?

我当时第一个反应就是,是不是处理消息的程序(也就是消费者)自己挂掉了?比如因为代码里有未处理的异常崩溃了,或者部署的服务器重启了没人知道,这就像工厂的传送带还在转,但是流水线上的工人睡着了,东西自然就堆在那里了,解决办法很简单,直接去服务器上看看消费者的进程还在不在,或者查一下应用的日志文件,有没有最新的错误记录,如果真是进程挂了,重启服务是最快的办法,但更重要的是要找出崩溃的原因,比如是不是某条特殊的消息导致了程序处理不了,从而完善代码的健壮性。

第二点,检查一下网络连接,是不是Redis和你的应用“失联”了?

Redis队列消息突然取不到了,排查各种可能原因和解决办法分享

有时候问题不出在Redis本身,也不出在应用代码,而是它们之间的网络出了问题,比如机房网络抖动、防火墙规则被意外修改、或者Redis服务器迁移了地址但应用配置没及时更新,你可以尝试用ping命令测试网络连通性,或者用telnet命令测试Redis的端口(默认6379)是否能通,如果网络不通,那就需要联系运维同事一起排查网络配置了。

第三点,消息是不是被其他客户端或者人给误删了?

这种情况在多人协作的开发或测试环境尤其常见,可能是有其他同事为了清理测试数据,手动连上了Redis,执行了一个FLUSHDB或者DEL命令,把整个数据库或者特定的队列给清空了,也有可能存在另一个配置错误的消费者程序,它也在监听同一个队列,把消息取走处理(或者取走没处理),这时候可以去查一下Redis的慢查询日志(如果开启了的话),或者询问一下团队成员是否有过相关操作,对于生产环境,严格管理Redis的访问权限非常重要。

Redis队列消息突然取不到了,排查各种可能原因和解决办法分享

第四点,关注消息是否陷入了“处理中但未完成”的黑洞。

如果你使用的队列模式是BRPOPLPUSH这类需要先将消息转移到一个“处理中列表”的,就要特别小心,消费者从主队列取出消息,放入处理中列表,然后开始处理,如果消费者在处理过程中崩溃了,这条消息就会永远躺在处理中列表里,既不会被其他消费者处理,也不会回到主队列,这就像快递员从分拣中心拿了一个包裹准备派送,结果半路车坏了,这个包裹就“失踪”了,解决办法是建立一个监控机制,定期去扫描这个“处理中列表”,如果发现有些消息在里面停留时间过长,就把它们重新放回主队列,这也就是所谓的“死信队列”处理思想。

第五点,一个不太常见但很致命的原因:内存不足,导致数据被Redis的淘汰策略删掉了。

Redis队列消息突然取不到了,排查各种可能原因和解决办法分享

Redis是内存数据库,如果服务器内存不足,并且你配置了maxmemory-policy(内存淘汰策略)为allkeys-lruvolatile-lru等,当内存达到上限时,Redis可能会把一些旧的或者不常用的键(包括你的队列)给删除掉以释放空间,这时候你的消息就真的永久消失了,你可以通过info memory命令查看Redis的内存使用情况,如果确实是内存不足,要么增加服务器内存,要么优化数据结构,要么检查是否有内存泄漏,或者调整淘汰策略(但这只是治标不治本)。

第六点,检查Redis服务本身的健康状况。

虽然不常见,但Redis服务器本身也可能出问题,比如CPU占用率100%、磁盘写满(如果开启了持久化)、或者Redis进程僵死,你可以通过redis-cli连接后执行info命令,查看一下服务器的整体状态,比如connected_clients(连接数)、used_memory(内存使用量)、latest_fork_usec(上次fork操作的耗时,如果持久化时fork卡住也会影响服务)等指标是否正常,如果Redis服务异常,可能需要重启Redis实例。

别忘了看看客户端的代码逻辑。

有时候问题就出在我们自己写的代码里,消费消息的代码逻辑被修改了,可能不小心把循环阻塞获取消息的代码注释掉了,或者消费逻辑里增加了一个条件判断,导致当前队列里的所有消息都不满足条件,所以看起来像是没取到消息,这种问题需要通过代码审查和日志调试来定位。

当Redis队列取不到消息时,不要慌,可以从简到繁地排查:先确认消费者进程存活,再检查网络连通性,然后考虑是否有误操作,接着深入检查消息处理逻辑和Redis服务器状态,建立一个完善的监控告警系统,对队列长度、消费者延迟等进行监控,能在问题发生的早期就发出警报,避免更大的损失。 参考了常见的系统故障排查思路以及Redis官方文档中关于持久化、内存管理、命令使用的说明,并结合了实际的运维经验。)