前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >游戏后台开发共性问题和解决方法(1)

游戏后台开发共性问题和解决方法(1)

原创
作者头像
谢盼
发布2023-06-06 21:32:29
4880
发布2023-06-06 21:32:29
举报
文章被收录于专栏:视频AI视频AI

1. 玩家数据、公会数据等如何保证一致性?

玩家数据比如经验、等级,公会的数据比如成员列表、活跃度、等级等,会有很多触发更改的时机,这些修改可能在不同的模块触发。

数据读取不存在一致性的问题,最多读到的新数据还是旧数据而已,即使读到旧数据,在下次刷新的时候也会读到新的数据,问题不大。

数据写的时候,如果是(1)读(2)内存更新(3)写 (4)读(5)更新(6)写的数据,没有问题;但如果是并发写不是这个顺序的话,数据大概率就乱了。

最简单的解决办法是使用版本号机制,在回写的时候如果旧的版本号对不上,就意味着在你的读和写之间有其他玩家执行了写操作。这种情况下,让写失败并进行重试,直到成功为止。

但是使用版本号机制有个问题,就是重试比较麻烦,甚至有时候执行重试逻辑是不可行的。所以单纯的版本号机制还不行,需要对请求进行按照某种key排队,这种排队可以保证对kv中特定key的读写是串行的。而按照某种key进行排队有效的前提是,对db中某个表(某个类型)的数据集中在1个特定的模块进行处理,其他模块最多只允许读,不允许写。

但是收归到一个模块之后又有一个问题,相同的模块可能会有多个实例,如果同一个key无法路由到固定的模块实例,仍然有可能出现并发读写的情况,这个时候,一致性哈希路由上场了。微服务寻址时,上游模块指定路由的key,一般使用玩家id/公会id等,保证请求发到模块的相同实例。

2. 任务、活动定时刷新的问题

游戏的策划案中,经常会有一些定时刷新的需求。比如每日任务,在领取之后,不管进度如何、是否完成,都需要在凌晨某个时间点把玩家的任务领取数据清除掉。

比如要求每日任务在凌晨4点刷新,刷新之后,如果没有完成需要重新领取,才能去继续完成任务获得奖励;如果已经完成,也要清除掉完成记录,支持再次领取后继续执行。

玩家在白天11点领取了每日任务,但是凌晨3:59才开始做任务,如果在凌晨4点不能把领取的任务清除掉,那么玩家就会可以继续做任务,完成后获得奖励,与策划要求不一致。

要在4点准时刷新,有几种方法。

第一种比较直接,就是后台模块在统一刷新的时间,遍历所有用户数据进行刷新。但是游戏的存储普遍是使用KV数据库,KV的库对于遍历key都存在严重性能问题,并且短时间内大量的db操作尖峰,可能会导致其他模块对db的读写操作报错。难以接受。

第二种方法简单些,就是在线玩家实时刷新+离线玩家登陆时刷新。在线玩家实时更新有三种触发方法,一种是大厅实例直接遍历本大厅维护的在线玩家,一种就是通过广播机制由大厅通知在线客户端,在线客户端向后台模块发刷新请求,最后一种是由客户端自己定时,在定时时间向服务端发刷新请求。三种方法由不同的利弊。由客户端配合做刷新是优选,客户端可以在一个定时范围内做随机,避免形成请求尖峰。一般刷新的时间是凌晨,在线玩家也不多,服务器可以承受。

第三种方法是推策划改需求或接收有损的刷新,将刷新操作延时到用户重新登录或者重新请求该模块,这样一来可以平滑流量,二来逻辑比较清晰。比如将任务的入口进行统一,在玩家进入任务入口的时候进行刷新。同时要求策划适当接受有损的服务,接受少量未及时刷新带来的副作用。

3. 有状态服务的容灾

最简单的,就是想办法把有状态的服务改造成无状态的服务,尽量减少有状态的服务模块。这个怎么改造呢,一般都是借助KV,避免内存中保存数据。这个其实是把容灾的活交给KV,现在的KV,牛逼点的都是百万千万读写QPS的量级,问题不大。KV是标准化组件,有问题怨基础组件团队或者云产商好了。

但是,有些模块必然会有比较大的数据,比如对局匹配的模块,必然需要在内存中缓存各种玩家的匹配请求,否则性能上、延时上无法接受。这种模块的容灾,就是比较通用的套路,单机上使用共享内存存数据支持快速拉起,拉起后利用共享内存的数据继续干活;在模块上就是热备容灾,玩家进出池子、请求同时发到所有主备实例上,但是只有主实例会实际输出应答。

这里的主备容灾又有不同的实现方法,借助各类KV、Zookeeper等都可以实现选主。

有状态的服务一般使用k8s的statefulset部署,这个statefulset有个问题,就是当机器挂掉的时候,无法在新的节点上重新拉起,原因是k8s master在node挂掉的时候,无法确认故障原因是什么,也不清楚statefulset的实例是否还在运行,如果强行拉起新的,可能出现PV重复挂载等严重问题。

所以statefulset的容灾只能业务、运维自己实现,充分的冗余+至少跨机房的容灾部署+实时监控告警,及早请开发、运维大佬介入。deployment就没有这个问题,所以尽量使用deployment部署。

4. 服务部署中的大镜像问题

游戏后台模块多,可能服务模块打包后有好几个GB的大小,特别是在开发测试阶段,没有使用编译优化,CPP编译出来的可执行文件可能有几百MB,里面有符号表和各种调试信息。

如果把服务模块打包到docker的镜像中,会导致镜像非常大,而且只要代码有更新,镜像就需要更新。

在版本发布更新的时候,成百上千台机器同时拉取镜像,镜像仓库可能扛不住直接挂了,或者被限速几个小时才完成更新也是有可能的。当然,这种操作方式有好处的,简单、便于流水线化、不易出错。

但是如果上面的问题无法解决,那么就必然要求基础镜像和服务器资源包分开,基础镜像不更新,服务器资源包通过运维工具提前分发到节点上。通过文件挂载的方式,将资源包映射到容器内部。

这个分发也可以使用其他方式完成,比如借助一些存储分发工具比如FTP,容器启动脚本主动从FTP拉取。这种方式容易操作上容易出错,旧文件残留等,这需要把操作标准化,发布更新的时候严格按照步骤来,并且及时check各个步骤结果是否符合预期。

5. 服务的任意重启

可以任意重启的服务 与 不能任意重启的服务,执行更新的难度差别很大。可以任意重启服务,意味着不丢包、不丢请求。但是如果存在网络连接,那么大概率是无法任意重启的,需要先禁用模块把流量切走,还需要把存量的连接也断掉,这会影响到玩家的体验,并且操作起来也步骤多并且麻烦。解决的方法就是网络连接维护 和 业务分离为两个独立进程,两者通过共享内存的方法进行包的消费。在业务进程需要重启的时候,首先停止消费包,然后在所有请求处理完成后执行重启即可。共享内存要求有一定的容量,能够缓存短时间内的请求。

但是共享内存也只能缓存一段时间的请求,如果服务没有快速再次拉起并处理请求,这些请求可能都会超时。这些超时由其他上游模块或者客户端执行重试。

如果服务长时间没有拉起来,会体现在为服务的路由表上,新的流量不会在转发到该连接进程上。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 玩家数据、公会数据等如何保证一致性?
  • 2. 任务、活动定时刷新的问题
  • 3. 有状态服务的容灾
  • 4. 服务部署中的大镜像问题
  • 5. 服务的任意重启
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档