游戏业务流量录制回放

前置知识

对于一个业务进程,其对外的交互大概有一下几种操作,1.从接入层收包处理,2. 写入或者读db拿数据包处理,3.与其他的进程交互接收其他进程的处理结果,也是通过收发包。如果用兼容的格式或者同样的框架写逻辑进程,收发包的地方可以抽象为 Recv和Send两个接口,定义一个通用类,每个逻辑进程继承通用的收发接口。

image

使用TBuspp可以继承框架的收发接口和处理包格式等接口,由于每个业务类都继承了框架的收发接口,录制和回放可以通过更改框架函数添加 自定义逻辑。按照现有的项目举例,业务实现的大厅服务器,好友服务器,对局服务器,每个服务器都继承了框架提供的 收发接口。NH使用TBuspp作为通信框架,每个进程直接继承或者继承于ITBuspp, 业务自己实现了一个 Wrapper封装,封装类里面会传入一个 handler, 每个handler传入的是每个业务进程类,比如CZoneServer,然后通过调用 handler的方法处理业务逻辑:

image

录制

NH处理tconnd、tcaplus、其他svr发过来的包都包装成了tbuspp格式,有一个格式转换或者增加了一个中间层,比如通过dbproxy读写tcaplus,其他进程通过TBuspp与dbproxy通信。在进程 接收外部数据包的地方,增加自定义的逻辑,可以按照时间序列保存所有上行包,按照同样的逻辑,也可以保存所有的下行包:
image

回放

先看原来收包的逻辑,svr_A往svr_B发送数据包,实际上是往svr_B的缓冲区发送数据包,svr_B收包调用recv的时候,也是从缓冲区去拿。其实系统调用send/recv对应的buffer对应的是网卡的buffer。
image

如果我们想回放之前保存的数据,在这个地方修改数据的来源,将数据包的来源改成一个我们可以控制写入的buffer.

使用之前录制的数据包作为数据源,用一个新的进程读文件,然后按照一定时间写入svr2的收包buffer,如上图svr2收包的地方,经过了改造,如果是回放模式,那么从特定的缓冲区收包,回放进程只要一直往这块buffer写入数据,业务进程就可以拿到 回放的包,按照业务逻辑处理。

image

回放随机数的改造:

以抽卡为例,传入的随机种子是固定的,固定为game_id,第N次运行的第M次随机和第N+1次运行的M次随机获得的随机数是完全一样的

image

每次对局将 m_uiRandomSeed = (unsigned int)(ulGameId ); 回放的时候传入的game_id相同,则每回合的抽卡是相同的。
那么就有一个问题:创建单局的时候game_id不能一样,不然创建不了相同的N局,game_id是对局的标志,是不能重复的,但是随机种子还要依据单局id去随机,这是一个冲突,怎么解决呢? 看这句 m_uiRandomSeed = (unsigned int)(ulGameId ); game_id是64位的,随机种子是取其低32位,还剩32位可操作。
随机在绝大多数还是非随机的逻辑,一般单局中抽卡,给装备,单局外抽奖、随机某个人或属性等玩法会用到随机,可以找或者构造随机中不变的部分,作为回放的标记种子。

时间的处理:

录制包的时候,存储的包头中有记录这个包发送的时间,默认不是测试环境GM调的时间,都是统一的正常时间。
比如玩家有活动或者赛季变更的时候,服务器的配置是赛季时间,收集数据包的是在两天前,刚过赛季,这时候判断赛季或者活动时间如果按照服务器当前时间,肯定时间是对应不上的,这个地方需要该系统的时间函数,并且在获取数据包的时候设置进程时间。

image

回放的时候更改进程时间:
image

应用

  • 可以采用老版本的协议,协议兼容性检测
  • 导入消息 过程中增加设置异常参数测试
  • 回归测试,使用之前人工的操作产生的 请求包
  • 难以复现的bug,复现
  • 调用链,时序分析
  • 代码覆盖率驱动
  • 性能测试、倍速播放
-->