三国志战略版服务器卡顿问题

https://blog.codingnow.com/2019/10/sanguo.html

我们的新作品 三国志战略版 上线有一小段时间了。市场反应不错,获得了许多玩家。随着玩家数量增加,服务器也产生了严重的卡顿问题,在每天高峰期尤其严重。

这个产品的服务器开发团队在立项之初并没有接触过 skynet ,可以说是从头学习起,在很短的时间内就完成了项目,还是很不错的。我没有参加过这个项目的开发,在问题显露的那几天正巧国庆假期在国外度假,远程参与了一些讨论。帮助分析了问题,等休假完毕后,昨天又开了一整天的会,大致了解了游戏的玩法(需求),结合前两天的思考,给了几套不同的改进方案。

目前遇到的直接问题是,skynet 中有个巨大的服务,管理了整个游戏场景的数据,大约有 20G 。所有的地块、部队、建筑对象都在这个服务中。且注册了大量的 timer 用来更新这些对象。最终导致在游戏繁忙时,该服务会以大约每分钟 500M 的速度生成临时数据。

这给 gc 带来的极大的负担。gc 会造成该服务的卡顿。而其它业务逻辑反而不太占用 cpu 。

通过监控数据的分析,我认为,gc 的原子操作阶段时间过长是罪魁祸首。这个阶段是不可分割的,真正的 stop the world 。而导致这个步骤过长的原因是,该服务大量使用了弱表。当弱表项高达几十万时,清理重置被影响的弱表,就需要很长的时间。

而实现中几乎把所有的对象都关联在了弱表中,仅仅是为了追踪每个类型的对象在内存中的存活情况,方便排查内存泄漏。我认为这是对弱表的滥用。在真的有这类需求时,通过遍历 vm 一样方便查找,不必为了监控而加大 gc 的负担。

去掉这些无谓的弱表后,情况得到了改观。

另外,一个意外的发现是,在 gc 的 sweep 阶段,每个 step 消耗的时间是 mark 阶段的 10 倍。这让我颇为不解。因为 sweep 的工作仅仅是遍历 gcobject 的双向链表而已。每轮 gc 大约有 1/6 的垃圾需要回收。最多的时间消耗在遍历已有对象上。我的解释是,lua gc 的步进单位,mark 阶段是用对象大小估算的,而 sweep 阶段,每个对象则是一个固定值(GCSWEEPCOST)。其实,mark 一个对象和 sweep 扫描一个对象的成本其实相差无几,尤其是在消耗内存很大时,内存 cache 几乎无效,此时,sweep 阶段的一个步骤就真的比 mark 阶段多访问了大约 10 倍的内存。

解决这个问题,我认为应该结合我们的实际情况,将 GCSWEEPCOST 调大,平衡 gc step 的停顿时间。

在这个项目中,采用的是定时主动 gc step 的策略,而不是默认用分配内存器推动 gc 。我认为,在内存使用情况有明显规律的情况下,通过调整默认参数效果更好。目前我们服务器总共用了 40G 内存,而硬件配备了 128G 内存,这显然是浪费。不如把 gc pause 调大,减慢 gc 单轮的周期,让长期 gc 的总开销减少:因为,gc 越激进,不断地遍历 vm 是一种浪费。


不过,我认为根本原因是开发者没有好好的设计服务器的结构,制造出一个数十 G 的单个 vm (实现也有极大的优化空间,不过这个需要有长期的 lua 使用经验,没有简单的银弹去优化)是根本问题。在 skynet 的结构下,我们通常倾向于合理的切割服务,避免出现单个负担过重的服务。

经过对游戏规则的了解,我意识到源头是策划设计时的含糊不清。在我看来,这类游戏本质上是一个回合游戏。它的大部分事件都以分钟为单位,和过去的类似游戏不同,这次策划让部队在行军时真正在棋盘格上移动,部队之间可以在路途上相遇发生遭遇战。这有点像一个拖慢了的 RTS ,但行动依然是按数秒左右为单位的。

