Organizations

56 results for Java
  • 序列化处理 现在已经可以通过配置来生成 ActorSystem, 但是现在需要引入 Protobuf 来做序列化传输. 默认 pekko 传输都是依赖 java-serialization 默认序列化做传输, 如果你成功启动服务传输的时候会出现以下提示: [ERROR] [08/28/2025 11:29:54.811] [fortress-cluster-pekko.remote.default-remote-dispatcher-5] [Encoder(pekko://fortress-cluster)] Failed to serialize message [ActorSelectionMessage(io.fortress.common.event.ConnectedEvent)]. java.io.NotSerializableException: No configured serialization-bindings for class [io.fortress.common.event.ConnectedEvent] 看起来传输的消息结构只需要实现 java.io.Serializable 并且追加以下配置即可: # Actor 序列化允许采用 Java 默认处理 fortress.actor.settings.pekko.actor.allow-java-serialization=on 这里虽然传输成功, 但是默认是会提示警告: [WARN] [SECURITY][08/28/2025 12:01:31.345] [fortress-cluster-pekko.remote.default-remote-dispatcher-14] [org.apache.pekko.serialization.Serialization(pekko://fortress-cluster)] Using the Java serializer for class [io.fortress.common.event.ConnectedEvent] which is not recommended because of performance implications. Use another serializer or disable this warning using the setting 'pekko.
    fortress Java Created Thu, 28 Aug 2025 19:34:47 +0800
  • 功能初始化 这里首先需要配置 common 子项目的功能, 一切基于 protobuf 生成的代码也是由 common 项目构建生成. 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"> <!-- 父依赖 --> <parent> <groupId>io.fortress</groupId> <artifactId>boot</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../../pom.xml</relativePath> </parent> <!-- 子属性 --> <modelVersion>4.0.0</modelVersion> <artifactId>fortress-common</artifactId> <name>Fortress Common Module</name> <description>Fortress utilities and models</description> <packaging>jar</packaging> <!-- 第三方依赖 --> <dependencies> <!-- 通用字符串、集合等工具类 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!-- 通用编码哈希工具 --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency> <!-- Quarkus 核心依赖 --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-core</artifactId> </dependency> <!-- Quarkus 容器依赖 --> <dependency> <groupId>io.
    fortress Java Created Wed, 27 Aug 2025 20:06:01 +0800
  • 项目初始化 这里主要面向的游戏服务端相关业务, 但是本质上也可以看作设计 RPC 系统, 具体的依赖如下: maven-wrapper: 包管理系统, 不要用 gradle 做项目管理, 坑太多了不好填 java21: 采用 21 版本主要是准备承接后续大更新 24 版本 pekko: 需要搭建 actor 节点集群开发, akka 已经转商业授权, 所以采用 pekko 开源版本 protobuf: 客户端交换消息的方式采用 protobufV3, pekko 内部集群序列化传输消息也支持 quarkus: redhat 出的开发框架, 需要用到 容器管理/WebSocket服务/Web服务 功能并且支持打包生成原生可执行文件 mariadb-jpa: mariadb数据库和 ORM 框架, 用于挂载和落地玩家数据实体到数据库之中 这部分相关的官方文档可以先自行前往官网查阅: pekko protobuf quarkus hibernate-orm quarkus-native 之所以采用 quarkus 作为主要开发框架是基于以下几点: 支持原生应用生成 容器和云原生支持 启动快和内存占用小 轻量化依赖扩展 官方提供高并发WebSocket实现 业务开发需要比较轻量化的容器管理 之后就是设计项目目录结构: # io.fortress 是项目包名, 这里采用 quarkus 工具链初始化 --- /.mvn( maven-wrapper 目录 ) --- /wrapper/maven-wrapper.
    fortress Java Created Tue, 26 Aug 2025 20:08:58 +0800
  • 最近需要用到部署 SDK 支付服务网关服务器, 需要对不同第三方支付商的开发包有所支持; 一般第三方的支付商对 Java 方面支持最多, 可以节约时间省下从底层编写发起网络请求的代码. 本来打算直接采用 springboot 直接构建 web 接口即可, 但是现在 springboot 越来越臃肿且庞大, 有时候启动服务的时候也很缓慢, 所以打算采用别的框架处理这方面独立服务. 也就是在这个时间发现 Java 当中轻量级的框架: quarkus 甚至可以依靠 GraalVM | Mandrel 将程序打包成原生可执行文件(依赖Java21+), 方便直接把可执行二进制丢到服务器运行 如果从0开始可以按照官方文档处理: 创建你的第一个应用程序 不过我习惯还是采用 IDEA 初始化构建, 并且因为要和 Android 客户端环境尽可能匹配而采用 gradle 管理( build.gradle.kts ): // 声明插件 plugins { java id("io.quarkus") } // 第三方库 repositories { mavenCentral() mavenLocal() } // 加载全局项目当中配置 val quarkusPlatformGroupId: String by project val quarkusPlatformArtifactId: String by project val quarkusPlatformVersion: String by project // 第三方包 dependencies { implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")) // Web 配置 implementation("io.
    Java Created Sat, 09 Aug 2025 19:30:30 +0800
  • 最近后续项目可能要做 Android 客户端对接, 为了前后端统一后续可能采用 gradle + java 方案, 所以先做好一定知识积累方便后续从 maven 转为 gradle. gradle 主要有两种方式: 全局二进制命令行 : https://gradle.org/releases 下载 GRADLE_HOME 和 GRADLE_HOME/bin 设为全局命令 Gradle Wrapper: 把二进制放置在项目之中, 加入版本库保证所有团队成员使用相同版本构建和启动 建议采用 Gradle Wrapper 方式, 这样可以避免团队成员可能因为不同版本导致构建打包问题, 打包可以用以下配置提高构建性能: 启用构建缓存:org.gradle.caching=true 配置适当的 JVM 内存:org.gradle.jvmargs=-Xmx2g 使用并行构建:org.gradle.parallel=true 目前 gradle 引入两种打包构建脚本: Gradle-Groovy: 更像是传统脚本语言, 采用空格隔开作为每个参数 Gradle-Kotlin: 类型安全并且对于客户端支持更加稳健, 官方比较推荐的打包脚本 Gradle-Kotlin 创建初始化之后会默认带有两个打包文件: build.gradle.kts: 打包构建脚本 settings.gradle.kts: 属性参数清单 之前 gradle 在国内远不如 maven, 我这边采用 IDEA 直接初始化, 主要看下 build.gradle.kts 配置说明: // 声明项目采用的打包语言 plugins { id("java") } // 打包程序包名 group = "com.
    Java Created Sat, 09 Aug 2025 15:51:12 +0800
  • 前面比较长时间篇幅去说明 Description, 主要开始对开源项目的复杂性有基础认识, 可以看到开源项目当中做了大量妥协和扩展, 导致有时候很简单的功能都要为了扩展而写的很复杂. 这还仅仅是作为很小的组件就要这么多依赖类, 其他的网络|文件|Actor库依赖更加庞大, 所以需要总结适合自己理解方法 这里回过头来说明配置 Configuration 类的相关工具类: org.apache.flink.configuration.WritableConfig org.apache.flink.configuration.ReadableConfig org.apache.flink.configuration.ConfigOption org.apache.flink.configuration.ConfigOptions: 主要对之前的 ConfigOption 进行构建的 Builder 上个篇章已经说明 ConfigOption 是作为 Configuration 单独配置存放内部集合中, 而 ConfigOptions 就是相当于创建器: // 这里引用官方文档的构建器例子 // simple string-valued option with a default value ConfigOption<String> tempDirs = ConfigOptions .key("tmp.dir") .stringType() .defaultValue("/tmp"); // simple integer-valued option with a default value ConfigOption<Integer> parallelism = ConfigOptions .key("application.parallelism") .intType() .defaultValue(100); // option of list of integers with a default value ConfigOption<Integer> parallelism = ConfigOptions .
    Java Flink Created Thu, 03 Jul 2025 19:58:07 +0800
  • 接下来我们想看下 POM 的 modules 节点上面的子模块, 首先 flink-annotations 可以先排除, 里面基本都是一些实验性注解和版本信息, 不过如果参与开源开发的话这个功能就比较有用. 可以学习下 FlinkVersion 版本文件设计, 这个文件在其他面向对象编程语言都能改动下就能使用(建议学习) 那么可以参照官方文档的例子来一步步解构内部源码: // pojo class WordWithCount public class WordWithCount { public String word; public int count; public WordWithCount() {} public WordWithCount(String word, int count) { this.word = word; this.count = count; } } // main method StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStreamSource<String> text = env.socketTextStream(host, port); DataStream<WordWithCount> windowCounts = text .flatMap( (FlatMapFunction<String, String>) (line, collector) -> Arrays.stream(line.split("\\s")).forEach(collector::collect) ).returns(String.class) .
    Java Flink Created Wed, 02 Jul 2025 02:37:19 +0800
  • 我个人对 akka/pekko 这类 Java-Actor 库十分喜爱, 所以在日常当中也会去为这类 Java 方案做推广; 虽然直接调用内部库函数方法就能满足日常使用, 但是作为后续扩展当中直接声明裸写是不符合工程化规范, 后续多人维护麻烦也挺多的. 如果2-3人的时候可能还不太明显, 随便架个 pom.xml 单项目管理也没什么问题, 但业务扩展出来的时候就很麻烦; 这里说下工作当中可能遇到的问题, 以下就是工作当中实际遇到: 1. 项目刚开始立项国内平台, 直接简单引入 pekko+msgpack 搭建服务端协议 2. 客户端调通之后开始编写业务逻辑, 然后开始跑运营流程追加功能业务 3. 底层依赖没有做封装和业务抽离, 所以直接在内部改动些底层功能, 实际上这时候还没问题 4. 海外需要构建个版本低于国内版本, 这是否问题就开始发作, 出现版本底层割裂问题 5. 国内版本在v1.2的时候底层函数改动参数变动, 但海外版本v1.0依赖这个函数而不依赖逻辑改动 (比如抽奖函数两边最开始一致, 但底层函数更新要求传递随机种子 seed 值, 国内版本已经更新而国外版本不需要更新避免干扰到其他用到函数) ------------------------ 从这里开始就是版本管理崩溃的前兆 ------------------- 6. 虽然这次靠着直接复制底层功能类, 手动修改函数调用方法处理完成, 但是结果终归是好的, 可以满足业务正常运行 7. 原先网络库采用 websocket 做传输, 现在为了底层性能需求需要改动成 tcp|udp 8. 第三方库引入又对两个版本产生巨大差异, 海外版本可能落后两个大版本, 但是内部业务还是要维持更新 9. 国内v2.0大版本更新了, 但是海外依旧保持v1.2版本, 这时候运营要求把v2.0国内某些功能业务移动海外v1.2 10. 最可怕的事情就发生了, 底层有着大量没有同步更新导致两边版本底层可能完全对不上, 只能人肉比对版本然后修改变动 11. 随着这种差异版本对比越来越多, 后续版本合并越来越困难, 两个版本不断人肉比对修改第三方引用和调用方式差别越来越大 12.
    Java Flink Created Tue, 01 Jul 2025 13:11:30 +0800
  • 如果长期使用 spring 全系列的开发者, 对于 依赖注入(Dependency Injection) 能够很明显体现到: /** * RestApi接口类 */ @RestController public class ExampleController { private final IService service; /** * 在构造的时候注入 IService 接口实现 */ @Autowired public ExampleController(IService service) { this.service = service; } } 这种很便利把所需的外部编写服务类注入到类内部, 省下了直接实例化new的过程尽可能把功能做好可控性. 实际工作中发现在项目之中可以随意实例化确实后患无穷, 要么直接用上下文(context)做构建器传递, 要么就直接做依赖注入管理. 但是这种强依赖 spring 在做自己项目集成的时候基本不会考虑而是考虑比较轻量化的方案容易保持项目的简洁, 社区提供的方案如下: Spring: 和 spring 集成绑定最高的依赖注入库 Guice: 谷歌推出的轻量级基于模块方式的依赖注入库 Dagger/Dagger2: Android 官方推荐的基于 Guice 依赖注入库 其他可以跳过…… 这里基本上已经说明了各自所用的场景, 所以最后按轻量化方案采用了 Guice. 如果想尽快上手可以直接引入 spring-core 底层依赖直接使用就行, 毕竟项目规划好实现时间需要尽快搭建好准备对接需求 实际上我基本上对于依赖注入就是把功能句柄注册到全局之中, 起到作为 任务管理器(Task Manager) 作用. 这里基本上引入第三方库, 之后就开始说明一些:
    Java Created Fri, 27 Jun 2025 14:12:18 +0800
  • 一般 Actor 不会是单个项目独享的, 可能会分开大量架构(比如游戏关卡,游戏战斗等)来集群处理, 这种可以作为集群在其他项目之中启动处理. pekko 内部封装其实已经很好了, 工作之中发现基本没什么需要封装, 除非有必要否则可以直接引入构建高级应用 实际上也是考虑到常常会用到搭建 Actor 管理器关系, 所以才打算学习 Erlang 处理方式让其支持 Supervisor-Worker 架构. 默认都是采用 pekko 的强类型 actor 构建, 弱类型后面传输的内容不好做控制 默认做个接口类方便作为数据容器继承: /** * 基础的数据载体, 用于被其他类型做实现衍生 */ public interface ActorCommand { } 动态实现有种泛型静态反射构建的抽象类, 可以用来做些特殊的动态抽象: import org.apache.pekko.actor.typed.Behavior; import org.apache.pekko.actor.typed.javadsl.AbstractBehavior; import org.apache.pekko.actor.typed.javadsl.ActorContext; import org.apache.pekko.actor.typed.javadsl.Behaviors; import java.lang.reflect.Constructor; /** * 自定义 Actor 运行层扩展 */ public abstract class AbstractActorSupervisor extends AbstractBehavior<ActorCommand> { /** * Actor 名称 */ protected String name; /** * 构造方法 * 默认的 */ public AbstractActorSupervisor(ActorContext<ActorCommand> context, String name) { super(context); this.
    Java Created Mon, 16 Jun 2025 16:51:16 +0800