MeteorCat / Fortress 1 - 项目初始化

Created Tue, 26 Aug 2025 20:08:58 +0800 Modified Wed, 29 Oct 2025 23:24:54 +0800

项目初始化

这里主要面向的游戏服务端相关业务, 但是本质上也可以看作设计 RPC 系统, 具体的依赖如下:

  • maven-wrapper: 包管理系统, 不要用 gradle 做项目管理, 坑太多了不好填
  • java21: 采用 21 版本主要是准备承接后续大更新 24 版本
  • pekko: 需要搭建 actor 节点集群开发, akka 已经转商业授权, 所以采用 pekko 开源版本
  • protobuf: 客户端交换消息的方式采用 protobufV3, pekko 内部集群序列化传输消息也支持
  • quarkus: redhat 出的开发框架, 需要用到 容器管理/WebSocket服务/Web服务 功能并且支持打包生成原生可执行文件
  • mariadb-jpa: mariadb数据库和 ORM 框架, 用于挂载和落地玩家数据实体到数据库之中

这部分相关的官方文档可以先自行前往官网查阅:

之所以采用 quarkus 作为主要开发框架是基于以下几点:

  • 支持原生应用生成
  • 容器和云原生支持
  • 启动快和内存占用小
  • 轻量化依赖扩展
  • 官方提供高并发WebSocket实现
  • 业务开发需要比较轻量化的容器管理

之后就是设计项目目录结构:

# io.fortress 是项目包名, 这里采用 quarkus 工具链初始化
--- /.mvn( maven-wrapper 目录 )  --- /wrapper/maven-wrapper.properties( mvn-wrapper 管理配置,建议提交版本管理 ) 
        |
    /mvnw( linux/unix的 mvn 执行命令, 建议提交版本管理 )
        |
    /mvnw.cmd( window 的 mvn 执行命令, 建议提交版本管理 ) 
        |
    /pom.xml( maven 配置文件 )
        |
    /proto( protobuf 的 .proto 文件目录 )
        |
    /modules( 项目模块目录 ) -------- /common( 项目通用集成核心库 )
                             |        |
                             |        | --------- /pom.xml( common 的 maven 配置文件 )
                             |        |
                             |        | --------- /src( common 源代码目录 )
                             |                       |
                             |                       | ---------- /main( 项目入口目录 )
                             |                       |               |
                             |                       |               | -------- /java(Java的源代码目录)
                             |                       |               |            |
                             |                       |               |            | ------- io.fortress.common (核心包目录)
                             |                       |               |
                             |                       |               | -------- /resources(内部资源目录)
                             |                       |               |
                             |                       |               | -------- /docker( docker 启动配置 )
                             |                       |
                             |                       | ---------- /test( 测试单元目录 )
                             |
                             |
                             |
                             |--- /websocket( websocket 网关, 基本和 common 一致, 包名为 io.fortress.websocket, 方便后续扩展多端 tcp 网关等 )
                             |
                             |--- /cluster( 逻辑集群, 主要的业务编写入口, 和 common 基本一致, 包名为 io.fortress.cluster )                                  

国内的 maven 镜像很不稳定, 所以需要将其修改成国内镜像源, 把 .mvn/wrapper/maven-wrapper.properties 文件修改一下:

# 这里需要按照你生成项目时候 wrapper 版本去阿里源找对应软件路径
wrapperVersion=3.3.2
distributionType=source
distributionUrl=https://maven.aliyun.com/repository/public/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
wrapperUrl=https://maven.aliyun.com/repository/public/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar

注意: 如果是团队项目管理, 最好锁定项目 maven-wrapper 版本开发而不要去用到本地的 maven 包管理

之后就是根目录项目的 pom.xml 配置, 我这里对根目录的包名设为 io.fortress.boot, 具体如下:

<?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>io.fortress</groupId>
    <artifactId>boot</artifactId>
    <name>fortress-boot</name>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <!-- 基本属性 -->
    <properties>
        <compiler-plugin.version>3.14.0</compiler-plugin.version>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- 平台相关插件 -->
        <os.plugin.group-id>kr.motd.maven</os.plugin.group-id>
        <os.plugin.version>1.6.2</os.plugin.version>

        <!-- quarkus 依赖 -->
        <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
        <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
        <quarkus.platform.version>3.26.1</quarkus.platform.version>
        <skipITs>true</skipITs>
        <surefire-plugin.version>3.5.3</surefire-plugin.version>

        <!-- pekko 依赖 -->
        <pekko.platform.artifact-id>pekko-bom</pekko.platform.artifact-id>
        <pekko.platform.group-id>org.apache.pekko</pekko.platform.group-id>
        <pekko.platform.version>1.1.5</pekko.platform.version>
        <pekko.platform.scala-version>2.13</pekko.platform.scala-version>

        <!-- protobuf 依赖 -->
        <protobuf.platform.artifact-id>protobuf-bom</protobuf.platform.artifact-id>
        <protobuf.platform.group-id>com.google.protobuf</protobuf.platform.group-id>
        <protobuf.platform.version>4.32.0</protobuf.platform.version>

        <!-- protobuf 插件 -->
        <protobuf.plugin.group-id>org.xolstice.maven.plugins</protobuf.plugin.group-id>
        <protobuf.plugin.version>0.6.1</protobuf.plugin.version>

        <!-- protoc 编译器 -->
        <protobuf.compiler.artifact-id>protoc</protobuf.compiler.artifact-id>
        <protobuf.compiler.group-id>com.google.protobuf</protobuf.compiler.group-id>
        <protobuf.compiler.version>${protobuf.platform.version}</protobuf.compiler.version>

    </properties>


    <!-- 统一管理依赖版本配置 -->
    <dependencyManagement>
        <dependencies>

            <!-- 自身版本库 -->
            <dependency>
                <groupId>io.fortress</groupId>
                <artifactId>fortress-common</artifactId>
                <version>${project.version}</version>
            </dependency>

            <!-- quarkus 依赖 -->
            <dependency>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>${quarkus.platform.artifact-id}</artifactId>
                <version>${quarkus.platform.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- pekko 依赖 -->
            <dependency>
                <groupId>${pekko.platform.group-id}</groupId>
                <artifactId>${pekko.platform.artifact-id}_${pekko.platform.scala-version}</artifactId>
                <version>${pekko.platform.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- protobuf 依赖 -->
            <dependency>
                <groupId>${protobuf.platform.group-id}</groupId>
                <artifactId>${protobuf.platform.artifact-id}</artifactId>
                <version>${protobuf.platform.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- protobuf 核心依赖 -->
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>${protobuf.platform.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <!-- 源仓库配置 -->
    <repositories>

        <!-- public 库 -->
        <repository>
            <id>central</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>

        <!-- apache-snapshots 库 -->
        <repository>
            <id>apache-snapshots</id>
            <url>https://maven.aliyun.com/repository/apache-snapshots</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>

        <!-- Maven Central -->
        <repository>
            <id>maven-central</id>
            <name>Maven Central Repository</name>
            <url>https://repo1.maven.org/maven2/</url>
        </repository>

    </repositories>

    <!-- 源插件配置 -->
    <pluginRepositories>

        <!-- 阿里云的插件源 -->
        <pluginRepository>
            <id>aliyun-plugin-public</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>

        <!-- 阿里云的 apache-snapshots 插件源 -->
        <pluginRepository>
            <id>aliyun-plugin-apache</id>
            <url>https://maven.aliyun.com/repository/apache-snapshots</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <!-- 子目录配置 -->
    <modules>
        <module>modules/common</module>
        <module>modules/cluster</module>
        <module>modules/websocket</module>
    </modules>


    <!-- 公共编译配置 -->
    <build>

        <!-- 扩展插件 -->
        <extensions>
            <!-- 检测系统插件 -->
            <extension>
                <groupId>${os.plugin.group-id}</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>${os.plugin.version}</version>
            </extension>
        </extensions>

        <plugins>

            <!-- quarkus-maven 插件配置 -->
            <plugin>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>${quarkus.platform.version}</version>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                            <goal>generate-code</goal>
                            <goal>generate-code-tests</goal>
                            <goal>native-image-agent</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>


            <!-- maven 编译配置 -->
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin.version}</version>
                <configuration>
                    <parameters>true</parameters>
                    <source>${maven.compiler.release}</source>
                    <target>${maven.compiler.release}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>

            <!-- 默认日志配置 -->
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <configuration>
                    <systemPropertyVariables>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>


            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <systemPropertyVariables>
                        <native.image.path>${project.build.directory}/${project.build.finalName}-runner
                        </native.image.path>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- 公共打包参数 -->
    <profiles>

        <!-- 生成原生二进制应用 -->
        <profile>
            <id>native</id>
            <activation>
                <property>
                    <name>native</name>
                </property>
            </activation>
            <properties>
                <skipITs>false</skipITs>
                <quarkus.native.enabled>true</quarkus.native.enabled>
            </properties>
        </profile>
    </profiles>
</project>

这里实际上复制用就行了, 主要是 <modules> 节点之中对应子目录生成, 最终的目录就像这样:

directory

Java 选型理由

实际上是从招人和开发易用性方面考虑, 首先就是 Java 在国内受众基础很庞大, 其次就是 Java 本身做作为老牌开发语言虽然死板但是规范挺好的; 另外提到 Java 就不得不提到新秀 Kotlin, 作为提供大量语法糖和语法的快速编程语言, 某些方面领先于 Java(可空类型等).

但是 Kotlin 带来的大量语法糖会导致加大理解难度, 并且目前主要开发依赖 jetbrains 公司的支持程度; 虽然作为 idea 出身的 jetbrains 能够从 ide 更好结合起来开发, 但是指不定某个时间段不玩了就丢给开源社区撒手不管( fleet 前车之鉴, 要抛弃的时候直接无声无息不管 )

目前 Kotlin 可能在安卓移动端方面可能比较好用点, 我虽然比较崇尚新技术但对于 Kotlin 这种也是偏保守看待

另外之前也曾经尝试过 Java 切换到 Kotlin, 但是在做 JPA-ORM 就出现 bug:


@Entity
class PlayerEntity {
    // 这个字段在 Kotlin 里面会报错
    private Boolean isPay;
}

按照网上说法就是 Kotlin 会对字段本身追加 isPay() 函数, 所以出现属性被覆盖从而未找到的映射冲突, 这虽然是个小问题, 但如果经验不足很容易在这种小问题上卡很久.

另外单纯开发服务端绝对不要用 gradle 作为包管理, 这个包管理目前极不成熟, 版本兼容性和第三方插件支持也一塌糊涂

不过这部分其实都能够妥协, 比如按照团队分工来设计, 像是核心工具库求稳可以采用 Java 开发, 而如果相关集群业务的子模块可以采用 Kotlin 另外开发, 这两者是相互兼容而非互斥的, 所以主要按照团队习惯来选择就行了.