如果把游戏简化为回合,那么规则上就应该明确出同一回合在同一地点发生的事件如何决定次序。但现在游戏规则是没有定出次序,靠程序在处理时的天然持续决定。我们在实现中使用了大量的定时器,事实上这些次序无法确定。这给测试,业务分割都造成了麻烦。

如果拿桌游比较,任何桌面游戏在行动回合中,都会规定不同的事件的处理次序,结算规则。而这个只是普通战棋的放大版,却没有统一的结算规则。依靠着程序处理的次序来决定,QA 感觉是对的就是对的,感觉有问题就添加一些例外处理。不同服务间同一时刻 timer 消息的先后不一致,业务种类(行军,对战等)处理复杂度的不同,相关服务的不同时期的负载不同,都会引起相同初始量导致的结果不同。


在了解完现状后,我笑道,现在策划其实给了服务器极大的自由,各种结算次序都不太所谓了。只要看起来正确就行。有什么理由不直接把单一场景拆分成多个服务呢?如果这样做,无非面临三个问题:

第一,部队可能在某个时刻从一个场景格移动到另一个场景上的格子。对于这种跨服务行军,简单的修改成服务间远程调用即可。如果不做额外的工作,的确存在一些一致性问题。

比如,如果一只军队从 A 场景服务的边界移动到 B 场景服务;同时另一只军队从它的目的地移动到 A 。两件事情同时发生时,他们就错过了。而在同一场景下,由于移动都是串行处理的,所以不会错过。

但实际上,现在由于使用了大量不确定次序的定时器,以及将寻路,战斗计算等分离,也存在某些边界情况没有考虑。由于策划在游戏规则上并没有严格的按回合推演,其实有大量的小概率不符合规则的例外都被容忍了(原因可能是计算服务的负载过高,或是同时刻定时器的执行次序不确定等)。换句话说,新出现的实际情况和规则的不一致,严重程度并没有超过原有的情况。

当然,如果肯花心思,上面这个问题是可以解决的,这里就不展开。

潜在的更重要的问题是多场景服务的数据落地。如果涉及军队对象的迁移,就可能发生军队对象同时存在在多个服务的情况。但这个问题是好解决的,只需要给军队对象加一个版本号,发生迁移时递增版本就可以防止多份对象数据同时落地的冲突。(落地服务永远以最新版本号为优先)

第二,某些大建筑会覆盖多个地块格,如果恰巧在边界上处理起来会比较麻烦。

比较简单的方法是:在修建跨边界的大建筑时,先由一个场景去另一个场景索要地块的管理权。能修建筑的都是空地,所以不存在数据的迁移,仅仅是所有权的转移。在所有权转移之后,再把建筑盖下去即可。

第三,同盟关系需要共享。

这个很容易解决,只需要相互同步即可。同步时效性也不是那么重要。

总结一下就是,把场景切分开,分到多个服务中并不是太困难的工作。带来的一致性问题会有,但出现最坏的情况并不比现在的设计下的潜在问题更糟糕。所以它是一个可行的,可以很快实施的方案。


不过,如果让我从头做设计,我肯定不喜欢现在的方案。

首先,我认为这个游戏本质上就是一个电子化的战棋类桌游。应该有严谨的结算规则。

比如群星,它的游戏推演规则其实有两个不同的周期。单位的移动和战斗是按天威单位推演的,发展和资源结算是按月结算的。一个月内的数值变化都不会在中途影响结算,而仅以月末的状态来决定。这就是很好的简化,让核心规则可以更加严谨。

我们这个游戏也一样,如果抛开行军,其它机制的结算周期都是以分钟这个数量级为周期的,可以说是推演的非常缓慢。只要我们规则制定清楚,无非就是给每个地块一个事件发生队列,以分钟这个长周期步进。同一回合内,不同的事件应该有严格的结算次序:屯田,占领,战斗……这样有一个规则明确的结算序列,才好做到同一输入可以得到确定的输出,从软件角度讲,也更利于测试;从玩家角度讲,战略规划也更加清楚。

