项目初始化
这里主要面向的游戏服务端相关业务, 但是本质上也可以看作设计 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> 节点之中对应子目录生成, 最终的目录就像这样:
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 另外开发, 这两者是相互兼容而非互斥的, 所以主要按照团队习惯来选择就行了.