线上消息丢失问题

Posted by ni on August 27, 2025
本文共1031 字 | 大约需要3.44 分钟阅读

前言

  • 在经过一段时间的工作后,项目中的老功能出现了问题
  • 并且这个问题是概率触发,内网无法复现
  • 接下来简单介绍一下功能

相关介绍

  • 首先在游戏项目,并且很少涉及到玩家与玩家之间的交互,对玩家的数据做了分离,这样做的好处
  • 1、减少高并发问题
  • 2、玩家只能对自己的数据进行控制,数据安全
  • 3、可以对操作数据直接缓存到redis,每次调用接口获取数据首先获取redis,再获取数据库,在接口中操作的数据可以在接口请求结束后统一修改redis和数据库,不用再编写保存方法。由于玩家只能对自己数据进行操作,并且客户端的请求要等待响应才能进行下一步操作,基本不用担心并发问题
  • 但是有些操作需要涉及到玩家交互,例如玩家1向玩家2发送请求,玩家2需要收到消息
  • 当时开发人员使用redis设计了类似消息队列来实现该功能,玩家1发送请求,此时记录玩家1已经发送过了就不能再进行发送,并且发送一条请求消息到redis中,玩家2在进行一些相关接口请求时会对消息队列里的消息进行消费落库

问题

  • 但是线上出现了玩家1发送消息给玩家2,并且对发送状态进行了记录,可是玩家2并没有收到消息

猜测

  • 1、既然是概率问题猜测可能是并发问题,查看代码发现原来的逻辑是获取数据,消费数据,删除数据;获取和删除是分开操作并且该方法多处调用 解决:使用lua脚本进行获取和删除操作 结果:上线后发现仍有问题出现

  • 2、经过了解,客户端和服务器之间请求对于一个玩家来说请求是串行的,基本不会出现并发问题,但是在服务器报警群中经常出现save version is error ,查看底层,在保存到redis中时会使用lua脚本,使用乐观锁的版本控制,当获取的版本和保存时的版本不同时就会报错,并且保存失败。由于前面说过请求是串行的不会因为并发问题而报错,通过询问和查询了解会因为线上数据库卡断和redis的网络波动导致,猜测可能因为这个原因导致落库失败 解决:由于是框架上和网络以及数据库问题,通过添加保底方案来解决在每次发起方获取列表时重新根据发送状态再次进行发送,并对重新发送的时间进行控制, 消费方如果有多个相同的请求玩家就进行过滤。 结果:线上问题解决。

总结

  • 开发需要考虑的逻辑很重要,但考虑问题也很重要,即使逻辑正确但是在环境不稳定的情况下依旧会出现问题
  • 所以对于一个功能的实现,需要尽可能的使用效率高的,简单的,可复用的实现。对于重要的功能还需要进行保底