[开发者心得] 【断线重连解决方案】

[复制链接]
378 |2
空伊伊 发表于 2024-3-1 16:59:58 | 显示全部楼层 |阅读模式
本帖最后由 空伊伊 于 2024-3-1 17:02 编辑

断线重连解决方案


一、什么是断线重连?
断线重连实际上是两个词,分别是“重连”和“断线”

1.重连
当客户端超过150帧收不到服务端消息的时候,就会触发"重连"。
即出现下图弹窗,客户端在此时会持续的与服务器进行重连。
此时会触发事件 Player.onPlayerDisconnect
如果在这个阶段重连成功,则会触发事件 Player.onPlayerReconnect
image.png

2.断线
当重连进行超过10秒还未链接成功,就会触发“断线”。
即出现下图弹窗,用户此时只能选择退出或者重新加入。
此时会触发事件 Player.onPlayerLeave
如果重新加入成功,则会触发事件 Player.onPlayerJoin
重新加入规则:如果原房间已经崩溃或者人数达到上限,就会将用户分配到一个新的房间,否则返回原房间)
image.png


二、常见断线重连的产生原因及解决方案

1.断网or网络波动
这是由于用户本身的设备以及所处网络环境所导致的,并没有太好的解决办法。
不过这也需要我们在开发过程中注意,不要把影响整个房间游戏流程的判断逻辑交由客户端处理,否则可能会出现游戏进程不可控的情况,导致许多bug的产生。

2.服务端性能超限
由于平台需要同时给很多游戏房间提供服务器功能,所以每个房间能够分配到的性能都是有限的。
服务端性能超限后,就有可能导致房间崩溃重启,这会使得房间内的所有玩家都触发断线重连。
下面为大家介绍如何通过创作者中心提供的性能视图来对断线重连问题进行排查:
2.1.打开房间服务端性能视图
服务端性能限制需要发布游戏后,到创作者中心进行查看。
(下图即为服务端性能数据页面,在创作者中心找到想要查看的游戏,然后点击"房间列表",然后再找到对应的房间,在房间右侧有一个"服务端性能"选项,点击即可进入)
image.png
2.2.影响服务器CPU的因素
(服务器CPU的上限会根据房间最大人数来进行分配,每新增5个房间最大人数则会上升0.1个单位)
CPU主要的功能就是进行计算,在服务端创建物体、改变物体的位置、改变物体的大小、进行逻辑计算、收发RPC等操作都会需要使用到CPU。
所以想降低服务端CPU消耗,有如下优化手段:
  🔹 检查实例化(spawn)接口,避免短时间内大量创建的情况。
  🔹 检查update、interval等循环逻辑,查看是否有大量复杂的计算逻辑未进行优化。
  🔹 避免大量物体使用服务端来每帧更新位置
  🔹 减少双端Character对象的数量(因为Character对象自带很多属性需要同步)
2.3.影响服务器内存的因素
(服务器内存的上限会根据房间最大人数来进行分配,每新增5个房间最大人数则会上升200Mib)
服务端内存主要受到双端物体的影响,双端物体越多,内存占用就越多。因为服务端需要时刻维护双端物体的信息,并且实时给客户端同步。
所以想降低服务端内存消耗,有如下手段:
  🔹 减少场景里双端物体的数量,场景过大可以考虑增加房间最大人数,或者使用多场景功能。
  🔹 检查代码中实例化对象的逻辑,是否在服务端动态创建了过多的双端对象。
  🔹 检查玩家的上线下逻辑,是否当玩家离开房间后,所属的对象未进行回收。
  🔹 优化代码逻辑,减少内存泄漏。


3.RPC栈溢出
RPC是我们开发网络游戏时最常使用到的功能,对于RPC使用不当同样能够导致断线重连问题的出现。
(RPC相关内容:【RPC和Replicated】—— RPC介绍 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
3.1.“栈”是什么?
当服务端向客户端发送一条RPC,服务端需要确认客户端是否收到。但是客户端可能因为网络波动,或者进入“挂起状态”,导致没有接收到RPC。这个时候服务端就会将这条未发送成功的RPC入栈,栈对于一个客户端有256条的上限。当堆积的RPC超过这个上限,服务端则会主动断开与这个客户端的链接,这种情况也就是标题所说的“RPC栈溢出”。
(挂起状态:当手机进入看广告或者将游戏切后台的时候,就进入了挂起状态。在该状态下,游戏客户端将会停止运行,这也就会导致客户端收不到服务器发送过来的RPC,所以这段时间内服务端发送的RPC极容易入栈产生堆积)

3.2."单点"&"广播"
编辑器提供的RPC主要分为两种类型:
  🔹 "单点"(可以理解为服务端向指定客户端发送RPC)
  🔹 "广播"(可以理解为服务端向所有客户端发送RPC)
举个例子,使用装饰器 @RemoteFunction(Client) 以及 Event.dispatchToClient() 发送的RPC即为“单点”;
需要重点注意的是,Event.dispatchToAllClient() 看起来像是“广播”,但本质是封装了 Event.dispatchToClient() 所以它也属于单点;所以目前只有使用装饰器 @RemoteFunction(Client, Multicast) 发送的RPC才是“广播”;

这里有一个很重要的机制:单点RPC会一直在服务端进行堆积,广播RPC会在客户端持续超过1.5秒收不到消息后,停止向对应客户端发送RPC。

3.3.优化方案
了解上述机制后,我们可以知道,导致RPC栈溢出的主要原因是< "单点"RPC在短时间内使用过多 > ,所以我们主要的优化方向就是减少服务端向客户端发送单点RPC的数量,以及降低发送频率。下面例举几个场景:
  🔹 将高频率的同步逻辑替换为属性同步【RPC和Replicated】——Replicated介绍
  🔹 减少在服务端通过EffectService播放特效,因为EffectService产生的RPC都是单点RPC
  🔹 将控制游戏主要流程的逻辑 与 控制游戏表现的逻辑进行区分,前者用单点RPC实现,后者用广播或者属性同步实现。(因为少播一个特效或动作带来的影响并不会导致游戏流程卡死)
  🔹 特殊处理看广告逻辑,在用户点击看广告按钮时,先通知服务端停止发送单点RPC,然后再播放广告。

3.4.RPC数量查看工具
为了方便大家排查自己项目里哪些RPC使用过多,这里提供一个单脚本方便大家进行RPC数量的统计。
脚本只需要挂载到场景任意双端物体上,运行即可使用
StatScript.ts (13.97 KB, 下载次数: 15)
回复

使用道具 举报

神帖
回复

使用道具 举报

空伊伊楼主 发表于 2024-3-5 17:00:12 | 显示全部楼层
回复

使用道具 举报

热门版块
快速回复 返回顶部 返回列表