而且,所有的地块都是相互独立的,非常适合切分,独立运算。由于大家都遵循一致的回合数,同步规则也相当清晰:每个回合必须所有地块都处理完,再进入下个回合。少数建筑会影响多个地块,比如防御箭塔,简单复制到受影响的每个地块即可。跨地块的大建筑也可以简单的生成多个副本,仅仅是在摧毁判定时,再累加伤害即可。

关于军队在地图上的运动,我调查过我们现在最活跃的服务器,同时在行军的部队,也不超过 10K 这个数量级。所以,我们完全可以制作一个单独的行军服务,管理所有在运动中的部队。这个服务可以有秒级的心跳,专门处理军队的运动。它最关键的职责在于触发部队的遭遇,而这类事件,仅需要部队 id 和路径即可。所需内存不多,cpu 的开销也不大。

btw, 去掉军队的移动,让行军服务空转,那么这个游戏就退化成类似战略游戏中部队点对点移动方式。游戏还是可玩的。而加上行军服务处理行军过程,并不会增加已有规则地复杂程度。复杂度被限制在该服务内部。


最后,我认为以现在 CPU 能力,以秒级别的周期处理区区 1 M 数量级的对象,游戏有卡顿的体验,这是不太正常的。我认为背后一定有大量质量不高的代码。一定有很大的优化余地。考虑到这个项目完成的速度颇快,这个现状可以理解。目前还没有时间去 review 代码,那是未来的工作。

-------------------------------------------------------------

博主的话:

前段时间一直在考虑如何以微服务的模式,铺设三国志战略版的服务端框架。在服务拆分方面总是不得要领,后来甚至开始怀疑是不是云风大神使用了什么“黑科技”(汗。。)。直到看到云风的这篇文章,才有种茅塞顿开的感觉。

技术只是基础,不会成为你成功路上的关键性指标,真正的高手会通过冷静的分析和合理的设计,选取成本相对较低的折中方案。实用且舒服。

-------------------------------------------------------------

转自云风blog:三国志战略版服务器卡顿问题相关推荐

  1. 三国志战略版服务器维护延时,三国志战略版服务器爆满是多少人 爆满进不去怎么办...

    三国志战略版服务器爆满是多少人?这个游戏的合区比较特别,s1赛季结束后会进行合区,之后每个赛季结束时会拆开之前的分组,然后再重新进行合区/分组,此时玩家可以选择转到其他服务器,这时候就可能碰到服务器爆 ...

  2. 三国志战略版服务器维护延时,三国志战略版5月19日维护更新公告 跨区转服规则调整...

    三国志战略版现在更想越来越敷衍了,之前还有更想调整一些武将,这几次更新什么都不打算动了.这次这次更新具体有哪些东西呢?下面就让我来告诉你们吧. 三国志战略版新版本[点击下载]签到送五星庞德,袁绍和橙将 ...

  3. 饥荒连接的时候服务器未响应,饥荒联机版服务器卡顿原因分析及解决教程 服务器卡怎么办-游侠网...

    很多在饥荒联机版的同学经常会遇见卡顿问题,而很多玩家为了解决卡顿问题都会选择自己建一个服务器在其中游玩.可是有些时候连自己建的服务器都会卡,这是什么问题呢>今天小编就为大家带来关于服务器卡顿原因 ...

  4. 饥荒服务器运行时cpu的占用,饥荒联机版服务器卡顿原因分析及解决教程 服务器卡怎么办_游侠网...

    很多在饥荒联机版的同学经常会遇见卡顿问题,而很多玩家为了解决卡顿问题都会选择自己建一个服务器在其中游玩.可是有些时候连自己建的服务器都会卡,这是什么问题呢>今天小编就为大家带来关于服务器卡顿原因 ...

  5. 三国志战略版360区S4服务器合并信息,三国志战略版S2赛季合区须知,季转服功能介绍...

    三国志战略版将在S2赛季开始后对目前的服务器进行合区操作,而同时在二赛季末还将开启转服功能.那么这些牵扯到账号数据的操作有什么需要我们注意的呢?接下来就让我们了解一下吧. 三国志战略版S2赛季合区须知 ...

  6. 三国志战略版360区S4服务器合并信息,三国志战略版pk赛季怎么转区?s4转区规则[多图]...

    三国志战略版pk赛季是全新的开始,那么如果有的玩家想要转区的话,需要有哪些方法或者说是条件呢?下面来了解下! 三国志战略版pk赛季怎么转区? 一.多久转服 因为转服只有赛季快结束,也就是赛季末期才会开 ...

  7. 三国志战略版360区S4服务器合并信息,三国志战略版s3赛季如何合区

    三国志战略版s3赛季如何合区 时间:2020-08-05 12:37:45 责任编辑:李尬尬 大小:279.23MB 语言:中文 平台:安卓 三国志战略版s3赛季如何合区-s3赛季服务器合并规则详解. ...

  8. 【三国志战略版】拆解与分析

    EEA是探娱互动研究院(Explore Entertainment Academy),我们会定期分享我们对游戏研究的文章,希望通过解决一个又一个的命题帮助大家以及我们自己将游戏设计的理论系统化,最终提 ...

  9. 三国志群英会java,八方群英会《三国志·战略版》盟主齐聚洛阳城 2021由此起航...

    在2020年的最后一天,<三国志·战略版>在洛阳城召开了首届同盟峰会&2021年度内容发布会.在为过去画上圆满的句号的同时也开启了未来新的篇章.游久网也受邀参加了本次盛会,为各位玩 ...

