This commit is contained in:
boybook
2025-12-01 20:59:16 +08:00
parent 12738a142c
commit 760c2dd9ad
5535 changed files with 21070 additions and 2021 deletions

View File

@@ -0,0 +1,115 @@
# 分布式技术
<iframe src="https://cc.163.com/act/m/daily/iframeplayer/?id=64818ee9c31a9c0f360dc5fc" width="800" height="600" allow="fullscreen"/>
由于开服工具2.0使用的是Spigot服务端。该服务端的主要游戏逻辑都放在单一线程内进行处理。
在CPU单核性能的限制下如果单服务器承载玩家数量较多的情况下就非常有可能会造成卡顿。
因此推荐将玩家通过代理端,分发给不同的除了游戏数据之外完全一致的服务器进行游戏。
下面将介绍几个常用的玩法服务器的架设方式,来介绍如何更合理的使用硬件资源。
## 小游戏服
- 某玩法
- 地图A
- 服务器1
- 游戏1
- 游戏2
- 游戏3
- 服务器2
- 游戏4
- 游戏5
- 地图B
- 服务器3
- 游戏6
- 游戏7
- 服务器4
- 游戏8
- 游戏9
在小游戏服的架构中可以根据某个玩法对CPU性能的消耗估算出单一服务器进程能承载的玩家最大玩家数再在单一服务器下开设n个游戏房间来提高单台机器的玩家承载能力。
## RPG服
- 主城服
- 服务器1
- 服务器2
- 区域A
- 服务器3
- 服务器4
- 区域B
- 服务器5
- 副本1
- 服务器6
- 服务器7
- 副本2
- 服务器8
- 服务器9
在RPG类游戏玩法服务器的架构中可以考虑将玩家在线量较大的服务器进行水平拓展复制出n个完全相同的服务器这些服务器共享玩家的数据通过代理服进行负载均衡。
## 性能观察
在确定好架构之后,还需要对服务器性能进行观察,合理调整每个服务器的最大人数,降低服务器的压力。
### TPS
服务器TPS是衡量服务器流畅度的指标之一它代表了每秒钟服务端程序运行了多少个游戏刻。服务器在最理想情况下每秒应运行20个游戏刻即最理想的TPS值为20。我们可以依靠TPS对服务器流畅度量化在TPS值低于15时玩家会有很明显的卡顿感。
在Spigot服务端中可以以管理员身份输入`/tps`指令来查看服务器TPS。
在服务器TPS较低时就需要依靠工具来对卡顿进行检查。在这里仅介绍spigot原生支持的timings进行检查。
### timings
timings是spigot服务端原生提供的一个性能追踪工具。使用它可以对服务端运行的耗时进行检查。
使用步骤
- 服务器卡顿情况下,输入`/timings on`
- 等待几分钟,输入`/timings paste`
- 访问`/timings paste`给出的链接,对性能进行分析。
具体参数含义,可以参考[文档](https://www.spigotmc.org/wiki/timings/)。打开timings指令提供的链接
- 如果主要占用时间较多的项目是插件逻辑。建议对插件逻辑进行检查避免在主线程上进行I/O、网络操作尽可能的优化插件算法减少时间复杂度。
- 如果主要占用时间较多的项目是游戏原生逻辑,则需要对应去检查是否有玩家恶意利用游戏漏洞、使用高频红石等等。并使用插件对此类行为进行一定限度的限制。
如果在上述方面都以已经无法进行优化那么在不魔改服务端内核的情况下已经达到了CPU单核性能所能承载的最大人数。可以分别测试不同人数下TPS的数值推算出一个可以让TPS稳定在19+的人数数值。并限制单服务器最大在线人数。
对于单服多游戏的小游戏架构同理,可以估算出最大在线人数,再除以每局游戏最大人数,计算出一个服务器可以部署多少个游戏。或者可以修改匹配算法,让单服务器同时最多承载一局游戏,把单核性能发挥到极致。
> 例如起床战争玩法一个服务器进程提供3个维度进行游戏服务器启动时注册3个不同维度的房间。
>
> 匹配算法,优先匹配没有任何运行中的维度的服务器,分配玩家。
>
> 这样每个玩家进入的服务器,都只有玩家所在维度的房间是有人的。
>
> 当3个维度房间全部消耗殆尽后重启服务器并重置地图。
除此之外,我们还需要预估出让玩家流畅游玩所需要使用的机器数量,在流量高峰到来之前,提前申请机器做好准备。
首先我们需要在服务器TPS能稳定在19+、服务器到达最大在线人数的时候进入ssh输入`ps -aux | grep java8`找到这个服务器所对应的进程。如果有多个java8进程可以通过插件例如CMI插件在服务器内查看服务器进程的PID找到对应的PID所对应的进程。
```
fuzhu@g79xxspt-runtime27-87000:~$ ps -aux | grep java
fuzhu 8046 0.0 0.0 13408 892 pts/0 S+ 10:15 0:00 grep java
fuzhu 14058 0.4 3.5 3532168 143324 pts/1 Sl+ Apr06 91:39 java8 -jar BungeeCord.jar
fuzhu 19984 2.0 18.3 3672320 739604 pts/2 Sl+ Apr17 49:43 java8 -jar -Xms1G -Xmx1G -jar spigot-1.12.2.jar
```
例如在这里我们找到lobby端所在的进程即PID为19984的进程。
找到进程之后,我们使用`top -p PID`,例如这里的`top -p 19984 `来查看资源占用峰值。
![](./images/01.png)
例如这里CPU占用峰值为2.0%内存占用峰值为18.3%。
得出了单服满载时的硬件资源消耗后,我们可以根据这个数值,计算出一台机器的最大承载数量,并根据这个最大在线数据,计算需要提前申请多少的机器。

View File

@@ -0,0 +1,58 @@
# 分布式服务器之间的数据同步
在了解了分布式技术假设服务器之后,如何在不同服务器之间共享数据,就成为了一个问题。
从插件数据的持久化部分来说,应该优先使用数据库,而不是本地文件进行数据保存。同时在需要时,对数据预先进行缓存。
开服工具2.0的服务器默认提供了3种数据库MySQL、Redis、MongoDB。开发者可以根据自己的开发习惯和数据的格式来选择更加合适的数据库来存储数据。如果自己更习惯的数据库不在这三者之中也可以自行或联系机房技术人员在服务器上安装其他数据库。
## 持久化
数据在持久化上应该优先考虑使用MySQL或MongoDB来进行数据存储。
推荐使用连接池来预先准备足够数量的连接,方便在需要时立刻取用。
可以使用的库例如
### MySQL
- [druid](https://github.com/alibaba/druid)
- [HikariCP](https://github.com/brettwooldridge/HikariCP)
### MongoDB
- [Mongo Java Driver](https://github.com/mongodb/mongo-java-driver/)
## 缓存
对于高频访问的数据,可以考虑使用内存或[Redis](https://redis.io/)进行存储,防止流量直击数据库,对数据库造成较大的压力。
对于Java可以使用[Jedis](https://github.com/redis/jedis)或[redisson](https://github.com/redisson/redisson)来创建并管理Redis连接池。
> 例如[RedisBungee](https://github.com/minecrafter/RedisBungee)插件就是利用Redis高速的特性通过Redis存储BungeeCord之间的玩家在线数据实现了数据同步。
## 分布式锁
在分布式服务器之间,经常会遇到需要争抢同一个资源的情况。
> 例如:交易市场功能
>
> 遇到此类情景时经常会遇到多个用户同时争抢购买同一个商品的功能为了防止同时对一个资源进行操作推荐使用Redis提供的分布式锁的功能。在尝试交易之前首先设置Redis的分布式锁抢锁成功后开始交易操作交易完成释放锁。
具体操作方法可以参考[官方文档](https://redis.io/docs/manual/patterns/distributed-locks/)。
## 订阅/发布
Redis还提供了订阅发布功能即Pub/Sub
如果要实现服务器之间的消息广播通信可以依赖Pub/Sub来实现。
> 例如全服喊话功能就可以在不同BC服之间订阅同一个频道来实现。
具体操作方法可以参考[官方文档](https://redis.io/docs/manual/pubsub/)。
## 消息队列
如果需要使用消息队列功能新版本Redis也同样提供了Streams接口来实现消息队列具体可以参考[官方文档](https://redis.io/docs/data-types/streams-tutorial/)。
如果Redis无法满足你的需求也可以自行或联系机房工作人员安装其他消息队列中间件。