MeteorCat / 构建游戏网关(一)

Created Sat, 19 Apr 2025 11:53:29 +0800 Modified Wed, 29 Oct 2025 23:24:45 +0800
1685 Words

这里依赖以下第三方库组件来构建第一个游戏网关:

  • lombok: 节约 GET|SET 开发流程
  • spring-boot: 基础依赖注入组件
  • logback: 更新版本日志组件, spring-boot 老版本需要覆盖避免漏洞
  • spring-boot-websocket: WebSocket 传输层
  • pekko: akka 的开源替代 actor 模型

jvm 语言其实选择比较, 按照个人喜好就行了:

  • java: 老牌语言实现, 稳定性和兼容性最好
  • groovy: 动态脚本语言, 支持 jvm 的动态热更新
  • scala: 携带大量语法糖且高度抽象的实现编程语言语言
  • kotlin: JetBrains 官方直接支持, 开发效率很不错

实际上还是建议还是采用 Java 语言作为编程语言, 因为大部分商业游戏都是挂靠 Unity3d+C# 实现, 而 C# 大部分语法都是和 Java 近似, 所以有时候服务端+客户端有时候业务繁忙的时候客户互相帮忙编写.

另外还有 pekko 挂载 quickjs|lua 虚拟来做动态热更新, 这种方式也能兼容游戏客户端如果采用双端脚本的方案

在正式进入之前需要说明下服务器架构选择流程:

# 1. 转发服务集群                                                                                        
客户端(TCP) ------|
                 |
                 |
                 | ------- [(同进程) NettyTCP -> GatewaActor (startProxy) ] ------- AuthorityActor
                                            |
                                            | ----------------------------------- WorldActor
                                            | ----------------------------------- PlayerActor


# 2. 多端集群服务
客户端(TCP) ------|
                 |
                 | ------- [(同进程) NettyTCP -> NettyGatewaActor (startProxy) ]
                                        |
                                        |----- AuthorityShardingActor-A,...
                                        |----- WorldShardingActor-A,...
                                        |----- PlayerShardingActor-A,...
                                        |
                 | ------- [(同进程) SpringBootWS->WSGatewaActor (startProxy) ]
                 |
客户端(WebSocket)-|



# 3. 跨端树状集群
客户端(TCP) ------|
                 |
                 |
                 | -------- [(TCP进程)NettyTCP-A -> WSClusterActor(startProxy)]  
                 | -------- [(TCP进程)NettyTCP-B -> WSClusterActor(startProxy)]
                                    |
                                    |                             
                             [(内网分片转发网关)ShardingGatway-A]    |----- AuthorityActor-A,...
                             [(内网分片转发网关)ShardingGatway-B] ---|----- WorldActor-A,...
                             [(内网分片转发网关)ShardingGatway-C]    |----- PlayerActor-A,...
                                    |                              
                                    |
                 | -------- [(WS进程)SpringBootWS-A -> WSClusterActor(startProxy)]                                   
                 | -------- [(WS进程)SpringBootWS-B -> WSClusterActor(startProxy)]
                 |
                 |
客户端(WebSocket)-|

第一套方案是常见 分区分服 游戏服务端转发架构, 好处就是 客户端端点(endpoint) 可以依赖分服来让压力分流, 通过实时确认服务器状态将人数爆满的服务器A分流到服务器B, 也是经过市场检验最多的架构设计.

第二套是以 世界服 概念把 数据传输层+网关服务 两者集成在一起, 支持多端互通让其操作交互

第三套方案也是以 世界服 概念把业务依赖集群散布在多台内网服务器上, 通过把业务分类让玩家直接让其直接互通而无需选服, 同时因为 传输层(tcp|udp|websocket) 抽离出来方便实现跨端交互( h5-websocket网页端, tcp-pc客户端 ).

第一套相对简单且方便后续扩展, 但是后续需要维护服务合服来避免某台服务器完全闲置, 也就是日常需要操作 合服滚服 避免死区导致服务器空置(总不能单台服务器支持不到10个人游玩).

第二套方案抛弃了选服概念, 让服务器尽可能针对业务运行在不同服务器上, 传输层分布在独立的服务器上并且挂载 actor 代理服务; 这样好处就是需要再把心力花费在 停服-合服 这种流程上.

第三套方案因为扩展大量集群设备, 所以每条数据会出现比较复杂转发路径(客户端进程 <-> 网络进程 <-> 内网网关 <-> 业务集群), 不过好处就是可以调动大量服务器来做运算, 对于后续动态扩容也十分有利.

还有种情况就是 CPU密集计算 > 实时转发同步 就需要其他设备分布运算, 这种情况可以考虑第三套方案

如果不确定后续架构扩展的话, 其实还是选择 分区分服 这种默认方案准没错, 对于游戏服务尽可能减少内网转发(转发不超过三层).

推荐采用 多项目管理 来管理多个项目, 方便后续扩展出来的继承相同配置, 同时还方便组件统一编写在 common 之中

这里采用 maven 来做组件设置(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.meteorcat.fusion</groupId>
    <packaging>pom</packaging>
    <description>Java游戏网关服务游戏</description>

    <!-- fusion-framework 作为主要多项目父框架名 -->
    <artifactId>fusion-framework</artifactId>

    <!-- 版本号我喜欢采用Y.m.d做区分, 方便确定哪一天需求当作版本 -->
    <version>2024.4.19-SNAPSHOT</version>

    <!-- 全局属性 -->
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>3.3.10</spring-boot.version>
        <maven-compiler.version>3.13.0</maven-compiler.version>
        <lombok.version>1.18.36</lombok.version>


        <!-- pekko Actor -->
        <pekko.version>1.1.3</pekko.version>
        <scala.binary.version>2.13</scala.binary.version>
        <logback.version>1.5.18</logback.version>
    </properties>


    <!-- 子模块定义 -->
    <modules>
    </modules>


    <!-- 管理子项目的依赖 -->
    <dependencyManagement>

        <dependencies>
            <!-- LOMBOK组件 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
                <version>${lombok.version}</version>
            </dependency>

            <!-- pekko 为SLF4J 后端 -->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${logback.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- spring 组件依赖  -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- WebSocket依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- pekko Actor -->
            <dependency>
                <groupId>org.apache.pekko</groupId>
                <artifactId>pekko-bom_${scala.binary.version}</artifactId>
                <version>${pekko.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>


    <!-- 配置子项目第三方库源 -->
    <repositories>
        <repository>
            <id>central</id>
            <url>https://maven.aliyun.com/repository/central</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>


        <repository>
            <id>netflix-candidates</id>
            <name>Netflix Candidates</name>
            <url>https://artifactory-oss.prod.netflix.net/artifactory/maven-oss-candidates</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>

        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>

    </repositories>


    <!-- 配置公共的插件管理等 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

这里保存一下提交版本库, 这就是最开始我们的项目初始化, 后续就是准备组件库编写和测试.