最新文章

  1. 行人被遮挡问题怎么破?百度提出PGFA新方法,发布Occluded-DukeMTMC大型数据集 | ICCV 2019...
  2. C# 制作外挂常用的API
  3. 把时间当作朋友 随笔记
  4. java解压_Java ZIP压缩和解压缩文件(解决中文文件名乱码问题)
  5. C# 淘宝商品微信返利助手开发-(三)返利助手开发(1)API介绍
  6. PostgreSQL数值类型--浮点类型和序列
  7. “敏捷”联袂“ALM” 上演市场模范夫妻秀
  8. TCP新手误区–心跳的意义
  9. mysql 监听 udp_通用TCP、UDP服务监控脚本
  10. TraceWrite waittype
  11. php颜色十六进制代码,如何通过PHP中的十六进制代码检索颜色的人名
  12. 扇贝有道180914每日一句
  13. android adb调试驱动,安卓手机上的调试工具adb驱动要怎么安装 手机安装adb驱动怎么做 - 驱动管家...
  14. 讲座笔记 | 批判性思维和论文写作
  15. 神经网络 游戏,神经连接游戏
  16. 使用Python实现excel项目清单自动生成word文档
  17. ASEMI的MOS管9N90参数,9N90电路图,9N90实物图
  18. 机器学习笔记(通俗易懂)---监督学习介绍:分类与回归,泛化~过拟合与欠拟合(2)
  19. arcgis api 4.13 —— Layer详细介绍
  20. 浅谈Java反射的实现原理

热门文章

  1. wos 文献被引_全世界最权威的文献检索工具,这6个检索技巧必须要掌握!
  2. 【Spring第四篇】DI注入以及c、p命名空间
  3. 【Spring第二篇】IOC:控制反转
  4. Vue packages version mismatch:- vue@2.6.14 - vue-template-compiler@2.6.11解决方法
  5. python网址编码转换_刚学python,抓中文网页遇到编码的问题,怎么转换也不行……...
  6. C++程序中可以没有using namespace std;这一句吗
  7. 整理了js数组去重4种方法
  8. 整理Java基础知识--Date Time2
  9. Shell脚本学习-阶段十-Bash脚本实现每次登录Shell时可以查看Linux系统信息
  10. pinpoint agent性能优化方面官方文档翻译