首页 文章 精选 留言 我的

精选列表

搜索[云真机],共10000篇文章
优秀的个人博客,低调大师

FDE落地实战 01|别急着做AI:先判断这是不是一个真需求

导语:会议刚开始,老板就定下目标:“今年必须做一个AI助手。”业务部门很兴奋,技术团队立刻讨论模型、知识库和Agent框架,供应商两周后交出一个漂亮Demo。可是当项目准备接入生产系统时,大家才发现:真正使用它的人没有参加需求会;历史数据口径互相冲突;错误结果由谁复核没人确认;上线后的价值也只能用“看起来很先进”来解释。许多企业AI项目并不是做不出来,而是在问题尚未成立时就过早进入了解法。本篇给出一套FDE需求挖掘五步法:定对象、看流程、找损失、改写问题、确认责任。它帮助团队把“老板想要AI”改写成一个有用户、有场景、有证据、有边界、有指标、有责任人的业务问题,并用访谈提纲、一页式问题陈述和No-Go清单判断:应该继续、继续发现,还是暂时停止。

优秀的个人博客,低调大师

netty 高性能游戏服务器框架;ioGame 21.9 真.轻量级网络编程框架发布

ioGame 源码完全开放、最新文档阅读完全开放;使用完全自由、免费(遵守开源协议)。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。 21.9 文档与日志 ioGame javadoc api 框架版本更新日志 (yuque.com) ioGame网络游戏服务器框架 (yuque.com) 版本更新汇总 [core] #294 增加范围内的广播接口 RangeBroadcaster,业务参数支持基础类型(协议碎片)的简化使用 [core-对接文档] #293 广播文档构建器支持对参数的单独描述 [light-game-room] #297 模拟系统创建房间,RoomCreateContext 的使用 [light-game-room] #298 模拟系统创建房间,GameFlowContext 的使用 [core] #301 FlowContext 更新元信息后,需要立即生效(跨服调用时) [内置 kit] 开放 TaskListener 接口 为 SimpleRoom aggregationContext 属性提供默认值,移除 RoomCreateContext 接口的 getAggregationContext 方法,以免产生误导。 [light-game-room] 为 SimpleRoom aggregationContext 属性提供默认值 #297,模拟系统创建房间,RoomCreateContext 的使用 移除 RoomCreateContext 接口的 getAggregationContext 方法,以免产生误导。 RoomCreateContext 增加默认重载 RoomCreateContext.of(); // 无房间创建者,通常表示系统创建 RoomCreateContext.of(userId); // 房间创建者为 userId #298 模拟系统创建房间,GameFlowContext 的使用 public void test() { Room room = ...; GameFlowContext context = GameFlowContext.of(room); ... 省略部分代码 } [core] #294 增加范围内的广播接口 RangeBroadcaster,业务参数支持基础类型(协议碎片)的简化使用 public void testRangeBroadcaster(FlowContext flowContext) { // ------------ object ------------ // 广播 object DemoBroadcastMessage message = new DemoBroadcastMessage(); message.msg = "helloBroadcast --- 1"; RangeBroadcaster.of(flowContext) .setResponseMessage(cmdInfo, message); // 广播 object list List<DemoBroadcastMessage> messageList = List.of(message); RangeBroadcaster.of(flowContext) .setResponseMessageList(cmdInfo, messageList); // ------------ int ------------ // 广播 int int intValue = 1; RangeBroadcaster.of(flowContext) .setResponseMessage(cmdInfo, intValue); // 广播 int list List<Integer> intValueList = List.of(1, 2); RangeBroadcaster.of(flowContext) .setResponseMessageIntList(cmdInfo, intValueList); // ------------ long ------------ // 广播 long long longValue = 1L; RangeBroadcaster.of(flowContext) .setResponseMessage(cmdInfo, longValue); // 广播 long list List<Long> longValueList = List.of(1L, 2L); RangeBroadcaster.of(flowContext) .setResponseMessageLongList(cmdInfo, longValueList); // ------------ String ------------ // 广播 String String stringValue = "1"; RangeBroadcaster.of(flowContext) .setResponseMessage(cmdInfo, stringValue); // 广播 String list List<String> stringValueList = List.of("1L", "2L"); RangeBroadcaster.of(flowContext) .setResponseMessageStringList(cmdInfo, stringValueList); // ------------ boolean ------------ // 广播 boolean boolean boolValue = true; RangeBroadcaster.of(flowContext) .setResponseMessage(cmdInfo, boolValue); // 广播 boolean list List<Boolean> boolValueList = List.of(true, false); RangeBroadcaster.of(flowContext) .setResponseMessageBoolList(cmdInfo, boolValueList); } #301 FlowContext 更新元信息后,需要立即生效(跨服调用时) 在此之前,更新元信息后,并不会将元信息同步到 FlowContext 中,只会将元信息同步到游戏对外服中;所以在更新元信息后,紧接着执行跨服调用是不能获取新的元信息内容的。 当前 issues 会对这部分做增强,也就是在更新元信息后,会将元信息同步到 FlowContext 中;这样,在后续的跨服调用中也能获取到最新的元信息。 void test1(FlowContext flowContext) { // 获取元信息 MyAttachment attachment = flowContext.getAttachment(MyAttachment.class); attachment.nickname = "渔民小镇"; // [同步]更新 - 将元信息同步到玩家所在的游戏对外服中 flowContext.updateAttachment(attachment); // 跨服请求 CmdInfo helloCmdInfo = CmdInfo.of(1, 1); flowContext.invokeModuleMessage(helloCmdInfo); } @ActionController(1) public class DemoFightAction { @ActionMethod(1) void hello(FlowContext flowContext) { // 可以得到最新的元信息 MyAttachment attachment = flowContext.getAttachment(MyAttachment.class); log.info("{}", attachment.nickname); } } #293 广播文档构建器支持对参数的单独描述 private void extractedDco(BarSkeletonBuilder builder) { // UserCmd builder.addBroadcastDoc(BroadcastDoc.newBuilder(UserCmd.of(UserCmd.enterSquare)) .setDataClass(SquarePlayer.class) .setDescription("新玩家加入房间,给房间内的其他玩家广播") ).addBroadcastDoc(BroadcastDoc.newBuilder(UserCmd.of(UserCmd.offline)) .setDataClass(LongValue.class, "userId") .setDescription("有玩家下线了") ); } 下面是生成后的对接文档预览 ==================== 游戏文档格式说明 ==================== https://www.yuque.com/iohao/game/irth38#cJLdC ==================== FightHallAction 大厅(类似地图) ==================== 路由: 1 - 2 --- 【进入大厅】 --- 【FightHallAction:94】【enterSquare】 方法参数: EnterSquare enterSquare 进入大厅 方法返回值: ByteValueList<SquarePlayer> 所有玩家 广播推送: SquarePlayer ,(新玩家加入房间,给房间内的其他玩家广播) 路由: 1 - 5 --- 【玩家下线】 --- 【FightHallAction:154】【offline】 方法返回值: void 广播推送: LongValue userId,(有玩家下线了) [内置 kit] 开放 TaskListener 接口,TaskListener 是 TaskKit 相关的任务监听接口。 TaskListener 任务监听回调,使用场景有:一次性延时任务、任务调度、轻量可控的延时任务、轻量的定时入库辅助功能 ...等其他扩展场景。这些使用场景都有一个共同特点,即监听回调。接口提供了 4 个方法,如下 CommonTaskListener.onUpdate(),监听回调 CommonTaskListener.triggerUpdate(),是否触发 CommonTaskListener.onUpdate() 监听回调方法 CommonTaskListener.onException(Throwable) ,异常回调。在执行 CommonTaskListener.triggerUpdate() 和 CommonTaskListener.onUpdate() 方法时,如果触发了异常,异常将被该方法捕获。 CommonTaskListener.getExecutor(),指定执行器来执行上述方法,目的是不占用业务线程。 更多介绍与使用,请阅读 TaskKit (yuque.com) ioGame 使用趋势数据 关注 ioGame 的游戏服务器开发者持续增多,2022-09 ~ 至今各月的统计数据; 这里的统计信息是关于开发者关注 ioGame 框架相关的,从统计数据中可以看出,由于 ioGame 上手简单,功能强大等优点,得到了众多开发者的关注。如果你想知道 ioGame 有没有人在使用,可以先到这里看下统计数据、开发者的评价与讨论。 https://www.yuque.com/iohao/game/gpxk93#TwVa8 这里展示了每月的统计数据,统计数据来源于语雀后台,这些数据都是真实的、客观存在的、活的。 因为成本的原因,某宝某多还没有出现能提供这种服务的商家,所以这样的统计数据也更具真实性。 通过统计数据,我们可以看到每天会有很多开发者在访问 ioGame 的在线文档,并且这些统计数据不是来源于口嗨的,也不是主观创造的。 所以,还在犹豫要不要使用 ioGame 的开发者们,更应该讨论的是 “为什么这些开发者会选择使用 ioGame”,而不是 ioGame 有没有人在使用的问题。 点击我,到语雀后台查看 ioGame 的数据 ioGame 网络游戏服务器框架简介 无锁异步化、事件驱动的架构设计;轻量级,无需依赖任何第三方中间件或数据库就能支持集群、分布式 通过 ioGame 可以很容易的搭建出一个集群无中心节点、集群自动化、多进程的分布式游戏服务器 包体小、启动快、内存占用少、更加的节约、无需配置文件、提供了优雅的路由访问权限控制 可同时支持多种连接方式:WS、UDP、TCP... 等;框架已支持全链路调用日志跟踪特性 让开发者用一套业务代码,能轻松切换和扩展不同的通信协议:Protobuf、JSON 近原生的性能;业务框架在单线程中平均每秒可以执行 1152 万次业务逻辑 代码即联调文档、JSR380 验证、断言 + 异常机制 = 更少的维护成本 框架具备智能的同进程亲和性;开发中,业务代码可定位与跳转 架构部署灵活性与多样性:既可相互独立,又可相互融合 可同时与同类型的多个游戏逻辑服通信并得到数据 逻辑服之间可相互跨进程、跨机器进行通信 支持玩家对游戏逻辑服进行动态绑定 能与任何其他框架做融合共存 对 webMVC 开发者友好 无 spring 强依赖 零学习成本 javaSE 你是否想要开发一个高性能、稳定、易用、自带负载均衡、避免类爆炸设计、可跨进程跨机器通信、集群无中心节点、集群自动化、有状态多进程的分布式的网络编程服务器呢?如果是的话,这里向你推荐一个由 java 语言编写的网络编程框架 ioGame。下面将会从多个方面来对框架做一些简单的介绍。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; ioGame 有以下特点: 无锁异步化、事件驱动的架构设计 同时支持 TCP、WebSocket、UDP 多种连接方式,并且可扩展 支持 protobuf、json 等不同的通信协议 集群无中心节点、集群自动化、分布式的设计 真轻量级,不依赖任何第三方中间件或数据库就能支持集群、分布式 提供多种通讯方式,且逻辑服之间可以相互跨机器通信 框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问 ioGame 是纯 javaSE 的,使得 ioGame 能与其他框架方便的进行集成、融合;如 spring ... 等 学习成本低,开发体验好 支持多服单进程、多服多进程的启动和部署方式 提供游戏文档生成的辅助功能 包体小、启动快、内存占用少 提供优雅的路由访问权限控制 提供了灵活的线程扩展、设置 具备智能的同进程亲和性 具备全链路调用日志跟踪特性 业务框架提供了插件机制,插件是可插拨、可扩展的 JSR380 验证、断言 + 异常机制 = 更少的维护成本 action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ioGame 是一个专为网络编程设计的轻量级框架,它可以帮助你快速地搭建和运行自己的网络服务器。ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。如各种类型和规模的网络游戏,无论是 H5、手游还是 PC 游戏,无论是简单的聊天室,还是复杂的全球同服、回合制游戏、策略游戏、放置休闲游戏、即时战斗、MMORPG 等,ioGame 都可以满足你的需求。 ioGame 在打包、内存占用、启动速度等方面也是优秀的。打 jar 包后大约15MB,应用通常会在0.x 秒内完成启动,内存占用小。详细请看快速从零编写服务器完整示例。 在生态融合方面,ioGame 可以很方便的与spring 集成(5 行代码);除了 spring 外,还能与任何其他的框架做融合,如:solon... 等,从而使用其他框架的相关生态。 ioGame 在架构上解决了传统框架所产生的N*N 问题(与传统架构对比)。传统架构在扩展机器时,需要借助很多第三方中间件,如:Redis、MQ、ZooKeeper ... 等,才能满足整体架构的运作。通常,只要引入了需要安装的中间件才能做到扩展的,那么你的架构或者说框架,基本上与轻量级无缘了。 在轻量级方面,ioGame不依赖任何第三方中间件或数据库就能支持集群、分布式,只需要 java 环境就可以运行。这意味着在使用上简单了,在部署上也为企业减少了部署成本、维护难度。使用 ioGame 时,只需一个依赖即可获得整个框架,而无需安装其他服务,如: Nginx、Redis、MQ、Mysql、ZooKeeper、Protobuf 协议编译工具 ... 等。 ioGame 具备全链路调用日志跟踪特性,这在分布式下非常的实用。该特性为每个请求分配一个唯一标识,并记录在日志中,通过唯一标识可以快速的在日志中过滤出指定请求的信息。ioGame 提供的全链路调用日志跟踪特性更是强大,支持跨机器、跨进程。简单的说,从玩家的请求进来到结束,无论该请求经过了多少个游戏逻辑服,都能精准记录。 在通讯方式方面,大部分框架只能支持推送(广播)这一类型的通讯方式;ioGame 则提供了多种类型的通讯方式,通过对各种通讯方式的组合使用,可以简单完成以往难以完成的工作,并且这些通讯方式都支持跨进程、跨机器通信,且具备全链路调用日志跟踪。这些通讯方式分别是 请求响应(单次请求处理) 广播(推送) 单个逻辑服间的相互通讯(可跨机器通信、可跨进程通信) 与同类型多个逻辑服相互通讯(可跨多个机器通信、可跨多个进程通信) 脉冲通讯(可跨多个机器通信、可跨多个进程通信) 分布式事件总线(类似 MQ、Redis 发布订阅机制;可跨多个机器通信、可跨多个进程通信) 在编码风格上,ioGame 为开发者提供了类 MVC 的编码风格(无入侵的 Java Bean ),这种设计方式很好的避免了类爆炸。同时,框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问;这使得开发者所编写的代码会非常的优雅,并且具备全链路调用日志跟踪。 从 ioGame21 开始,框架添加了虚拟线程的相关支持。各逻辑服之间通信阻塞部分使用虚拟线程,这样可以很好的避免阻塞业务线程,并大幅提高了框架的吞吐量。 在线程安全方面,框架为开发者解决了单个玩家的并发问题;即使玩家重新登录后,也会使用相同的线程来消费业务,并推荐使用领域事件来解决同一房间或业务内多个玩家的并发问题。框架在线程的扩展性上提供了友好的支持,开发者可以很容易的编写出无锁并发代码,这得益于 ioGame 独有的线程执行器设计与扩展。换句话说,你不会因为并发问题烦恼。 在无锁并发方面,ioGame 提供了优雅、独特的线程执行器设计。通过该特性,开发者能轻易的编写出无锁高并发的代码。 在连接方式方面,ioGame 允许开发者使用一套业务代码,同时支持多种连接方式,无需进行任何修改。ioGame 已经支持了 TCP、WebSocket 和 UDP 连接方式,并且也支持在这几种连接方式之间进行灵活切换。连接方式是可扩展的,并且扩展操作也很简单,这意味着之后如果支持了 KCP,无论你当前项目使用的是 TCP、WebSocket 还是 UDP,都可以切换成 KCP;注意了,即使切换到 KCP 的连接方式,现有的业务代码也无需改变。 在通信协议方面,ioGame 让开发者用一套业务代码,就能轻松切换和扩展不同的通信协议,如 Protobuf、JSON 等。只需一行代码,就可以从 Protobuf 切换到 JSON,无需改变业务方法。 在增减协议方面,ioGame 可以让你在新增或减少协议时,无需重启游戏对外服与 Broker(游戏网关);这样既能避免玩家断线,又能避免因新增、减少协议而重启所有机器的痛点。 在协议碎片方面,action 支持自动装箱、拆箱基础类型特性,用于解决协议碎片的问题。同时该特性除了能使你的业务代码更加清晰以外,还能大幅提高开发者在该环节的生产力。 在集群方面,ioGame 的 Broker (游戏网关)采用无中心节点、自动化的集群设计,所有节点平等且自治,不存在单点故障。集群能够自动管理和弹性扩缩,节点加入或退出时,能够自动保证负载均衡和数据一致性,不影响服务可用性。 在分布式方面,ioGame 的逻辑服使用了分布式设计思想,将服务器分为游戏对外服、游戏逻辑服等不同层次,并且每一层都有明确的职责和接口。这样可以提高代码可读性和可维护性,并且方便进行水平扩展。 在学习成本方面,ioGame 的学习成本非常低,可以说是零学习成本,即使没有游戏编程经验,也能轻松上手。开发者只需掌握普通的 java 方法或 webMVC 相关知识,就能用框架开发业务。框架不要求开发者改变编码习惯,而是自身适应开发者的需求。 在同进程亲和性方面,在同一进程内,不同 Netty 实例之间的通信,是通过内存进行传输的,不需要经过网络传输,数据传输速度极快。同进程亲和性指的是,优先访问同进程内的游戏逻辑服,当同进程内没有能处理请求的游戏逻辑服时,才会去其他进程或机器中查找能处理请求的游戏逻辑服;简单点说,框架对于请求的处理很智能,会优先将请求给同进程内的逻辑服消费。 在开发体验方面,ioGame 非常注重开发者的开发体验;框架提供了JSR380 验证、断言 + 异常机制、业务代码定位,action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ... 等。诸多丰富的功能,使得开发者的业务代码更加的清晰、简洁; 业务框架提供了插件机制,插件是可插拨、可扩展的。框架内置提供了DebugInOut、action 调用统计、业务线程监控插件、各时间段调用统计插件... 等插件;不同的插件提供了不同的关注点,比如我们可以使用调用、监控等插件相互配合,可以让我们在开发阶段就知道是否存在性能问题。合理利用好各个插件,可以让我们在开发阶段就能知道问题所在,提前发现问题,提前预防问题。 在分布式开发体验方面,通常在开发分布式应用时是需要启动多个进程的。这会让调试与排查问题变得非常困难,从而降低开发者的效率、增加工作量等,这也是很多框架都解决不了的问题,但 ioGame 做到了!ioGame 支持多服单进程的启动方式,这使得开发者在开发和调试分布式系统时更加简单。 与前端对接联调方面,ioGame 提供了游戏文档生成的辅助功能,可以做到代码即对接文档。简单地说,当业务代码编写完后,框架会自动生成最新的文档。如果没有游戏文档的生成,那么你将要抽出一些时间来编写、维护对接文档的工作,而且当团队人数多了之后,文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。 在部署方面,ioGame 支持多服单进程的方式部署,也支持多服多进程多机器的方式部署;在部署方式上可以随意的切换而不需要更改代码。日常中我们可以按照单体思维开发,到了生产可以选择使用多进程的方式部署。 在安全方面,所有的游戏逻辑服不需要开放端口,天然地避免了扫描攻击。由于不需要为每个逻辑服分配独立的端口,那么我们在使用诸如云服务器之类的服务时,就不需要担心端口开放权限的问题了。别小看这一个环节,通常这些小细节最浪费开发者的时间。由于我们不需要管理这些 IP:Port,这部分的工作量就自然地消失了。 在模拟客户端测试方面,ioGame 提供了压测 & 模拟客户端请求模块。此模块是用于模拟客户端,简化模拟工作量,只需要编写对应请求与回调。除了可以模拟简单的请求外,通常还可以做一些复杂的请求编排,并支持复杂业务的压测。与单元测试不同的是,该模块可以模拟真实的网络环境,并且在模拟测试的过程中与服务器的交互是可持续的、可互动的,同时也是支持自动化的。 在架构灵活性方面,ioGame 的架构由三部分组成:1. 游戏对外服、2.Broker(游戏网关)、3. 游戏逻辑服;三者既可相互独立,又可相互融合。这意味着使用 ioGame 可以适应任何类型的游戏,因为只需通过调整部署方式,就可以满足不同类型的游戏需求。在 ioGame 中进行这些调整工作非常简单,而且不会对现有代码产生不良影响。 架构是可以动态扩缩的,游戏对外服、游戏逻辑服、Broker(游戏网关)都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。同时,架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 开发者基于 ioGame 编写的项目模块,通常是条理清晰的,得益于框架对路由的合理设计,同时也为路由提供了优雅的访问权限控制。当我们整理好这些模块后,对于其他开发者接管项目或后续的维护中,会是一个不错的帮助(模块的整理与建议)。或许现阶段你感受不到这块的威力,随着你深入地使用实践就能体会到这么设计的诸多好处与优势。 开发者基于 ioGame 编写的项目,通常是语法简洁的、高性能的、低延迟的。框架最低要求使用JDK21,这样即可以让项目享受到分代 ZGC带来的改进,还能享受语法上的简洁。分代 ZGC 远低于其亚毫秒级暂停时间的目标,可以在不影响游戏速度的情况下,清理掉多余的内存;这样就不会出现卡顿或者崩溃的问题了,相当于在项目中变相的引入了一位 JVM 调优大师。 综上所述,ioGame 是一个非常适合网络游戏开发的框架。可以让你轻松地创建高性能、低延迟、易扩展的游戏服务器,并且节省时间和资源。如果你想要快速地开发出令人惊艳的网络游戏,请不要犹豫,立即选择 ioGame 吧!框架屏蔽了很多复杂且重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。 框架在开发、部署、压测 & 模拟测试 ... 等,各个阶段都提供了很好的支持。相信你已经对 ioGame 有了一个初步的了解,虽然还有很多丰富的功能与特性没有介绍到,但你可以通过后续的实践过程中来深入了解。感谢你的阅读,并期待你使用 ioGame 来打造自己的游戏服务器。 ioGame 的组成 ioGame 由 [网络通信框架] 和 [业务框架] 组成 网络通信框架:职责是各服务器之间的网络通信 业务框架:职责是业务逻辑的处理方式和编写方式 网络通信框架 SOFABolt是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名字取自迪士尼动画 - 闪电狗,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架。 业务框架 如果说sofa-bolt是为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上。而业务框架正是解决业务逻辑如何方便实现这一问题上。业务框架是游戏框架的一部分,职责是简化程序员的业务逻辑实现,业务框架使程序员能够快速的开始编写游戏业务。 业务框架对于每个 action (即业务的处理方法) 都是通过asm与 Singleton、Flyweight 、Command 等设计模式结合,对 action 的获取上通过 array 来得到,是一种近原生的方式。 单线程中,业务框架平均每秒可以执行 1152 万次业务逻辑。 架构简图 通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分布式的网络游戏服务器! 从图中可以看出,游戏网关支持以集群方式启动多个实例。这个设计选择了集群的方式,因为游戏网关通常是无状态的,主要作用是解耦各逻辑服之间的关系、负载均衡、调度和转发任务。 而游戏对外服、游戏逻辑服使用分布式设计,支持启动多个相同类型的服务。这意味着,当玩家数量增加时,我们可以轻松增加相应类型的游戏逻辑服以处理更多请求。 以游戏逻辑服为例,假设我们启动了两个 A 类型的游戏逻辑服,分别为 A-1 和 A-2。当玩家向 A 类型的游戏逻辑服发起多次请求时,游戏网关会使用默认的随机负载策略将请求分配给 A-1 和 A-2 来处理。 现在我们明白,游戏对外服和游戏逻辑服都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 此外,框架还支持玩家动态绑定游戏逻辑服;玩家与游戏逻辑服绑定后,之后的请求都由该游戏逻辑服来处理。 除了游戏之外,ioGame 也适用于物联网相关项目。只需将图中的玩家视为具体的设备,即使存在数亿个设备,ioGame 的架构也可以轻松支持。从 2022 年开始,已经有一些物联网公司开始采用这一解决方案,并得到了很好的体验。 ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; 无锁异步化与事件驱动的架构设计、集群无中心节点、自带负载均衡、分布式支持、可动态增减机器、避免类爆炸的设计; 名称 扩展方式 职责 游戏对外服 分布式 与玩家连接、交互 游戏逻辑服 分布式 处理具体业务逻辑 Broker(游戏网关) 集群 调度和转发任务; 通过 ioGame 可以使得游戏编程变得简单,下面是一个业务示例 协议文件定义 首先我们自定义一个协议文件,这个协议文件作为我们的业务载体描述。这个协议是纯 java 代码编写的,使用的是 jprotobuf,jprotobuf 是对google protobuf的简化使用,性能同等。 可以把这理解成 DTO、POJO、业务数据载体等,其主要目的是用于业务数据的传输; /** 请求 */ @ProtobufClass @FieldDefaults(level = AccessLevel.PUBLIC) public class HelloReq { String name; } Action 游戏服务器的编程,游戏服务器接收业务数据后,对业务数据进行处理;下面这段代码可以同时支持 TCP、WebSocket、UDP 通信方式。 示例代码中展示了玩家的请求与响应处理,还展示了跨服(跨进程、跨机器)的请求处理的示例。无入侵的 Java Bean。 @ActionController(1) public class DemoAction { @ActionMethod(0) public HelloReq here(HelloReq helloReq) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; return newHelloReq; } // 注意,这个方法只是为了演示而写的;(ioGame21 开始支持) // 效果与上面的方法一样,只不过是用广播(推送)的方式将数据返回给请求方 @ActionMethod(0) public void here(HelloReq helloReq, FlowContext flowContext) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; flowContext.broadcastMe(newHelloReq); } // 跨服调用示例,下面分别展示了同步与异步回调的写法 void testShowInvokeModule(FlowContext flowContext) { /* * 框架为跨服请求提供了同步、异步、异步回调的编码风格 api。(ioGame21 开始支持) */ var cmdInfo = CmdInfo.of(1,0); var yourData = ... 你的请求参数 // 跨服请求(异步回调 - 无阻塞)-- 路由、请求参数、回调。 flowContext.invokeModuleMessageAsync(cmdInfo, yourData, responseMessage -> { var helloReq = responseMessage.getData(HelloReq.class); // --- 此异步回调,具备全链路调用日志跟踪 --- log.info("异步回调 : {}", helloReq); }); // 跨服请求(同步 - 阻塞)-- 路由、请求参数。 ResponseMessage responseMessage = flowContext.invokeModuleMessage(cmdInfo, yourData); var helloReq = responseMessage.getData(HelloReq.class); log.info("同步调用 : {}", helloReq); } } 一个方法(here)在业务框架中表示一个Action(一个业务动作)。 方法声明的参数是用于接收前端传入的业务数据,在方法 return 时,数据就可以被游戏前端接收到。程序员可以不需要关心业务框架的内部细节。 从上面的示例可以看出,这和普通的 java 类并无区别,同时这种设计方式避免了类爆炸。如果只负责编写游戏业务,那么对于业务框架的学习可以到此为止了。 游戏编程就是如此简单! 问:我可以开始游戏服务器的编程了吗? 是的,你已经可以开始游戏服务器的编程了。 访问示例(控制台) 当我们访问here方法时(通常由游戏前端来请求),控制台将会打印 ┏━━━━━ Debug. [(DemoAction.java:4).hello] ━━━━━ [cmd:1-0 65536] ━━━━━ [逻辑服 [xxx逻辑服] - id:[76526c134cc88232379167be83e4ddfc]] ┣ userId: 1 ┣ 参数: active : HelloReq(id=101, name=塔姆) ┣ 响应: HelloReq(name=塔姆, I'm here ) ┣ 时间: 1 ms (业务方法总耗时) ┗━━━━━ [ioGameVersion] ━━━━━ [线程:User-8-2] ━━━━━━━ [traceId:956230991452569600] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 控制台打印说明 Debug. [(DemoAction.java:4).here]: 表示执行业务的是DemoAction类下的here方法,4表示业务方法所在的代码行数。 在工具中点击控制台的DemoAction.java:4这条信息,就可以跳转到对应的代码中(快速导航到对应的代码),这是一个开发良好体验的开始! userId: 当前发起请求的 用户 id。 参数: 通常是游戏前端传入的值。 响应:通常是业务方法返回的值 ,业务框架会把这个返回值推送到游戏前端。 时间:执行业务方法总耗时,我们可根据业务方法总耗时的时长来优化业务。 路由信息:[cmd - subCmd]路由是唯一的访问地址。 ioGameVersion:表示当前所使用的 ioGame 版本。 线程:当前执行 action 所使用的线程。 traceId:全链路调用日志跟踪 id,每个请求唯一。(该特性在分布式下非常实用) 逻辑服:当前游戏逻辑服与其 id 有了以上信息,游戏开发者可以很快的定位问题。如果没有可视化的信息,开发中会浪费很多时间在前后端的沟通上。问题包括: 是否传参问题 (游戏前端说传了) 是否响应问题(游戏后端说返回了) 业务执行时长问题 (游戏前端说没收到响应, 游戏后端说早就响应了) 其中代码导航可以让开发者快速的跳转到业务类对应代码中,在多人合作的项目中,可以快速的知道业务经过了哪些方法的执行,使得我们可以快速的进行阅读或修改; 框架内置的其他功能 内置多种可选模块,可按需选择,以方便应用开发: 领域事件(轻量级单机最快 MQ --disruptor;通过领域事件模块,可为你的系统实现类似 Guava-EventBus、Spring 事件驱动模型 ApplicationEvent、业务解耦、规避并发、不阻塞主线程... 等,各种浪操作) 任务延时器(将来某个时间可对任务进行执行、暂停、取消等操作,并不是类似 Quartz 的任务调度) 多环境切换(不同运行环境下的配置支持) light-jprotobuf(补足 jprotobuf 不能让多个对象在单个 .proto 源文件中生成的需求,并简化 jprotobuf 对源文件的注释) 分布式锁(基于 Redisson 的简单实现) room 桌游、房间类,该模块是桌游类、房间类游戏的解决方案。比较适合桌游类、房间类的游戏基础搭建,基于该模型可以做一些如,炉石传说、三国杀、斗地主、麻将 ...等类似的桌游。或者说只要是房间类的游戏,该模型都适用。比如,CS、泡泡堂、飞行棋、坦克大战 ...等。 内置的其他功能: 心跳相关 用户上线、离线相关的钩子方法 UserSessions(对所有用户 UserSession 的管理,统计在线用户等) UserSession(与 channel 是 1:1 的关系,可取到对应的 userId、channel 等信息。) 登录相关(提供重复登录、顶号等相关增强功能) 业务参数基础类型 自动装箱、拆箱(解决协议碎片) 适合人群? 长期从事 web 内部系统开发人员, 想了解游戏的 刚从事游戏开发的 未从事过游戏开发,但却对其感兴趣的 对设计模式在实践中的应用和 sofa-bolt 有兴趣的学习者 可以接受新鲜事物的 想放弃祖传代码的 推荐实际编程经验一年以上的人员。 ioGame提供了丰富的在线高质量使用文档,为你的团队助力,带上你们的小伙伴一起,这样就不用手把手的教了。

优秀的个人博客,低调大师

netty 高性能游戏服务器框架;ioGame 21.8 真.轻量级网络编程框架发布

ioGame 源码完全开放、最新文档阅读完全开放;使用完全自由、免费(遵守开源协议)。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。 文档与更新日志 ioGame网络游戏服务器框架 (yuque.com) 框架版本更新日志 (yuque.com) ioGame javadoc api https://github.com/iohao/ioGame/releases/tag/21.8 版本更新汇总 [light-game-room] #278 桌游类、房间类游戏的扩展模块,简化与规范化房间管理相关的、开始游戏流程相关的、玩法操作相关的相关扩展 [core] #290 新增广播文档构建器,简化生成广播对接文档 [示例集合整理] 将 SimpleExample(文档中所有功能点的示例)、SpringBootExample(综合示例)、ioGameWeb2Game(web 转游戏 - 示例理解篇)、fxglSimpleGame(移动同步 FXGL + netty)合并成一个示例项目。 [core] #290 新增广播文档构建器,简化生成广播对接文档 下面是使用示例 public class MyLogicServer extends AbstractBrokerClientStartup { @Override public BarSkeleton createBarSkeleton() { // 业务框架构建器 BarSkeletonBuilder builder = ... // 错误码、广播、推送对接文档生成 extractedDco(builder); ​ return builder.build(); } private void extractedDco(BarSkeletonBuilder builder) { // 错误码 Arrays.stream(GameCode.values()).forEach(builder::addMsgExceptionInfo); ​ // UserCmd builder.addBroadcastDoc(BroadcastDoc.newBuilder(UserCmd.of(UserCmd.enterSquare)) .setDataClass(SquarePlayer.class) .setDescription("新玩家加入房间,给房间内的其他玩家广播") ).addBroadcastDoc(BroadcastDoc.newBuilder(UserCmd.of(UserCmd.move)) .setDataClass(SquarePlayerMove.class) .setDescription("其他玩家的移动") ).addBroadcastDoc(BroadcastDoc.newBuilder(UserCmd.of(UserCmd.offline)) .setDataClass(LongValue.class) .setDescription("有玩家下线了。userId") ); ​ // room builder.addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.roomUpdateBroadcast)) .setDataClass(FightRoomNotice.class) .setDescription("房间更新通知") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.playerEnterRoomBroadcast)) .setDataClass(FightPlayer.class) .setDescription("有新玩家加入房间") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.enterRoom)) .setDataClass(FightEnterRoom.class) .setDescription("玩家自己进入房间") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.dissolveRoomBroadcast)) .setDescription("解散房间") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.quitRoom)) .setDataClass(LongValue.class) .setDescription("有玩家退出房间了。userId") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.ready)) .setDataClass(PlayerReady.class) .setDescription("有玩家准备或取消准备了") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.nextRoundBroadcast)) .setDataClass(IntValue.class) .setDescription("对局开始,通知玩家开始选择。round 当前对局数") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.operationBroadcast)) .setDataClass(LongValue.class) .setDescription("通知其他玩家,有玩家做了选择。userId") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.littleSettleBroadcast)) .setDataClassList(FightRoundPlayerScore.class) .setDescription("广播玩家对局分数") ).addBroadcastDoc(BroadcastDoc.newBuilder(RoomCmd.of(RoomCmd.gameOverBroadcast)) .setDescription("游戏结束") ); } } 其他扩展阅读 游戏对接文档生成 (yuque.com) 示例中关于错误码可阅读 断言 + 异常机制 = 清晰简洁的代码 (yuque.com) 解决协议碎片 (yuque.com) 下面是生成后的对接文档预览 ==================== 游戏文档格式说明 ==================== https://www.yuque.com/iohao/game/irth38#cJLdC ​ ==================== FightHallAction 大厅(类似地图) ==================== 路由: 1 - 1 --- 【登录】 --- 【FightHallAction:67】【loginVerify】 方法参数: LoginVerify loginVerify 登录验证 方法返回值: UserInfo 玩家信息 路由: 1 - 2 --- 【进入大厅】 --- 【FightHallAction:95】【enterSquare】 方法参数: EnterSquare enterSquare 进入大厅 方法返回值: ByteValueList<SquarePlayer> 所有玩家 广播推送: SquarePlayer 新玩家加入房间,给房间内的其他玩家广播 路由: 1 - 4 --- 【玩家移动】 --- 【FightHallAction:131】【move】 方法参数: SquarePlayerMove squarePlayerMove 玩家移动 方法返回值: void 广播推送: SquarePlayerMove 其他玩家的移动 路由: 1 - 5 --- 【玩家下线】 --- 【FightHallAction:155】【offline】 方法返回值: void 广播推送: LongValue 有玩家下线了。userId ​ ==================== FightRoomAction ==================== 路由: 2 - 1 --- 【玩家创建新房间】 --- 【FightRoomAction:63】【createRoom】 方法返回值: void 路由: 2 - 2 --- 【玩家进入房间】 --- 【FightRoomAction:96】【enterRoom】 方法参数: LongValue roomId 房间号 方法返回值: void 房间信息 广播推送: FightEnterRoom 玩家自己进入房间 路由: 2 - 3 --- 【玩家退出房间】 --- 【FightRoomAction:120】【quitRoom】 方法返回值: void 广播推送: LongValue 有玩家退出房间了。userId 路由: 2 - 4 --- 【玩家准备】 --- 【FightRoomAction:146】【ready】 方法参数: BoolValue ready true 表示准备,false 则是取消准备 方法返回值: void 广播推送: PlayerReady 有玩家准备或取消准备了 路由: 2 - 5 --- 【房间列表】 --- 【FightRoomAction:222】【listRoom】 方法返回值: ByteValueList<FightRoomNotice> 房间列表 路由: 2 - 6 --- 【玩家在游戏中的操作】 --- 【FightRoomAction:191】【operation】 方法参数: FightOperationCommand command 玩家操作数据 方法返回值: void 路由: 2 - 7 --- 【开始游戏】 --- 【FightRoomAction:162】【startGame】 方法返回值: void ​ ==================== 其它广播推送 ==================== 路由: 2 - 51 --- 广播推送: FightRoomNotice (房间更新通知) 路由: 2 - 50 --- 广播推送: FightPlayer (有新玩家加入房间) 路由: 2 - 52 --- 广播推送: IntValue (对局开始,通知玩家开始选择。round 当前对局数) 路由: 2 - 53 --- 广播推送: LongValue (通知其他玩家,有玩家做了选择。userId) 路由: 2 - 56 --- 广播推送: none (解散房间) 路由: 2 - 54 --- 广播推送: ByteValueList<FightRoundPlayerScore> (广播玩家对局分数) 路由: 2 - 55 --- 广播推送: none (游戏结束) ==================== 错误码 ==================== -1008 : 绑定的游戏逻辑服不存在 -1007 : 强制玩家下线 -1006 : 数据不存在 -1005 : class 不存在 -1004 : 请先登录 -1003 : 心跳超时相关 -1002 : 路由错误 -1001 : 参数验错误 -1000 : 系统其它错误 1 : 玩家在房间里 3 : 房间不存在 4 : 非法操作 6 : 开始游戏需要的最小人数不足 7 : 请等待其他玩家准备 8 : 房间空间不足,人数已满 ​ [light-game-room] room 模块相关文档 - room 桌游、房间类 (yuque.com) #278 桌游类、房间类游戏的扩展模块,简化与规范化房间管理相关的、开始游戏流程相关的、玩法操作相关的相关扩展 light-game-room 房间,是 ioGame 提供的一个轻量小部件 - 可按需选择的模块。 light-game-room + 领域事件 + 内置 Kit = 轻松搞定桌游类游戏 该模块是桌游类、房间类游戏的解决方案。比较适合桌游类、房间类的游戏基础搭建,基于该模型可以做一些如,炉石传说、三国杀、斗地主、麻将 ...等类似的桌游。或者说只要是房间类的游戏,该模型都适用。比如,CS、泡泡堂、飞行棋、坦克大战 ...等。 如果你计划做一些桌游类的游戏,那么推荐你基于该模块做扩展。该模块遵循面向对象的设计原则,没有强耦合,可扩展性强。且帮助开发者屏蔽了很多重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。 主要解决的问题与职责 桌游、房间类的游戏在功能职责上可以分为 3 大类,分别是 房间管理相关的 管理着所有的房间、查询房间列表、房间的添加、房间的删除、房间与玩家之间的关联、房间查找(通过 roomId 查找、通过 userId 查找)。 开始游戏流程相关的 通常桌游、房间类的游戏都有一些固定的流程,如创建房间、玩家进入房间、玩家退出房间、解散房间、玩家准备、开始游戏 ...等。 开始游戏时,需要做开始前的验证,如房间内的玩家是否符足够 ...等,当一切符合业务时,才是真正的开始游戏。 玩法操作相关的 游戏开始后,由于不同游戏之间的具体操作是不相同的。如坦克的射击,炉石的战前选牌、出牌,麻将的吃、碰、杠、过、胡,回合制游戏的普攻、防御、技能 ...等。 由于玩法操作的不同,所以我们的玩法操作需要是可扩展的,并用于处理具体的玩法操作。同时这种扩展方式更符合单一职责,使得我们后续的扩展与维护成本更低。 以上功能职责(房间管理相关、流程相关、玩法操作相关)属于相对通用的功能。如果每款游戏都重复的做这些工作,除了枯燥之外,还将浪费巨大的人力成本。 而当前模块则能很好的帮助开发者屏蔽这些重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。更重要的是有相关文档,将来当你的团队有新进成员时,可以快速的上手。 room 实战简介 room 桌游、房间类实战(yuque.com) 文档中,我们基于该 room 模块做一个实战示例,该示例整体比较简单,多名玩家在房间里猜拳(石头、剪刀、布)得分。实战示例包括了前后端,前端使用 FXGL 引擎,这样开发者在学习时,只需 JDK 环境就可以了,而不需要安装更多的环境。启动游戏后玩家会将加入大厅(类似地图),多名玩家相互可见,并且玩家可以在大厅内移动。 下面是基于 room 模块的扩展实战 demo 源码统计与功能介绍 基于 room 模块的扩展,只需要数百行代码就完成了下述功能: 登录 玩家进入大厅(地图) 玩家可在大厅移动 玩家移动时相互可见 玩家离开大厅(玩家下线) 查询房间列表 房间信息实时变更通知(房间内有玩家数量变化,等待中、游戏中 ...等状态) 玩家创建房间 玩家进入房间 玩家退出房间 解散房间 玩家准备 开始游戏 玩家在房间内的玩法操作 游戏对接文档的生成 及各种验证(断言 + 异常机制 = 清晰简洁的代码) 正如开头介绍所说的,room 模块很好的帮助开发者屏蔽这些重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。更重要的是有相关文档,将来当你的团队有新进成员时,可以快速的上手。 部分截图展示 启动游戏后玩家会将加入大厅(类似地图),多名玩家相互可见,并且玩家可以在大厅内移动。图中左边是玩家的可移动区域,右边房间列表信息 [示例集合整理] 将 SimpleExample(文档中所有功能点的示例)、SpringBootExample(综合示例)、ioGameWeb2Game(web 转游戏 - 示例理解篇)、fxglSimpleGame(移动同步 FXGL + netty)合并成一个示例项目。 github gitee ioGame 示例集合 ioGame 示例集合 ioGame 使用趋势数据 关注 ioGame 的游戏服务器开发者持续增多,2022-09 ~ 至今各月的统计数据; 这里的统计信息是关于开发者关注 ioGame 框架相关的,从统计数据中可以看出,由于 ioGame 上手简单,功能强大等优点,得到了众多开发者的关注。如果你想知道 ioGame 有没有人在使用,可以先到这里看下统计数据、开发者的评价与讨论。 https://www.yuque.com/iohao/game/gpxk93#TwVa8 这里展示了每月的统计数据,统计数据来源于语雀后台,这些数据都是真实的、客观存在的、活的。 因为成本的原因,某宝某多还没有出现能提供这种服务的商家,所以这样的统计数据也更具真实性。 通过统计数据,我们可以看到每天会有很多开发者在访问 ioGame 的在线文档,并且这些统计数据不是来源于口嗨的,也不是主观创造的。 所以,还在犹豫要不要使用 ioGame 的开发者们,更应该讨论的是 “为什么这些开发者会选择使用 ioGame”,而不是 ioGame 有没有人在使用的问题。 点击我,到语雀后台查看 ioGame 的数据 ioGame 网络游戏服务器框架简介 无锁异步化、事件驱动的架构设计;轻量级,无需依赖任何第三方中间件或数据库就能支持集群、分布式 通过 ioGame 可以很容易的搭建出一个集群无中心节点、集群自动化、多进程的分布式游戏服务器 包体小、启动快、内存占用少、更加的节约、无需配置文件、提供了优雅的路由访问权限控制 可同时支持多种连接方式:WS、UDP、TCP... 等;框架已支持全链路调用日志跟踪特性 让开发者用一套业务代码,能轻松切换和扩展不同的通信协议:Protobuf、JSON 近原生的性能;业务框架在单线程中平均每秒可以执行 1152 万次业务逻辑 代码即联调文档、JSR380 验证、断言 + 异常机制 = 更少的维护成本 框架具备智能的同进程亲和性;开发中,业务代码可定位与跳转 架构部署灵活性与多样性:既可相互独立,又可相互融合 可同时与同类型的多个游戏逻辑服通信并得到数据 逻辑服之间可相互跨进程、跨机器进行通信 支持玩家对游戏逻辑服进行动态绑定 能与任何其他框架做融合共存 对 webMVC 开发者友好 无 spring 强依赖 零学习成本 javaSE 你是否想要开发一个高性能、稳定、易用、自带负载均衡、避免类爆炸设计、可跨进程跨机器通信、集群无中心节点、集群自动化、有状态多进程的分布式的网络编程服务器呢?如果是的话,这里向你推荐一个由 java 语言编写的网络编程框架 ioGame。下面将会从多个方面来对框架做一些简单的介绍。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; ioGame 有以下特点: 无锁异步化、事件驱动的架构设计 同时支持 TCP、WebSocket、UDP 多种连接方式,并且可扩展 支持 protobuf、json 等不同的通信协议 集群无中心节点、集群自动化、分布式的设计 真轻量级,不依赖任何第三方中间件或数据库就能支持集群、分布式 提供多种通讯方式,且逻辑服之间可以相互跨机器通信 框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问 ioGame 是纯 javaSE 的,使得 ioGame 能与其他框架方便的进行集成、融合;如 spring ... 等 学习成本低,开发体验好 支持多服单进程、多服多进程的启动和部署方式 提供游戏文档生成的辅助功能 包体小、启动快、内存占用少 提供优雅的路由访问权限控制 提供了灵活的线程扩展、设置 具备智能的同进程亲和性 具备全链路调用日志跟踪特性 业务框架提供了插件机制,插件是可插拨、可扩展的 JSR380 验证、断言 + 异常机制 = 更少的维护成本 action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ioGame 是一个专为网络编程设计的轻量级框架,它可以帮助你快速地搭建和运行自己的网络服务器。ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。如各种类型和规模的网络游戏,无论是 H5、手游还是 PC 游戏,无论是简单的聊天室,还是复杂的全球同服、回合制游戏、策略游戏、放置休闲游戏、即时战斗、MMORPG 等,ioGame 都可以满足你的需求。 ioGame 在打包、内存占用、启动速度等方面也是优秀的。打 jar 包后大约15MB,应用通常会在0.x 秒内完成启动,内存占用小。详细请看快速从零编写服务器完整示例。 在生态融合方面,ioGame 可以很方便的与spring 集成(5 行代码);除了 spring 外,还能与任何其他的框架做融合,如:solon... 等,从而使用其他框架的相关生态。 ioGame 在架构上解决了传统框架所产生的N*N 问题(与传统架构对比)。传统架构在扩展机器时,需要借助很多第三方中间件,如:Redis、MQ、ZooKeeper ... 等,才能满足整体架构的运作。通常,只要引入了需要安装的中间件才能做到扩展的,那么你的架构或者说框架,基本上与轻量级无缘了。 在轻量级方面,ioGame不依赖任何第三方中间件或数据库就能支持集群、分布式,只需要 java 环境就可以运行。这意味着在使用上简单了,在部署上也为企业减少了部署成本、维护难度。使用 ioGame 时,只需一个依赖即可获得整个框架,而无需安装其他服务,如: Nginx、Redis、MQ、Mysql、ZooKeeper、Protobuf 协议编译工具 ... 等。 ioGame 具备全链路调用日志跟踪特性,这在分布式下非常的实用。该特性为每个请求分配一个唯一标识,并记录在日志中,通过唯一标识可以快速的在日志中过滤出指定请求的信息。ioGame 提供的全链路调用日志跟踪特性更是强大,支持跨机器、跨进程。简单的说,从玩家的请求进来到结束,无论该请求经过了多少个游戏逻辑服,都能精准记录。 在通讯方式方面,大部分框架只能支持推送(广播)这一类型的通讯方式;ioGame 则提供了多种类型的通讯方式,通过对各种通讯方式的组合使用,可以简单完成以往难以完成的工作,并且这些通讯方式都支持跨进程、跨机器通信,且具备全链路调用日志跟踪。这些通讯方式分别是 请求响应(单次请求处理) 广播(推送) 单个逻辑服间的相互通讯(可跨机器通信、可跨进程通信) 与同类型多个逻辑服相互通讯(可跨多个机器通信、可跨多个进程通信) 脉冲通讯(可跨多个机器通信、可跨多个进程通信) 分布式事件总线(类似 MQ、Redis 发布订阅机制;可跨多个机器通信、可跨多个进程通信) 在编码风格上,ioGame 为开发者提供了类 MVC 的编码风格(无入侵的 Java Bean ),这种设计方式很好的避免了类爆炸。同时,框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问;这使得开发者所编写的代码会非常的优雅,并且具备全链路调用日志跟踪。 从 ioGame21 开始,框架添加了虚拟线程的相关支持。各逻辑服之间通信阻塞部分使用虚拟线程,这样可以很好的避免阻塞业务线程,并大幅提高了框架的吞吐量。 在线程安全方面,框架为开发者解决了单个玩家的并发问题;即使玩家重新登录后,也会使用相同的线程来消费业务,并推荐使用领域事件来解决同一房间或业务内多个玩家的并发问题。框架在线程的扩展性上提供了友好的支持,开发者可以很容易的编写出无锁并发代码,这得益于 ioGame 独有的线程执行器设计与扩展。换句话说,你不会因为并发问题烦恼。 在无锁并发方面,ioGame 提供了优雅、独特的线程执行器设计。通过该特性,开发者能轻易的编写出无锁高并发的代码。 在连接方式方面,ioGame 允许开发者使用一套业务代码,同时支持多种连接方式,无需进行任何修改。ioGame 已经支持了 TCP、WebSocket 和 UDP 连接方式,并且也支持在这几种连接方式之间进行灵活切换。连接方式是可扩展的,并且扩展操作也很简单,这意味着之后如果支持了 KCP,无论你当前项目使用的是 TCP、WebSocket 还是 UDP,都可以切换成 KCP;注意了,即使切换到 KCP 的连接方式,现有的业务代码也无需改变。 在通信协议方面,ioGame 让开发者用一套业务代码,就能轻松切换和扩展不同的通信协议,如 Protobuf、JSON 等。只需一行代码,就可以从 Protobuf 切换到 JSON,无需改变业务方法。 在增减协议方面,ioGame 可以让你在新增或减少协议时,无需重启游戏对外服与 Broker(游戏网关);这样既能避免玩家断线,又能避免因新增、减少协议而重启所有机器的痛点。 在协议碎片方面,action 支持自动装箱、拆箱基础类型特性,用于解决协议碎片的问题。同时该特性除了能使你的业务代码更加清晰以外,还能大幅提高开发者在该环节的生产力。 在集群方面,ioGame 的 Broker (游戏网关)采用无中心节点、自动化的集群设计,所有节点平等且自治,不存在单点故障。集群能够自动管理和弹性扩缩,节点加入或退出时,能够自动保证负载均衡和数据一致性,不影响服务可用性。 在分布式方面,ioGame 的逻辑服使用了分布式设计思想,将服务器分为游戏对外服、游戏逻辑服等不同层次,并且每一层都有明确的职责和接口。这样可以提高代码可读性和可维护性,并且方便进行水平扩展。 在学习成本方面,ioGame 的学习成本非常低,可以说是零学习成本,即使没有游戏编程经验,也能轻松上手。开发者只需掌握普通的 java 方法或 webMVC 相关知识,就能用框架开发业务。框架不要求开发者改变编码习惯,而是自身适应开发者的需求。 在同进程亲和性方面,在同一进程内,不同 Netty 实例之间的通信,是通过内存进行传输的,不需要经过网络传输,数据传输速度极快。同进程亲和性指的是,优先访问同进程内的游戏逻辑服,当同进程内没有能处理请求的游戏逻辑服时,才会去其他进程或机器中查找能处理请求的游戏逻辑服;简单点说,框架对于请求的处理很智能,会优先将请求给同进程内的逻辑服消费。 在开发体验方面,ioGame 非常注重开发者的开发体验;框架提供了JSR380 验证、断言 + 异常机制、业务代码定位,action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ... 等。诸多丰富的功能,使得开发者的业务代码更加的清晰、简洁; 业务框架提供了插件机制,插件是可插拨、可扩展的。框架内置提供了DebugInOut、action 调用统计、业务线程监控插件、各时间段调用统计插件... 等插件;不同的插件提供了不同的关注点,比如我们可以使用调用、监控等插件相互配合,可以让我们在开发阶段就知道是否存在性能问题。合理利用好各个插件,可以让我们在开发阶段就能知道问题所在,提前发现问题,提前预防问题。 在分布式开发体验方面,通常在开发分布式应用时是需要启动多个进程的。这会让调试与排查问题变得非常困难,从而降低开发者的效率、增加工作量等,这也是很多框架都解决不了的问题,但 ioGame 做到了!ioGame 支持多服单进程的启动方式,这使得开发者在开发和调试分布式系统时更加简单。 与前端对接联调方面,ioGame 提供了游戏文档生成的辅助功能,可以做到代码即对接文档。简单地说,当业务代码编写完后,框架会自动生成最新的文档。如果没有游戏文档的生成,那么你将要抽出一些时间来编写、维护对接文档的工作,而且当团队人数多了之后,文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。 在部署方面,ioGame 支持多服单进程的方式部署,也支持多服多进程多机器的方式部署;在部署方式上可以随意的切换而不需要更改代码。日常中我们可以按照单体思维开发,到了生产可以选择使用多进程的方式部署。 在安全方面,所有的游戏逻辑服不需要开放端口,天然地避免了扫描攻击。由于不需要为每个逻辑服分配独立的端口,那么我们在使用诸如云服务器之类的服务时,就不需要担心端口开放权限的问题了。别小看这一个环节,通常这些小细节最浪费开发者的时间。由于我们不需要管理这些 IP:Port,这部分的工作量就自然地消失了。 在模拟客户端测试方面,ioGame 提供了压测 & 模拟客户端请求模块。此模块是用于模拟客户端,简化模拟工作量,只需要编写对应请求与回调。除了可以模拟简单的请求外,通常还可以做一些复杂的请求编排,并支持复杂业务的压测。与单元测试不同的是,该模块可以模拟真实的网络环境,并且在模拟测试的过程中与服务器的交互是可持续的、可互动的,同时也是支持自动化的。 在架构灵活性方面,ioGame 的架构由三部分组成:1. 游戏对外服、2.Broker(游戏网关)、3. 游戏逻辑服;三者既可相互独立,又可相互融合。这意味着使用 ioGame 可以适应任何类型的游戏,因为只需通过调整部署方式,就可以满足不同类型的游戏需求。在 ioGame 中进行这些调整工作非常简单,而且不会对现有代码产生不良影响。 架构是可以动态扩缩的,游戏对外服、游戏逻辑服、Broker(游戏网关)都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。同时,架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 开发者基于 ioGame 编写的项目模块,通常是条理清晰的,得益于框架对路由的合理设计,同时也为路由提供了优雅的访问权限控制。当我们整理好这些模块后,对于其他开发者接管项目或后续的维护中,会是一个不错的帮助(模块的整理与建议)。或许现阶段你感受不到这块的威力,随着你深入地使用实践就能体会到这么设计的诸多好处与优势。 开发者基于 ioGame 编写的项目,通常是语法简洁的、高性能的、低延迟的。框架最低要求使用JDK21,这样即可以让项目享受到分代 ZGC带来的改进,还能享受语法上的简洁。分代 ZGC 远低于其亚毫秒级暂停时间的目标,可以在不影响游戏速度的情况下,清理掉多余的内存;这样就不会出现卡顿或者崩溃的问题了,相当于在项目中变相的引入了一位 JVM 调优大师。 综上所述,ioGame 是一个非常适合网络游戏开发的框架。可以让你轻松地创建高性能、低延迟、易扩展的游戏服务器,并且节省时间和资源。如果你想要快速地开发出令人惊艳的网络游戏,请不要犹豫,立即选择 ioGame 吧!框架屏蔽了很多复杂且重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。 框架在开发、部署、压测 & 模拟测试 ... 等,各个阶段都提供了很好的支持。相信你已经对 ioGame 有了一个初步的了解,虽然还有很多丰富的功能与特性没有介绍到,但你可以通过后续的实践过程中来深入了解。感谢你的阅读,并期待你使用 ioGame 来打造自己的游戏服务器。 ioGame 的组成 ioGame 由 [网络通信框架] 和 [业务框架] 组成 网络通信框架:职责是各服务器之间的网络通信 业务框架:职责是业务逻辑的处理方式和编写方式 网络通信框架 SOFABolt是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名字取自迪士尼动画 - 闪电狗,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架。 业务框架 如果说sofa-bolt是为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上。而业务框架正是解决业务逻辑如何方便实现这一问题上。业务框架是游戏框架的一部分,职责是简化程序员的业务逻辑实现,业务框架使程序员能够快速的开始编写游戏业务。 业务框架对于每个 action (即业务的处理方法) 都是通过asm与 Singleton、Flyweight 、Command 等设计模式结合,对 action 的获取上通过 array 来得到,是一种近原生的方式。 单线程中,业务框架平均每秒可以执行 1152 万次业务逻辑。 架构简图 通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分布式的网络游戏服务器! 从图中可以看出,游戏网关支持以集群方式启动多个实例。这个设计选择了集群的方式,因为游戏网关通常是无状态的,主要作用是解耦各逻辑服之间的关系、负载均衡、调度和转发任务。 而游戏对外服、游戏逻辑服使用分布式设计,支持启动多个相同类型的服务。这意味着,当玩家数量增加时,我们可以轻松增加相应类型的游戏逻辑服以处理更多请求。 以游戏逻辑服为例,假设我们启动了两个 A 类型的游戏逻辑服,分别为 A-1 和 A-2。当玩家向 A 类型的游戏逻辑服发起多次请求时,游戏网关会使用默认的随机负载策略将请求分配给 A-1 和 A-2 来处理。 现在我们明白,游戏对外服和游戏逻辑服都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 此外,框架还支持玩家动态绑定游戏逻辑服;玩家与游戏逻辑服绑定后,之后的请求都由该游戏逻辑服来处理。 除了游戏之外,ioGame 也适用于物联网相关项目。只需将图中的玩家视为具体的设备,即使存在数亿个设备,ioGame 的架构也可以轻松支持。从 2022 年开始,已经有一些物联网公司开始采用这一解决方案,并得到了很好的体验。 ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; 无锁异步化与事件驱动的架构设计、集群无中心节点、自带负载均衡、分布式支持、可动态增减机器、避免类爆炸的设计; 名称 扩展方式 职责 游戏对外服 分布式 与玩家连接、交互 游戏逻辑服 分布式 处理具体业务逻辑 Broker(游戏网关) 集群 调度和转发任务; 通过 ioGame 可以使得游戏编程变得简单,下面是一个业务示例 协议文件定义 首先我们自定义一个协议文件,这个协议文件作为我们的业务载体描述。这个协议是纯 java 代码编写的,使用的是 jprotobuf,jprotobuf 是对google protobuf的简化使用,性能同等。 可以把这理解成 DTO、POJO、业务数据载体等,其主要目的是用于业务数据的传输; /** 请求 */ @ProtobufClass @FieldDefaults(level = AccessLevel.PUBLIC) public class HelloReq { String name; } Action 游戏服务器的编程,游戏服务器接收业务数据后,对业务数据进行处理;下面这段代码可以同时支持 TCP、WebSocket、UDP 通信方式。 示例代码中展示了玩家的请求与响应处理,还展示了跨服(跨进程、跨机器)的请求处理的示例。无入侵的 Java Bean。 @ActionController(1) public class DemoAction { @ActionMethod(0) public HelloReq here(HelloReq helloReq) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; return newHelloReq; } // 注意,这个方法只是为了演示而写的;(ioGame21 开始支持) // 效果与上面的方法一样,只不过是用广播(推送)的方式将数据返回给请求方 @ActionMethod(0) public void here(HelloReq helloReq, FlowContext flowContext) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; flowContext.broadcastMe(newHelloReq); } // 跨服调用示例,下面分别展示了同步与异步回调的写法 void testShowInvokeModule(FlowContext flowContext) { /* * 框架为跨服请求提供了同步、异步、异步回调的编码风格 api。(ioGame21 开始支持) */ var cmdInfo = CmdInfo.of(1,0); var yourData = ... 你的请求参数 // 跨服请求(异步回调 - 无阻塞)-- 路由、请求参数、回调。 flowContext.invokeModuleMessageAsync(cmdInfo, yourData, responseMessage -> { var helloReq = responseMessage.getData(HelloReq.class); // --- 此异步回调,具备全链路调用日志跟踪 --- log.info("异步回调 : {}", helloReq); }); // 跨服请求(同步 - 阻塞)-- 路由、请求参数。 ResponseMessage responseMessage = flowContext.invokeModuleMessage(cmdInfo, yourData); var helloReq = responseMessage.getData(HelloReq.class); log.info("同步调用 : {}", helloReq); } } 一个方法(here)在业务框架中表示一个Action(一个业务动作)。 方法声明的参数是用于接收前端传入的业务数据,在方法 return 时,数据就可以被游戏前端接收到。程序员可以不需要关心业务框架的内部细节。 从上面的示例可以看出,这和普通的 java 类并无区别,同时这种设计方式避免了类爆炸。如果只负责编写游戏业务,那么对于业务框架的学习可以到此为止了。 游戏编程就是如此简单! 问:我可以开始游戏服务器的编程了吗? 是的,你已经可以开始游戏服务器的编程了。 访问示例(控制台) 当我们访问here方法时(通常由游戏前端来请求),控制台将会打印 ┏━━━━━ Debug. [(DemoAction.java:4).hello] ━━━━━ [cmd:1-0 65536] ━━━━━ [逻辑服 [xxx逻辑服] - id:[76526c134cc88232379167be83e4ddfc]] ┣ userId: 1 ┣ 参数: active : HelloReq(id=101, name=塔姆) ┣ 响应: HelloReq(name=塔姆, I'm here ) ┣ 时间: 1 ms (业务方法总耗时) ┗━━━━━ [ioGameVersion] ━━━━━ [线程:User-8-2] ━━━━━━━ [traceId:956230991452569600] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 控制台打印说明 Debug. [(DemoAction.java:4).here]: 表示执行业务的是DemoAction类下的here方法,4表示业务方法所在的代码行数。 在工具中点击控制台的DemoAction.java:4这条信息,就可以跳转到对应的代码中(快速导航到对应的代码),这是一个开发良好体验的开始! userId: 当前发起请求的 用户 id。 参数: 通常是游戏前端传入的值。 响应:通常是业务方法返回的值 ,业务框架会把这个返回值推送到游戏前端。 时间:执行业务方法总耗时,我们可根据业务方法总耗时的时长来优化业务。 路由信息:[cmd - subCmd]路由是唯一的访问地址。 ioGameVersion:表示当前所使用的 ioGame 版本。 线程:当前执行 action 所使用的线程。 traceId:全链路调用日志跟踪 id,每个请求唯一。(该特性在分布式下非常实用) 逻辑服:当前游戏逻辑服与其 id 有了以上信息,游戏开发者可以很快的定位问题。如果没有可视化的信息,开发中会浪费很多时间在前后端的沟通上。问题包括: 是否传参问题 (游戏前端说传了) 是否响应问题(游戏后端说返回了) 业务执行时长问题 (游戏前端说没收到响应, 游戏后端说早就响应了) 其中代码导航可以让开发者快速的跳转到业务类对应代码中,在多人合作的项目中,可以快速的知道业务经过了哪些方法的执行,使得我们可以快速的进行阅读或修改; 框架内置的其他功能 内置多种可选模块,可按需选择,以方便应用开发: 领域事件(轻量级单机最快 MQ --disruptor;通过领域事件模块,可为你的系统实现类似 Guava-EventBus、Spring 事件驱动模型 ApplicationEvent、业务解耦、规避并发、不阻塞主线程... 等,各种浪操作) 任务延时器(将来某个时间可对任务进行执行、暂停、取消等操作,并不是类似 Quartz 的任务调度) 多环境切换(不同运行环境下的配置支持) light-jprotobuf(补足 jprotobuf 不能让多个对象在单个 .proto 源文件中生成的需求,并简化 jprotobuf 对源文件的注释) 分布式锁(基于 Redisson 的简单实现) 内置的其他功能: 心跳相关 用户上线、离线相关的钩子方法 UserSessions(对所有用户 UserSession 的管理,统计在线用户等) UserSession(与 channel 是 1:1 的关系,可取到对应的 userId、channel 等信息。) 登录相关(提供重复登录、顶号等相关增强功能) 业务参数基础类型 自动装箱、拆箱(解决协议碎片) 适合人群? 长期从事 web 内部系统开发人员, 想了解游戏的 刚从事游戏开发的 未从事过游戏开发,但却对其感兴趣的 对设计模式在实践中的应用和 sofa-bolt 有兴趣的学习者 可以接受新鲜事物的 想放弃祖传代码的 推荐实际编程经验一年以上的人员。 ioGame提供了丰富的在线高质量使用文档,为你的团队助力,带上你们的小伙伴一起,这样就不用手把手的教了。

优秀的个人博客,低调大师

netty 高性能游戏服务器框架;ioGame 21.7 真.轻量级网络编程框架发布

ioGame 源码完全开放、最新文档阅读完全开放;使用完全自由、免费(遵守开源协议)。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。 文档与日志 框架版本更新日志 (yuque.com) ioGame网络游戏服务器框架 (yuque.com) Release ioGame 21.7,java 高性能游戏服务器框架;netty 高性能游戏服务器框架 · iohao/ioGame (github.com) 版本更新汇总 [core] #112 protobuf 协议类添加检测,通过 action 构建时的监听器实现 [core] #272 业务框架 - 提供 action 构建时的监听回调 [core] #274 优化、提速 - 预生成 jprotobuf 协议类的代理,通过 action 构建时的监听器实现 [broker] fix #277 、#280 偶现 BrokerClientType 为空 [external] #271 游戏对外服 - 内置与可选 handler - log 相关的打印(触发异常、断开连接时) [room] 简化命名: AbstractPlayer --> Player、AbstractRoom --> Room 其他优化:预先生成游戏对外服统一协议的代理类及内置的协议碎片 (yuque.com)相关代理类,优化 action 参数解析 [external] #271 游戏对外服 - 内置与可选 handler - log 相关的打印(触发异常、断开连接时) 其他参考 内置与可选的 Handler (yuque.com) [core] #272 业务框架 - 提供 action 构建时的监听回调 开发者可以利用 ActionParserListener 接口来观察 action 构建过程,或者做一些额外的扩展。 扩展示例参考 // 简单打印 public final class YourActionParserListener implements ActionParserListener { @Override public void onActionCommand(ActionParserContext context) { ActionCommand actionCommand = context.getActionCommand(); log.info(actionCommand); } } ​ void test() { BarSkeletonBuilder builder = ...; builder.addActionParserListener(new YourActionParserListener()); } #112 protobuf 协议类添加检测,通过 action 构建时的监听器实现 如果当前使用的编解码器为 ProtoDataCodec 时,当 action 的参数或返回值的类没有添加 ProtobufClass 注解时(通常是忘记添加),给予一些警告提示。 // 该协议类没有添加 ProtobufClass 注解 class Bird { public String name; } ​ @ActionController(1) public class MyAction { @ActionMethod(1) public Bird testObject() { return new Bird(); } } 警告打印参考 ======== 注意,协议类没有添加 ProtobufClass 注解 ======== class com.iohao.game.action.skeleton.core.action.Bird #274 优化、提速 - 预生成 jprotobuf 协议类的代理,通过 action 构建时的监听器实现 如果当前使用的编解码器为 ProtoDataCodec 时,会在启动时就预先生成好 jprotobuf 协议类对应的代理类(用于 .proto 相关的 编码、解码),而不必等到用时在创建该代理类。从而达到整体优化提速的效果。 在此之前,在没做其他设置的情况下,首次访问 action 时,如果参数使用的 jprotobuf 协议类,那么在解码该参数时,会通过 ProtobufProxy.create 来创建对应的代理类(类似 .proto 相关的 编码、解码)。之后再访问时,才会从缓存中取到对应的代理类。 该优化默认开启,开发者可以不需要使用与配置跟 jprotobuf-precompile-plugin 插件相关的了。 已经预先生成的代理类有 游戏对外服统一协议 ExternalMessage (yuque.com) 所有开发者定义的 action 的方法参数及返回值 解决协议碎片 (yuque.com)相关,如 int、int list、String、String list、long、long list、ByteValueList ...等 [room] 简化命名: AbstractPlayer --> Player、AbstractRoom --> Room 其他优化 优化 action 参数解析 ioGame 使用趋势数据 关注 ioGame 的游戏服务器开发者持续增多,2022-09 ~ 至今各月的统计数据; 这里的统计信息是关于开发者关注 ioGame 框架相关的,从统计数据中可以看出,由于 ioGame 上手简单,功能强大等优点,得到了众多开发者的关注。如果你想知道 ioGame 有没有人在使用,可以先到这里看下统计数据、开发者的评价与讨论。 https://www.yuque.com/iohao/game/gpxk93#TwVa8 这里展示了每月的统计数据,统计数据来源于语雀后台,这些数据都是真实的、客观存在的、活的。 因为成本的原因,某宝某多还没有出现能提供这种服务的商家,所以这样的统计数据也更具真实性。 通过统计数据,我们可以看到每天会有很多开发者在访问 ioGame 的在线文档,并且这些统计数据不是来源于口嗨的,也不是主观创造的。 所以,还在犹豫要不要使用 ioGame 的开发者们,更应该讨论的是 “为什么这些开发者会选择使用 ioGame”,而不是 ioGame 有没有人在使用的问题。 点击我,到语雀后台查看 ioGame 的数据 ioGame 网络游戏服务器框架简介 无锁异步化、事件驱动的架构设计;轻量级,无需依赖任何第三方中间件或数据库就能支持集群、分布式 通过 ioGame 可以很容易的搭建出一个集群无中心节点、集群自动化、多进程的分布式游戏服务器 包体小、启动快、内存占用少、更加的节约、无需配置文件、提供了优雅的路由访问权限控制 可同时支持多种连接方式:WS、UDP、TCP... 等;框架已支持全链路调用日志跟踪特性 让开发者用一套业务代码,能轻松切换和扩展不同的通信协议:Protobuf、JSON 近原生的性能;业务框架在单线程中平均每秒可以执行 1152 万次业务逻辑 代码即联调文档、JSR380 验证、断言 + 异常机制 = 更少的维护成本 框架具备智能的同进程亲和性;开发中,业务代码可定位与跳转 架构部署灵活性与多样性:既可相互独立,又可相互融合 可同时与同类型的多个游戏逻辑服通信并得到数据 逻辑服之间可相互跨进程、跨机器进行通信 支持玩家对游戏逻辑服进行动态绑定 能与任何其他框架做融合共存 对 webMVC 开发者友好 无 spring 强依赖 零学习成本 javaSE 你是否想要开发一个高性能、稳定、易用、自带负载均衡、避免类爆炸设计、可跨进程跨机器通信、集群无中心节点、集群自动化、有状态多进程的分布式的网络编程服务器呢?如果是的话,这里向你推荐一个由 java 语言编写的网络编程框架 ioGame。下面将会从多个方面来对框架做一些简单的介绍。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; ioGame 有以下特点: 无锁异步化、事件驱动的架构设计 同时支持 TCP、WebSocket、UDP 多种连接方式,并且可扩展 支持 protobuf、json 等不同的通信协议 集群无中心节点、集群自动化、分布式的设计 真轻量级,不依赖任何第三方中间件或数据库就能支持集群、分布式 提供多种通讯方式,且逻辑服之间可以相互跨机器通信 框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问 ioGame 是纯 javaSE 的,使得 ioGame 能与其他框架方便的进行集成、融合;如 spring ... 等 学习成本低,开发体验好 支持多服单进程、多服多进程的启动和部署方式 提供游戏文档生成的辅助功能 包体小、启动快、内存占用少 提供优雅的路由访问权限控制 提供了灵活的线程扩展、设置 具备智能的同进程亲和性 具备全链路调用日志跟踪特性 业务框架提供了插件机制,插件是可插拨、可扩展的 JSR380 验证、断言 + 异常机制 = 更少的维护成本 action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ioGame 是一个专为网络编程设计的轻量级框架,它可以帮助你快速地搭建和运行自己的网络服务器。ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。如各种类型和规模的网络游戏,无论是 H5、手游还是 PC 游戏,无论是简单的聊天室,还是复杂的全球同服、回合制游戏、策略游戏、放置休闲游戏、即时战斗、MMORPG 等,ioGame 都可以满足你的需求。 ioGame 在打包、内存占用、启动速度等方面也是优秀的。打 jar 包后大约15MB,应用通常会在0.x 秒内完成启动,内存占用小。详细请看快速从零编写服务器完整示例。 在生态融合方面,ioGame 可以很方便的与spring 集成(5 行代码);除了 spring 外,还能与任何其他的框架做融合,如:solon... 等,从而使用其他框架的相关生态。 ioGame 在架构上解决了传统框架所产生的N*N 问题(与传统架构对比)。传统架构在扩展机器时,需要借助很多第三方中间件,如:Redis、MQ、ZooKeeper ... 等,才能满足整体架构的运作。通常,只要引入了需要安装的中间件才能做到扩展的,那么你的架构或者说框架,基本上与轻量级无缘了。 在轻量级方面,ioGame不依赖任何第三方中间件或数据库就能支持集群、分布式,只需要 java 环境就可以运行。这意味着在使用上简单了,在部署上也为企业减少了部署成本、维护难度。使用 ioGame 时,只需一个依赖即可获得整个框架,而无需安装其他服务,如: Nginx、Redis、MQ、Mysql、ZooKeeper、Protobuf 协议编译工具 ... 等。 ioGame 具备全链路调用日志跟踪特性,这在分布式下非常的实用。该特性为每个请求分配一个唯一标识,并记录在日志中,通过唯一标识可以快速的在日志中过滤出指定请求的信息。ioGame 提供的全链路调用日志跟踪特性更是强大,支持跨机器、跨进程。简单的说,从玩家的请求进来到结束,无论该请求经过了多少个游戏逻辑服,都能精准记录。 在通讯方式方面,大部分框架只能支持推送(广播)这一类型的通讯方式;ioGame 则提供了多种类型的通讯方式,通过对各种通讯方式的组合使用,可以简单完成以往难以完成的工作,并且这些通讯方式都支持跨进程、跨机器通信,且具备全链路调用日志跟踪。这些通讯方式分别是 请求响应(单次请求处理) 广播(推送) 单个逻辑服间的相互通讯(可跨机器通信、可跨进程通信) 与同类型多个逻辑服相互通讯(可跨多个机器通信、可跨多个进程通信) 脉冲通讯(可跨多个机器通信、可跨多个进程通信) 分布式事件总线(类似 MQ、Redis 发布订阅机制;可跨多个机器通信、可跨多个进程通信) 在编码风格上,ioGame 为开发者提供了类 MVC 的编码风格(无入侵的 Java Bean ),这种设计方式很好的避免了类爆炸。同时,框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问;这使得开发者所编写的代码会非常的优雅,并且具备全链路调用日志跟踪。 从 ioGame21 开始,框架添加了虚拟线程的相关支持。各逻辑服之间通信阻塞部分使用虚拟线程,这样可以很好的避免阻塞业务线程,并大幅提高了框架的吞吐量。 在线程安全方面,框架为开发者解决了单个玩家的并发问题;即使玩家重新登录后,也会使用相同的线程来消费业务,并推荐使用领域事件来解决同一房间或业务内多个玩家的并发问题。框架在线程的扩展性上提供了友好的支持,开发者可以很容易的编写出无锁并发代码,这得益于 ioGame 独有的线程执行器设计与扩展。换句话说,你不会因为并发问题烦恼。 在无锁并发方面,ioGame 提供了优雅、独特的线程执行器设计。通过该特性,开发者能轻易的编写出无锁高并发的代码。 在连接方式方面,ioGame 允许开发者使用一套业务代码,同时支持多种连接方式,无需进行任何修改。ioGame 已经支持了 TCP、WebSocket 和 UDP 连接方式,并且也支持在这几种连接方式之间进行灵活切换。连接方式是可扩展的,并且扩展操作也很简单,这意味着之后如果支持了 KCP,无论你当前项目使用的是 TCP、WebSocket 还是 UDP,都可以切换成 KCP;注意了,即使切换到 KCP 的连接方式,现有的业务代码也无需改变。 在通信协议方面,ioGame 让开发者用一套业务代码,就能轻松切换和扩展不同的通信协议,如 Protobuf、JSON 等。只需一行代码,就可以从 Protobuf 切换到 JSON,无需改变业务方法。 在增减协议方面,ioGame 可以让你在新增或减少协议时,无需重启游戏对外服与 Broker(游戏网关);这样既能避免玩家断线,又能避免因新增、减少协议而重启所有机器的痛点。 在协议碎片方面,action 支持自动装箱、拆箱基础类型特性,用于解决协议碎片的问题。同时该特性除了能使你的业务代码更加清晰以外,还能大幅提高开发者在该环节的生产力。 在集群方面,ioGame 的 Broker (游戏网关)采用无中心节点、自动化的集群设计,所有节点平等且自治,不存在单点故障。集群能够自动管理和弹性扩缩,节点加入或退出时,能够自动保证负载均衡和数据一致性,不影响服务可用性。 在分布式方面,ioGame 的逻辑服使用了分布式设计思想,将服务器分为游戏对外服、游戏逻辑服等不同层次,并且每一层都有明确的职责和接口。这样可以提高代码可读性和可维护性,并且方便进行水平扩展。 在学习成本方面,ioGame 的学习成本非常低,可以说是零学习成本,即使没有游戏编程经验,也能轻松上手。开发者只需掌握普通的 java 方法或 webMVC 相关知识,就能用框架开发业务。框架不要求开发者改变编码习惯,而是自身适应开发者的需求。 在同进程亲和性方面,在同一进程内,不同 Netty 实例之间的通信,是通过内存进行传输的,不需要经过网络传输,数据传输速度极快。同进程亲和性指的是,优先访问同进程内的游戏逻辑服,当同进程内没有能处理请求的游戏逻辑服时,才会去其他进程或机器中查找能处理请求的游戏逻辑服;简单点说,框架对于请求的处理很智能,会优先将请求给同进程内的逻辑服消费。 在开发体验方面,ioGame 非常注重开发者的开发体验;框架提供了JSR380 验证、断言 + 异常机制、业务代码定位,action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ... 等。诸多丰富的功能,使得开发者的业务代码更加的清晰、简洁; 业务框架提供了插件机制,插件是可插拨、可扩展的。框架内置提供了DebugInOut、action 调用统计、业务线程监控插件、各时间段调用统计插件... 等插件;不同的插件提供了不同的关注点,比如我们可以使用调用、监控等插件相互配合,可以让我们在开发阶段就知道是否存在性能问题。合理利用好各个插件,可以让我们在开发阶段就能知道问题所在,提前发现问题,提前预防问题。 在分布式开发体验方面,通常在开发分布式应用时是需要启动多个进程的。这会让调试与排查问题变得非常困难,从而降低开发者的效率、增加工作量等,这也是很多框架都解决不了的问题,但 ioGame 做到了!ioGame 支持多服单进程的启动方式,这使得开发者在开发和调试分布式系统时更加简单。 与前端对接联调方面,ioGame 提供了游戏文档生成的辅助功能,可以做到代码即对接文档。简单地说,当业务代码编写完后,框架会自动生成最新的文档。如果没有游戏文档的生成,那么你将要抽出一些时间来编写、维护对接文档的工作,而且当团队人数多了之后,文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。 在部署方面,ioGame 支持多服单进程的方式部署,也支持多服多进程多机器的方式部署;在部署方式上可以随意的切换而不需要更改代码。日常中我们可以按照单体思维开发,到了生产可以选择使用多进程的方式部署。 在安全方面,所有的游戏逻辑服不需要开放端口,天然地避免了扫描攻击。由于不需要为每个逻辑服分配独立的端口,那么我们在使用诸如云服务器之类的服务时,就不需要担心端口开放权限的问题了。别小看这一个环节,通常这些小细节最浪费开发者的时间。由于我们不需要管理这些 IP:Port,这部分的工作量就自然地消失了。 在模拟客户端测试方面,ioGame 提供了压测 & 模拟客户端请求模块。此模块是用于模拟客户端,简化模拟工作量,只需要编写对应请求与回调。除了可以模拟简单的请求外,通常还可以做一些复杂的请求编排,并支持复杂业务的压测。与单元测试不同的是,该模块可以模拟真实的网络环境,并且在模拟测试的过程中与服务器的交互是可持续的、可互动的,同时也是支持自动化的。 在架构灵活性方面,ioGame 的架构由三部分组成:1. 游戏对外服、2.Broker(游戏网关)、3. 游戏逻辑服;三者既可相互独立,又可相互融合。这意味着使用 ioGame 可以适应任何类型的游戏,因为只需通过调整部署方式,就可以满足不同类型的游戏需求。在 ioGame 中进行这些调整工作非常简单,而且不会对现有代码产生不良影响。 架构是可以动态扩缩的,游戏对外服、游戏逻辑服、Broker(游戏网关)都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。同时,架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 开发者基于 ioGame 编写的项目模块,通常是条理清晰的,得益于框架对路由的合理设计,同时也为路由提供了优雅的访问权限控制。当我们整理好这些模块后,对于其他开发者接管项目或后续的维护中,会是一个不错的帮助(模块的整理与建议)。或许现阶段你感受不到这块的威力,随着你深入地使用实践就能体会到这么设计的诸多好处与优势。 开发者基于 ioGame 编写的项目,通常是语法简洁的、高性能的、低延迟的。框架最低要求使用JDK21,这样即可以让项目享受到分代 ZGC带来的改进,还能享受语法上的简洁。分代 ZGC 远低于其亚毫秒级暂停时间的目标,可以在不影响游戏速度的情况下,清理掉多余的内存;这样就不会出现卡顿或者崩溃的问题了,相当于在项目中变相的引入了一位 JVM 调优大师。 综上所述,ioGame 是一个非常适合网络游戏开发的框架。可以让你轻松地创建高性能、低延迟、易扩展的游戏服务器,并且节省时间和资源。如果你想要快速地开发出令人惊艳的网络游戏,请不要犹豫,立即选择 ioGame 吧!框架屏蔽了很多复杂且重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。 框架在开发、部署、压测 & 模拟测试 ... 等,各个阶段都提供了很好的支持。相信你已经对 ioGame 有了一个初步的了解,虽然还有很多丰富的功能与特性没有介绍到,但你可以通过后续的实践过程中来深入了解。感谢你的阅读,并期待你使用 ioGame 来打造自己的游戏服务器。 ioGame 的组成 ioGame 由 [网络通信框架] 和 [业务框架] 组成 网络通信框架:职责是各服务器之间的网络通信 业务框架:职责是业务逻辑的处理方式和编写方式 网络通信框架 SOFABolt是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名字取自迪士尼动画 - 闪电狗,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架。 业务框架 如果说sofa-bolt是为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上。而业务框架正是解决业务逻辑如何方便实现这一问题上。业务框架是游戏框架的一部分,职责是简化程序员的业务逻辑实现,业务框架使程序员能够快速的开始编写游戏业务。 业务框架对于每个 action (即业务的处理方法) 都是通过asm与 Singleton、Flyweight 、Command 等设计模式结合,对 action 的获取上通过 array 来得到,是一种近原生的方式。 单线程中,业务框架平均每秒可以执行 1152 万次业务逻辑。 架构简图 通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分布式的网络游戏服务器! 从图中可以看出,游戏网关支持以集群方式启动多个实例。这个设计选择了集群的方式,因为游戏网关通常是无状态的,主要作用是解耦各逻辑服之间的关系、负载均衡、调度和转发任务。 而游戏对外服、游戏逻辑服使用分布式设计,支持启动多个相同类型的服务。这意味着,当玩家数量增加时,我们可以轻松增加相应类型的游戏逻辑服以处理更多请求。 以游戏逻辑服为例,假设我们启动了两个 A 类型的游戏逻辑服,分别为 A-1 和 A-2。当玩家向 A 类型的游戏逻辑服发起多次请求时,游戏网关会使用默认的随机负载策略将请求分配给 A-1 和 A-2 来处理。 现在我们明白,游戏对外服和游戏逻辑服都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 此外,框架还支持玩家动态绑定游戏逻辑服;玩家与游戏逻辑服绑定后,之后的请求都由该游戏逻辑服来处理。 除了游戏之外,ioGame 也适用于物联网相关项目。只需将图中的玩家视为具体的设备,即使存在数亿个设备,ioGame 的架构也可以轻松支持。从 2022 年开始,已经有一些物联网公司开始采用这一解决方案,并得到了很好的体验。 ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; 无锁异步化与事件驱动的架构设计、集群无中心节点、自带负载均衡、分布式支持、可动态增减机器、避免类爆炸的设计; 名称 扩展方式 职责 游戏对外服 分布式 与玩家连接、交互 游戏逻辑服 分布式 处理具体业务逻辑 Broker(游戏网关) 集群 调度和转发任务; 通过 ioGame 可以使得游戏编程变得简单,下面是一个业务示例 协议文件定义 首先我们自定义一个协议文件,这个协议文件作为我们的业务载体描述。这个协议是纯 java 代码编写的,使用的是 jprotobuf,jprotobuf 是对google protobuf的简化使用,性能同等。 可以把这理解成 DTO、POJO、业务数据载体等,其主要目的是用于业务数据的传输; /** 请求 */ @ProtobufClass @FieldDefaults(level = AccessLevel.PUBLIC) public class HelloReq { String name; } Action 游戏服务器的编程,游戏服务器接收业务数据后,对业务数据进行处理;下面这段代码可以同时支持 TCP、WebSocket、UDP 通信方式。 示例代码中展示了玩家的请求与响应处理,还展示了跨服(跨进程、跨机器)的请求处理的示例。无入侵的 Java Bean。 @ActionController(1) public class DemoAction { @ActionMethod(0) public HelloReq here(HelloReq helloReq) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; return newHelloReq; } // 注意,这个方法只是为了演示而写的;(ioGame21 开始支持) // 效果与上面的方法一样,只不过是用广播(推送)的方式将数据返回给请求方 @ActionMethod(0) public void here(HelloReq helloReq, FlowContext flowContext) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; flowContext.broadcastMe(newHelloReq); } // 跨服调用示例,下面分别展示了同步与异步回调的写法 void testShowInvokeModule(FlowContext flowContext) { /* * 框架为跨服请求提供了同步、异步、异步回调的编码风格 api。(ioGame21 开始支持) */ var cmdInfo = CmdInfo.of(1,0); var yourData = ... 你的请求参数 // 跨服请求(异步回调 - 无阻塞)-- 路由、请求参数、回调。 flowContext.invokeModuleMessageAsync(cmdInfo, yourData, responseMessage -> { var helloReq = responseMessage.getData(HelloReq.class); // --- 此异步回调,具备全链路调用日志跟踪 --- log.info("异步回调 : {}", helloReq); }); // 跨服请求(同步 - 阻塞)-- 路由、请求参数。 ResponseMessage responseMessage = flowContext.invokeModuleMessage(cmdInfo, yourData); var helloReq = responseMessage.getData(HelloReq.class); log.info("同步调用 : {}", helloReq); } } 一个方法(here)在业务框架中表示一个Action(一个业务动作)。 方法声明的参数是用于接收前端传入的业务数据,在方法 return 时,数据就可以被游戏前端接收到。程序员可以不需要关心业务框架的内部细节。 从上面的示例可以看出,这和普通的 java 类并无区别,同时这种设计方式避免了类爆炸。如果只负责编写游戏业务,那么对于业务框架的学习可以到此为止了。 游戏编程就是如此简单! 问:我可以开始游戏服务器的编程了吗? 是的,你已经可以开始游戏服务器的编程了。 访问示例(控制台) 当我们访问here方法时(通常由游戏前端来请求),控制台将会打印 ┏━━━━━ Debug. [(DemoAction.java:4).hello] ━━━━━ [cmd:1-0 65536] ━━━━━ [逻辑服 [xxx逻辑服] - id:[76526c134cc88232379167be83e4ddfc]] ┣ userId: 1 ┣ 参数: active : HelloReq(id=101, name=塔姆) ┣ 响应: HelloReq(name=塔姆, I'm here ) ┣ 时间: 1 ms (业务方法总耗时) ┗━━━━━ [ioGameVersion] ━━━━━ [线程:User-8-2] ━━━━━━━ [traceId:956230991452569600] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 控制台打印说明 Debug. [(DemoAction.java:4).here]: 表示执行业务的是DemoAction类下的here方法,4表示业务方法所在的代码行数。 在工具中点击控制台的DemoAction.java:4这条信息,就可以跳转到对应的代码中(快速导航到对应的代码),这是一个开发良好体验的开始! userId: 当前发起请求的 用户 id。 参数: 通常是游戏前端传入的值。 响应:通常是业务方法返回的值 ,业务框架会把这个返回值推送到游戏前端。 时间:执行业务方法总耗时,我们可根据业务方法总耗时的时长来优化业务。 路由信息:[cmd - subCmd]路由是唯一的访问地址。 ioGameVersion:表示当前所使用的 ioGame 版本。 线程:当前执行 action 所使用的线程。 traceId:全链路调用日志跟踪 id,每个请求唯一。(该特性在分布式下非常实用) 逻辑服:当前游戏逻辑服与其 id 有了以上信息,游戏开发者可以很快的定位问题。如果没有可视化的信息,开发中会浪费很多时间在前后端的沟通上。问题包括: 是否传参问题 (游戏前端说传了) 是否响应问题(游戏后端说返回了) 业务执行时长问题 (游戏前端说没收到响应, 游戏后端说早就响应了) 其中代码导航可以让开发者快速的跳转到业务类对应代码中,在多人合作的项目中,可以快速的知道业务经过了哪些方法的执行,使得我们可以快速的进行阅读或修改; 框架内置的其他功能 内置多种可选模块,可按需选择,以方便应用开发: 领域事件(轻量级单机最快 MQ --disruptor;通过领域事件模块,可为你的系统实现类似 Guava-EventBus、Spring 事件驱动模型 ApplicationEvent、业务解耦、规避并发、不阻塞主线程... 等,各种浪操作) 任务延时器(将来某个时间可对任务进行执行、暂停、取消等操作,并不是类似 Quartz 的任务调度) 多环境切换(不同运行环境下的配置支持) light-jprotobuf(补足 jprotobuf 不能让多个对象在单个 .proto 源文件中生成的需求,并简化 jprotobuf 对源文件的注释) 分布式锁(基于 Redisson 的简单实现) 内置的其他功能: 心跳相关 用户上线、离线相关的钩子方法 UserSessions(对所有用户 UserSession 的管理,统计在线用户等) UserSession(与 channel 是 1:1 的关系,可取到对应的 userId、channel 等信息。) 登录相关(提供重复登录、顶号等相关增强功能) 业务参数基础类型 自动装箱、拆箱(解决协议碎片) 适合人群? 长期从事 web 内部系统开发人员, 想了解游戏的 刚从事游戏开发的 未从事过游戏开发,但却对其感兴趣的 对设计模式在实践中的应用和 sofa-bolt 有兴趣的学习者 可以接受新鲜事物的 想放弃祖传代码的 推荐实际编程经验一年以上的人员。 ioGame提供了丰富的在线高质量使用文档,为你的团队助力,带上你们的小伙伴一起,这样就不用手把手的教了。

优秀的个人博客,低调大师

netty 高性能游戏服务器框架;ioGame 21.6 真.轻量级网络编程框架发布

ioGame 源码完全开放、最新文档阅读完全开放;使用完全自由、免费(遵守开源协议)。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。 本次更新 版本更新汇总 #264 新增属性值变更监听特性 模拟客户端新增与服务器断开连接的方法。模拟客户端新增是否活跃的状态属性。 #265 从游戏对外服中获取玩家相关数据 - 模拟玩家请求。 任务相关:TaskListener 接口增加异常回调方法,用于接收异常信息;当 triggerUpdate 或 onUpdate 方法抛出异常时,将会传递到该回调方法中。 #266 新增 RangeBroadcast 范围内的广播功能,这个范围指的是,可指定某些用户进行广播。 AbstractRoom 增加 ifPlayerExist、ifPlayerNotExist 方法。 属性监听特性 #264 新增属性值变更监听特性 文档 : 属性监听 (yuque.com) 属性可添加监听器,当某些属性值的发生变化时,触发监听器。 使用场景举例 比如玩家的血量低于一定值时,需要触发无敌状态;此时,我们就可以监听玩家的血量,并在该属性上添加一个对应的监听器来观察血量的变化,当达到预期值时就触发对应的业务。 类似的使用场景还有很多,这里就不过多的举例了。属性监听的特点在于属性变化后会触发监听器。 属性监听特点 可为属性添加监听器,用于观察属性值的变化。 属性可以添加多个监听器。 属性的监听器可以移除。 框架已经内置了几个属性实现类,分别是: IntegerProperty LongProperty StringProperty BooleanProperty ObjectProperty for example - 添加监听器 当 BooleanProperty 对象的值发生改变时,触发监听器。 var property = new BooleanProperty(); // 添加一个监听器。 property.addListener((observable, oldValue, newValue) -> { log.info("oldValue:{}, newValue:{}", oldValue, newValue); }); ​ property.get(); // value is false property.set(true); // 值变更时,将会触发监听器 property.get(); // value is true 当 IntegerProperty 对象的值发生改变时,触发监听器。 var property = new IntegerProperty(); // add listener monitor property object property.addListener((observable, oldValue, newValue) -> { log.info("oldValue:{}, newValue:{}", oldValue, newValue); }); ​ property.get(); // value is 0 property.set(22); // When the value changes,listeners are triggered property.get(); // value is 22 ​ property.increment(); // value is 23. will trigger listeners for example - 移除监听器 下面这个示例,我们将 property 初始值设置为 10,随后添加了一个监听器;当监听器观察到新值为 9 时,就从 observable 中移除自己(这个自己指的是监听器本身),而 observable 则是 IntegerProperty。 @Test public void remove1() { IntegerProperty property = new IntegerProperty(10); // 添加一个监听器 property.addListener(new PropertyChangeListener<>() { @Override public void changed(PropertyValueObservable extends Number> observable, Number oldValue, Number newValue) { log.info("1 - newValue : {}", newValue); ​ if (newValue.intValue() == 9) { // 移除当前监听器 observable.removeListener(this); } } }); ​ property.decrement(); // value 是 9,并触发监听器 property.decrement(); // value 是 8,由于监听器已经移除,所以不会触发任何事件。 } 下面的示例中,我们定义了一个监听器类 OnePropertyChangeListener 并实现了 PropertyChangeListener 监听器接口。示例中,我们通过 OnePropertyChangeListener 对象的引用来移除监听器。 @Test public void remove2() { // 监听器 OnePropertyChangeListener onePropertyChangeListener = new OnePropertyChangeListener(); // 属性 IntegerProperty property = new IntegerProperty(); // 添加监听器 property.addListener(onePropertyChangeListener); ​ property.increment(); // value == 1,并触发监听器 property.removeListener(onePropertyChangeListener); // 移除监听器 property.increment(); // value == 2,由于监听器已经移除,所以不会触发任何事件。 } ​ // 自定义的监听器 class OnePropertyChangeListener implements PropertyChangeListener<Number> { @Override public void changed(PropertyValueObservable extends Number> observable, Number oldValue, Number newValue) { log.info("oldValue:{}, newValue:{}, observable:{}", oldValue, newValue, observable); } } 属性监听 - 小结 属性监听在使用上是简单的,如果你的业务中有关于属性变化后需要触发某些事件的,可以考虑引用该特性。框架为 int、long、boolean、Object、String 等基础类型提供了对应的属性监听。 属性监听特性支持添加多个监听器,支持移除监听器。 模拟客户端相关 模拟客户端新增与服务器断开连接的方法。 模拟客户端新增是否活跃的状态属性。 ClientUser clientUser = ...; // 是否活跃,true 表示玩家活跃 clientUser.isActive(); // 关闭模拟客户端连接 clientUser.getClientUserChannel().closeChannel(); 获取游戏对外服的数据与扩展相关 文档 获取游戏对外服的数据与扩展 (yuque.com) RequestCollectExternalMessage 增加 userId 字段。 #265 模拟玩家请求时 - 从游戏对外服中获取在线玩家相关数据 新增 UserHeadMetadataExternalBizRegion,从用户(玩家)所在游戏对外服中获取用户自身的数据,如用户所绑定的游戏逻辑服、元信息 ...等 使用参考 @Slf4j @RestController @RequestMapping("other") public class OtherController { static final AtomicLong msgId = GameManagerController.msgId; /** 为了方便测试,这里指定一个 userId 来模拟玩家 */ static final long userId = GameManagerController.userId; ​ @GetMapping("/notice") public String notice() { log.info("other notice"); // 使用协议碎片特性 https://www.yuque.com/iohao/game/ieimzn StringValue data = StringValue.of("other GM web msg " + msgId.incrementAndGet()); // 模拟请求 : 路由 - 业务数据 RequestMessage requestMessage = BarMessageKit.createRequestMessage(ExchangeCmd.of(ExchangeCmd.notice), data); ​ // 设置需要模拟的玩家 HeadMetadata headMetadata = requestMessage.getHeadMetadata(); headMetadata.setUserId(userId); ​ // 从游戏对外服中获取一些用户(玩家的)自身的数据,如元信息、所绑定的游戏逻辑服 ...等 Optional<HeadMetadata> headMetadataOptional = ExternalCommunicationKit.employHeadMetadata(requestMessage); ​ if (headMetadataOptional.isPresent()) { // 发起模拟请求 extractedRequestLogic(requestMessage); ​ // 打印从游戏对外服获取的元信息 byte[] attachmentData = headMetadata.getAttachmentData(); ExchangeAttachment attachment = DataCodecKit.decode(attachmentData, ExchangeAttachment.class); return "other notice 玩家的元信息: %s - %s".formatted(attachment, msgId.get()); } else { return "other notice 玩家 %s 不在线,无法获取玩家的元信息 - %s".formatted(userId, msgId.get()); } } ​ private void extractedRequestLogic(RequestMessage requestMessage) { // 向逻辑服发送请求,该模拟请求具备了玩家的元信息。 BrokerClient brokerClient = MyKit.brokerClient; InvokeModuleContext invokeModuleContext = brokerClient.getInvokeModuleContext(); invokeModuleContext.invokeModuleVoidMessage(requestMessage); } } 任务工具相关 TaskListener 接口增加异常回调方法 void onException(Throwable e),用于接收异常信息;当 triggerUpdate 或 onUpdate 方法抛出异常时,将会传递到该回调方法中。 @Test public void testException() throws InterruptedException { AtomicBoolean hasEx = new AtomicBoolean(false); TaskKit.runOnce(new OnceTaskListener() { @Override public void onUpdate() { // 模拟一个业务异常 throw new RuntimeException("hello exception"); } ​ @Override public void onException(Throwable e) { hasEx.set(true); // 触发异常后,将来到这里 log.error(e.getMessage(), e); } }, 10, TimeUnit.MILLISECONDS); ​ TimeUnit.MILLISECONDS.sleep(200); Assert.assertTrue(hasEx.get()); // true } 业务框架相关 - [common-core] #266 新增 RangeBroadcast 范围内的广播功能,这个范围指的是,可指定某些用户进行广播。 在执行广播前,开发者可以自定义业务逻辑,如 添加一些需要广播的用户 删除一些不需要接收广播的用户 可通过重写 logic、trick 方法来做一些额外扩展 for example 在某些业务场景下,能让语义更清晰 // example - 1 new RangeBroadcast(flowContext) // 需要广播的数据 .setResponseMessage(responseMessage) // 添加需要接收广播的用户 .addUserId(1) .addUserId(2) .addUserId(List.of(3L, 4L, 5L)) // 排除一些用户,被排除的用户将不会接收到广播 .removeUserId(1) // 执行广播 .execute(); ​ // example - 2 new RangeBroadcast(flowContext) // 需要广播的数据 .setResponseMessage(cmdInfo, playerReady) // 添加需要接收广播的用户 .addUserId(1) // 执行广播 .execute(); [light-game-room] 房间模块 移除 AbstractRoom broadcast 系列方法,开发者可使用 RoomBroadcastFlowContext 接口实现旧的兼容。 移除 AbstractRoom createSend 方法,开发者可使用 ofRangeBroadcast 系列来代替。AbstractRoom 新增 RoomBroadcastEnhance,实现房间内的广播增强,该系列在语义上更清晰。 这里使用一个准备就绪的示例来演示,当有玩家准备时,需要通知房间内的其他玩家 final RoomService roomService = ...; ​ @ActionMethod(RoomCmd.ready) public void ready(boolean ready, FlowContext flowContext) { long userId = flowContext.getUserId(); // 得到玩家所在的房间 AbstractRoom room = this.roomService.getRoomByUserId(userId); // 准备 PlayerReady playerReady = new PlayerReady(); playerReady.userId = userId; playerReady.ready = ready; // 通知房间内的所有玩家,有玩家准备或取消准备了 room.ofRangeBroadcast(flowContext) // 响应数据(路由、业务数据) .setResponseMessage(flowContext.getCmdInfo(), playerReady) .execute(); } ​ // 准备或取消准备 @ProtobufClass @FieldDefaults(level = AccessLevel.PUBLIC) public class PlayerReady { /** 当前操作的玩家 userId */ long userId; /** true 表示准备 */ boolean ready; } AbstractRoom 增加 ifPlayerExist、ifPlayerNotExist 方法。 ifPlayerExist 方法 如果玩家在房间内,就执行给定的操作,否则不执行任何操作。 这里演示玩家加入房间的业务 RoomService roomService = ...; AbstractRoom room = ...; // 如果玩家不在房间内,就创建一个玩家,并让玩家加入房间 room.ifPlayerNotExist(userId, () -> { // 玩家加入房间 FightPlayerEntity newPlayer = new FightPlayerEntity(); newPlayer.setId(userId); this.roomService.addPlayer(room, newPlayer); }); ifPlayerNotExist 方法 如果玩家不在房间内,就执行给定的操作,否则不执行任何操作。 这里结合 RangeBroadcast,演示当有新玩家加入房间时,通知房间内的其他玩家。 AbstractRoom room = ...; // 有新玩家加入房间,通知其他玩家 room.ifPlayerExist(userId, (FightPlayerEntity playerEntity) -> { FightPlayer fightPlayer = FightMapstruct.ME.convert(playerEntity); room.ofRangeBroadcast(flowContext) .setResponseMessage(RoomCmd.of(RoomCmd.playerEnterRoomBroadcast), fightPlayer) // 排除不需要通知的玩家(当前 userId 是自己) .removeUserId(userId) .execute(); }); ioGame 使用趋势数据 关注 ioGame 的游戏服务器开发者持续增多,2022-09 ~ 至今各月的统计数据; 这里的统计信息是关于开发者关注 ioGame 框架相关的,从统计数据中可以看出,由于 ioGame 上手简单,功能强大等优点,得到了众多开发者的关注。如果你想知道 ioGame 有没有人在使用,可以先到这里看下统计数据、开发者的评价与讨论。 https://www.yuque.com/iohao/game/gpxk93#TwVa8 这里展示了每月的统计数据,统计数据来源于语雀后台,这些数据都是真实的、客观存在的、活的。 因为成本的原因,某宝某多还没有出现能提供这种服务的商家,所以这样的统计数据也更具真实性。 通过统计数据,我们可以看到每天会有很多开发者在访问 ioGame 的在线文档,并且这些统计数据不是来源于口嗨的,也不是主观创造的。 所以,还在犹豫要不要使用 ioGame 的开发者们,更应该讨论的是 “为什么这些开发者会选择使用 ioGame”,而不是 ioGame 有没有人在使用的问题。 点击我,到语雀后台查看 ioGame 的数据 ioGame 网络游戏服务器框架简介 无锁异步化、事件驱动的架构设计;轻量级,无需依赖任何第三方中间件或数据库就能支持集群、分布式 通过 ioGame 可以很容易的搭建出一个集群无中心节点、集群自动化、多进程的分布式游戏服务器 包体小、启动快、内存占用少、更加的节约、无需配置文件、提供了优雅的路由访问权限控制 可同时支持多种连接方式:WS、UDP、TCP... 等;框架已支持全链路调用日志跟踪特性 让开发者用一套业务代码,能轻松切换和扩展不同的通信协议:Protobuf、JSON 近原生的性能;业务框架在单线程中平均每秒可以执行 1152 万次业务逻辑 代码即联调文档、JSR380 验证、断言 + 异常机制 = 更少的维护成本 框架具备智能的同进程亲和性;开发中,业务代码可定位与跳转 架构部署灵活性与多样性:既可相互独立,又可相互融合 可同时与同类型的多个游戏逻辑服通信并得到数据 逻辑服之间可相互跨进程、跨机器进行通信 支持玩家对游戏逻辑服进行动态绑定 能与任何其他框架做融合共存 对 webMVC 开发者友好 无 spring 强依赖 零学习成本 javaSE 你是否想要开发一个高性能、稳定、易用、自带负载均衡、避免类爆炸设计、可跨进程跨机器通信、集群无中心节点、集群自动化、有状态多进程的分布式的网络编程服务器呢?如果是的话,这里向你推荐一个由 java 语言编写的网络编程框架 ioGame。下面将会从多个方面来对框架做一些简单的介绍。 ioGame 是一个轻量级的网络编程框架,适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; ioGame 有以下特点: 无锁异步化、事件驱动的架构设计 同时支持 TCP、WebSocket、UDP 多种连接方式,并且可扩展 支持 protobuf、json 等不同的通信协议 集群无中心节点、集群自动化、分布式的设计 真轻量级,不依赖任何第三方中间件或数据库就能支持集群、分布式 提供多种通讯方式,且逻辑服之间可以相互跨机器通信 框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问 ioGame 是纯 javaSE 的,使得 ioGame 能与其他框架方便的进行集成、融合;如 spring ... 等 学习成本低,开发体验好 支持多服单进程、多服多进程的启动和部署方式 提供游戏文档生成的辅助功能 包体小、启动快、内存占用少 提供优雅的路由访问权限控制 提供了灵活的线程扩展、设置 具备智能的同进程亲和性 具备全链路调用日志跟踪特性 业务框架提供了插件机制,插件是可插拨、可扩展的 JSR380 验证、断言 + 异常机制 = 更少的维护成本 action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ioGame 是一个专为网络编程设计的轻量级框架,它可以帮助你快速地搭建和运行自己的网络服务器。ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景。如各种类型和规模的网络游戏,无论是 H5、手游还是 PC 游戏,无论是简单的聊天室,还是复杂的全球同服、回合制游戏、策略游戏、放置休闲游戏、即时战斗、MMORPG 等,ioGame 都可以满足你的需求。 ioGame 在打包、内存占用、启动速度等方面也是优秀的。打 jar 包后大约15MB,应用通常会在0.x 秒内完成启动,内存占用小。详细请看快速从零编写服务器完整示例。 在生态融合方面,ioGame 可以很方便的与spring 集成(5 行代码);除了 spring 外,还能与任何其他的框架做融合,如:solon... 等,从而使用其他框架的相关生态。 ioGame 在架构上解决了传统框架所产生的N*N 问题(与传统架构对比)。传统架构在扩展机器时,需要借助很多第三方中间件,如:Redis、MQ、ZooKeeper ... 等,才能满足整体架构的运作。通常,只要引入了需要安装的中间件才能做到扩展的,那么你的架构或者说框架,基本上与轻量级无缘了。 在轻量级方面,ioGame不依赖任何第三方中间件或数据库就能支持集群、分布式,只需要 java 环境就可以运行。这意味着在使用上简单了,在部署上也为企业减少了部署成本、维护难度。使用 ioGame 时,只需一个依赖即可获得整个框架,而无需安装其他服务,如: Nginx、Redis、MQ、Mysql、ZooKeeper、Protobuf 协议编译工具 ... 等。 ioGame 具备全链路调用日志跟踪特性,这在分布式下非常的实用。该特性为每个请求分配一个唯一标识,并记录在日志中,通过唯一标识可以快速的在日志中过滤出指定请求的信息。ioGame 提供的全链路调用日志跟踪特性更是强大,支持跨机器、跨进程。简单的说,从玩家的请求进来到结束,无论该请求经过了多少个游戏逻辑服,都能精准记录。 在通讯方式方面,大部分框架只能支持推送(广播)这一类型的通讯方式;ioGame 则提供了多种类型的通讯方式,通过对各种通讯方式的组合使用,可以简单完成以往难以完成的工作,并且这些通讯方式都支持跨进程、跨机器通信,且具备全链路调用日志跟踪。这些通讯方式分别是 请求响应(单次请求处理) 广播(推送) 单个逻辑服间的相互通讯(可跨机器通信、可跨进程通信) 与同类型多个逻辑服相互通讯(可跨多个机器通信、可跨多个进程通信) 脉冲通讯(可跨多个机器通信、可跨多个进程通信) 分布式事件总线(类似 MQ、Redis 发布订阅机制;可跨多个机器通信、可跨多个进程通信) 在编码风格上,ioGame 为开发者提供了类 MVC 的编码风格(无入侵的 Java Bean ),这种设计方式很好的避免了类爆炸。同时,框架为开发者提供了同步、异步、异步回调的方法,用于逻辑服之间的相互访问;这使得开发者所编写的代码会非常的优雅,并且具备全链路调用日志跟踪。 从 ioGame21 开始,框架添加了虚拟线程的相关支持。各逻辑服之间通信阻塞部分使用虚拟线程,这样可以很好的避免阻塞业务线程,并大幅提高了框架的吞吐量。 在线程安全方面,框架为开发者解决了单个玩家的并发问题;即使玩家重新登录后,也会使用相同的线程来消费业务,并推荐使用领域事件来解决同一房间或业务内多个玩家的并发问题。框架在线程的扩展性上提供了友好的支持,开发者可以很容易的编写出无锁并发代码,这得益于 ioGame 独有的线程执行器设计与扩展。换句话说,你不会因为并发问题烦恼。 在无锁并发方面,ioGame 提供了优雅、独特的线程执行器设计。通过该特性,开发者能轻易的编写出无锁高并发的代码。 在连接方式方面,ioGame 允许开发者使用一套业务代码,同时支持多种连接方式,无需进行任何修改。ioGame 已经支持了 TCP、WebSocket 和 UDP 连接方式,并且也支持在这几种连接方式之间进行灵活切换。连接方式是可扩展的,并且扩展操作也很简单,这意味着之后如果支持了 KCP,无论你当前项目使用的是 TCP、WebSocket 还是 UDP,都可以切换成 KCP;注意了,即使切换到 KCP 的连接方式,现有的业务代码也无需改变。 在通信协议方面,ioGame 让开发者用一套业务代码,就能轻松切换和扩展不同的通信协议,如 Protobuf、JSON 等。只需一行代码,就可以从 Protobuf 切换到 JSON,无需改变业务方法。 在增减协议方面,ioGame 可以让你在新增或减少协议时,无需重启游戏对外服与 Broker(游戏网关);这样既能避免玩家断线,又能避免因新增、减少协议而重启所有机器的痛点。 在协议碎片方面,action 支持自动装箱、拆箱基础类型特性,用于解决协议碎片的问题。同时该特性除了能使你的业务代码更加清晰以外,还能大幅提高开发者在该环节的生产力。 在集群方面,ioGame 的 Broker (游戏网关)采用无中心节点、自动化的集群设计,所有节点平等且自治,不存在单点故障。集群能够自动管理和弹性扩缩,节点加入或退出时,能够自动保证负载均衡和数据一致性,不影响服务可用性。 在分布式方面,ioGame 的逻辑服使用了分布式设计思想,将服务器分为游戏对外服、游戏逻辑服等不同层次,并且每一层都有明确的职责和接口。这样可以提高代码可读性和可维护性,并且方便进行水平扩展。 在学习成本方面,ioGame 的学习成本非常低,可以说是零学习成本,即使没有游戏编程经验,也能轻松上手。开发者只需掌握普通的 java 方法或 webMVC 相关知识,就能用框架开发业务。框架不要求开发者改变编码习惯,而是自身适应开发者的需求。 在同进程亲和性方面,在同一进程内,不同 Netty 实例之间的通信,是通过内存进行传输的,不需要经过网络传输,数据传输速度极快。同进程亲和性指的是,优先访问同进程内的游戏逻辑服,当同进程内没有能处理请求的游戏逻辑服时,才会去其他进程或机器中查找能处理请求的游戏逻辑服;简单点说,框架对于请求的处理很智能,会优先将请求给同进程内的逻辑服消费。 在开发体验方面,ioGame 非常注重开发者的开发体验;框架提供了JSR380 验证、断言 + 异常机制、业务代码定位,action 支持自动装箱、拆箱基础类型,用于解决协议碎片的问题 ... 等。诸多丰富的功能,使得开发者的业务代码更加的清晰、简洁; 业务框架提供了插件机制,插件是可插拨、可扩展的。框架内置提供了DebugInOut、action 调用统计、业务线程监控插件、各时间段调用统计插件... 等插件;不同的插件提供了不同的关注点,比如我们可以使用调用、监控等插件相互配合,可以让我们在开发阶段就知道是否存在性能问题。合理利用好各个插件,可以让我们在开发阶段就能知道问题所在,提前发现问题,提前预防问题。 在分布式开发体验方面,通常在开发分布式应用时是需要启动多个进程的。这会让调试与排查问题变得非常困难,从而降低开发者的效率、增加工作量等,这也是很多框架都解决不了的问题,但 ioGame 做到了!ioGame 支持多服单进程的启动方式,这使得开发者在开发和调试分布式系统时更加简单。 与前端对接联调方面,ioGame 提供了游戏文档生成的辅助功能,可以做到代码即对接文档。简单地说,当业务代码编写完后,框架会自动生成最新的文档。如果没有游戏文档的生成,那么你将要抽出一些时间来编写、维护对接文档的工作,而且当团队人数多了之后,文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。 在部署方面,ioGame 支持多服单进程的方式部署,也支持多服多进程多机器的方式部署;在部署方式上可以随意的切换而不需要更改代码。日常中我们可以按照单体思维开发,到了生产可以选择使用多进程的方式部署。 在安全方面,所有的游戏逻辑服不需要开放端口,天然地避免了扫描攻击。由于不需要为每个逻辑服分配独立的端口,那么我们在使用诸如云服务器之类的服务时,就不需要担心端口开放权限的问题了。别小看这一个环节,通常这些小细节最浪费开发者的时间。由于我们不需要管理这些 IP:Port,这部分的工作量就自然地消失了。 在模拟客户端测试方面,ioGame 提供了压测 & 模拟客户端请求模块。此模块是用于模拟客户端,简化模拟工作量,只需要编写对应请求与回调。除了可以模拟简单的请求外,通常还可以做一些复杂的请求编排,并支持复杂业务的压测。与单元测试不同的是,该模块可以模拟真实的网络环境,并且在模拟测试的过程中与服务器的交互是可持续的、可互动的,同时也是支持自动化的。 在架构灵活性方面,ioGame 的架构由三部分组成:1. 游戏对外服、2.Broker(游戏网关)、3. 游戏逻辑服;三者既可相互独立,又可相互融合。这意味着使用 ioGame 可以适应任何类型的游戏,因为只需通过调整部署方式,就可以满足不同类型的游戏需求。在 ioGame 中进行这些调整工作非常简单,而且不会对现有代码产生不良影响。 架构是可以动态扩缩的,游戏对外服、游戏逻辑服、Broker(游戏网关)都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。同时,架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 开发者基于 ioGame 编写的项目模块,通常是条理清晰的,得益于框架对路由的合理设计,同时也为路由提供了优雅的访问权限控制。当我们整理好这些模块后,对于其他开发者接管项目或后续的维护中,会是一个不错的帮助(模块的整理与建议)。或许现阶段你感受不到这块的威力,随着你深入地使用实践就能体会到这么设计的诸多好处与优势。 开发者基于 ioGame 编写的项目,通常是语法简洁的、高性能的、低延迟的。框架最低要求使用JDK21,这样即可以让项目享受到分代 ZGC带来的改进,还能享受语法上的简洁。分代 ZGC 远低于其亚毫秒级暂停时间的目标,可以在不影响游戏速度的情况下,清理掉多余的内存;这样就不会出现卡顿或者崩溃的问题了,相当于在项目中变相的引入了一位 JVM 调优大师。 综上所述,ioGame 是一个非常适合网络游戏开发的框架。可以让你轻松地创建高性能、低延迟、易扩展的游戏服务器,并且节省时间和资源。如果你想要快速地开发出令人惊艳的网络游戏,请不要犹豫,立即选择 ioGame 吧!框架屏蔽了很多复杂且重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。 框架在开发、部署、压测 & 模拟测试 ... 等,各个阶段都提供了很好的支持。相信你已经对 ioGame 有了一个初步的了解,虽然还有很多丰富的功能与特性没有介绍到,但你可以通过后续的实践过程中来深入了解。感谢你的阅读,并期待你使用 ioGame 来打造自己的游戏服务器。 ioGame 的组成 ioGame 由 [网络通信框架] 和 [业务框架] 组成 网络通信框架:职责是各服务器之间的网络通信 业务框架:职责是业务逻辑的处理方式和编写方式 网络通信框架 SOFABolt是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名字取自迪士尼动画 - 闪电狗,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架。 业务框架 如果说sofa-bolt是为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上。而业务框架正是解决业务逻辑如何方便实现这一问题上。业务框架是游戏框架的一部分,职责是简化程序员的业务逻辑实现,业务框架使程序员能够快速的开始编写游戏业务。 业务框架对于每个 action (即业务的处理方法) 都是通过asm与 Singleton、Flyweight 、Command 等设计模式结合,对 action 的获取上通过 array 来得到,是一种近原生的方式。 单线程中,业务框架平均每秒可以执行 1152 万次业务逻辑。 架构简图 通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分布式的网络游戏服务器! 从图中可以看出,游戏网关支持以集群方式启动多个实例。这个设计选择了集群的方式,因为游戏网关通常是无状态的,主要作用是解耦各逻辑服之间的关系、负载均衡、调度和转发任务。 而游戏对外服、游戏逻辑服使用分布式设计,支持启动多个相同类型的服务。这意味着,当玩家数量增加时,我们可以轻松增加相应类型的游戏逻辑服以处理更多请求。 以游戏逻辑服为例,假设我们启动了两个 A 类型的游戏逻辑服,分别为 A-1 和 A-2。当玩家向 A 类型的游戏逻辑服发起多次请求时,游戏网关会使用默认的随机负载策略将请求分配给 A-1 和 A-2 来处理。 现在我们明白,游戏对外服和游戏逻辑服都支持动态增加和减少。无论未来玩家数量增加或减少,我们都能够轻松应对。架构是支持玩家无感知更新的,这得益于分布式设计。举例来说,如果 A 类型的游戏逻辑服需要增加一些新功能,我们可以启动 A-3、A-4 等已经支持了新功能的服务器,然后逐步将之前的 A-1 和 A-2 下线,从而实现了无感知的更新。 此外,框架还支持玩家动态绑定游戏逻辑服;玩家与游戏逻辑服绑定后,之后的请求都由该游戏逻辑服来处理。 除了游戏之外,ioGame 也适用于物联网相关项目。只需将图中的玩家视为具体的设备,即使存在数亿个设备,ioGame 的架构也可以轻松支持。从 2022 年开始,已经有一些物联网公司开始采用这一解决方案,并得到了很好的体验。 ioGame 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; 无锁异步化与事件驱动的架构设计、集群无中心节点、自带负载均衡、分布式支持、可动态增减机器、避免类爆炸的设计; 名称 扩展方式 职责 游戏对外服 分布式 与玩家连接、交互 游戏逻辑服 分布式 处理具体业务逻辑 Broker(游戏网关) 集群 调度和转发任务; 通过 ioGame 可以使得游戏编程变得简单,下面是一个业务示例 协议文件定义 首先我们自定义一个协议文件,这个协议文件作为我们的业务载体描述。这个协议是纯 java 代码编写的,使用的是 jprotobuf,jprotobuf 是对google protobuf的简化使用,性能同等。 可以把这理解成 DTO、POJO、业务数据载体等,其主要目的是用于业务数据的传输; /** 请求 */ @ProtobufClass @FieldDefaults(level = AccessLevel.PUBLIC) public class HelloReq { String name; } Action 游戏服务器的编程,游戏服务器接收业务数据后,对业务数据进行处理;下面这段代码可以同时支持 TCP、WebSocket、UDP 通信方式。 示例代码中展示了玩家的请求与响应处理,还展示了跨服(跨进程、跨机器)的请求处理的示例。无入侵的 Java Bean。 @ActionController(1) public class DemoAction { @ActionMethod(0) public HelloReq here(HelloReq helloReq) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; return newHelloReq; } // 注意,这个方法只是为了演示而写的;(ioGame21 开始支持) // 效果与上面的方法一样,只不过是用广播(推送)的方式将数据返回给请求方 @ActionMethod(0) public void here(HelloReq helloReq, FlowContext flowContext) { // 业务数据 var newHelloReq = new HelloReq(); newHelloReq.name = helloReq.name + ", I'm here "; flowContext.broadcastMe(newHelloReq); } // 跨服调用示例,下面分别展示了同步与异步回调的写法 void testShowInvokeModule(FlowContext flowContext) { /* * 框架为跨服请求提供了同步、异步、异步回调的编码风格 api。(ioGame21 开始支持) */ var cmdInfo = CmdInfo.of(1,0); var yourData = ... 你的请求参数 // 跨服请求(异步回调 - 无阻塞)-- 路由、请求参数、回调。 flowContext.invokeModuleMessageAsync(cmdInfo, yourData, responseMessage -> { var helloReq = responseMessage.getData(HelloReq.class); // --- 此异步回调,具备全链路调用日志跟踪 --- log.info("异步回调 : {}", helloReq); }); // 跨服请求(同步 - 阻塞)-- 路由、请求参数。 ResponseMessage responseMessage = flowContext.invokeModuleMessage(cmdInfo, yourData); var helloReq = responseMessage.getData(HelloReq.class); log.info("同步调用 : {}", helloReq); } } 一个方法(here)在业务框架中表示一个Action(一个业务动作)。 方法声明的参数是用于接收前端传入的业务数据,在方法 return 时,数据就可以被游戏前端接收到。程序员可以不需要关心业务框架的内部细节。 从上面的示例可以看出,这和普通的 java 类并无区别,同时这种设计方式避免了类爆炸。如果只负责编写游戏业务,那么对于业务框架的学习可以到此为止了。 游戏编程就是如此简单! 问:我可以开始游戏服务器的编程了吗? 是的,你已经可以开始游戏服务器的编程了。 访问示例(控制台) 当我们访问here方法时(通常由游戏前端来请求),控制台将会打印 ┏━━━━━ Debug. [(DemoAction.java:4).hello] ━━━━━ [cmd:1-0 65536] ━━━━━ [逻辑服 [xxx逻辑服] - id:[76526c134cc88232379167be83e4ddfc]] ┣ userId: 1 ┣ 参数: active : HelloReq(id=101, name=塔姆) ┣ 响应: HelloReq(name=塔姆, I'm here ) ┣ 时间: 1 ms (业务方法总耗时) ┗━━━━━ [ioGameVersion] ━━━━━ [线程:User-8-2] ━━━━━━━ [traceId:956230991452569600] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 控制台打印说明 Debug. [(DemoAction.java:4).here]: 表示执行业务的是DemoAction类下的here方法,4表示业务方法所在的代码行数。 在工具中点击控制台的DemoAction.java:4这条信息,就可以跳转到对应的代码中(快速导航到对应的代码),这是一个开发良好体验的开始! userId: 当前发起请求的 用户 id。 参数: 通常是游戏前端传入的值。 响应:通常是业务方法返回的值 ,业务框架会把这个返回值推送到游戏前端。 时间:执行业务方法总耗时,我们可根据业务方法总耗时的时长来优化业务。 路由信息:[cmd - subCmd]路由是唯一的访问地址。 ioGameVersion:表示当前所使用的 ioGame 版本。 线程:当前执行 action 所使用的线程。 traceId:全链路调用日志跟踪 id,每个请求唯一。(该特性在分布式下非常实用) 逻辑服:当前游戏逻辑服与其 id 有了以上信息,游戏开发者可以很快的定位问题。如果没有可视化的信息,开发中会浪费很多时间在前后端的沟通上。问题包括: 是否传参问题 (游戏前端说传了) 是否响应问题(游戏后端说返回了) 业务执行时长问题 (游戏前端说没收到响应, 游戏后端说早就响应了) 其中代码导航可以让开发者快速的跳转到业务类对应代码中,在多人合作的项目中,可以快速的知道业务经过了哪些方法的执行,使得我们可以快速的进行阅读或修改; 框架内置的其他功能 内置多种可选模块,可按需选择,以方便应用开发: 领域事件(轻量级单机最快 MQ --disruptor;通过领域事件模块,可为你的系统实现类似 Guava-EventBus、Spring 事件驱动模型 ApplicationEvent、业务解耦、规避并发、不阻塞主线程... 等,各种浪操作) 任务延时器(将来某个时间可对任务进行执行、暂停、取消等操作,并不是类似 Quartz 的任务调度) 多环境切换(不同运行环境下的配置支持) light-jprotobuf(补足 jprotobuf 不能让多个对象在单个 .proto 源文件中生成的需求,并简化 jprotobuf 对源文件的注释) 分布式锁(基于 Redisson 的简单实现) 内置的其他功能: 心跳相关 用户上线、离线相关的钩子方法 UserSessions(对所有用户 UserSession 的管理,统计在线用户等) UserSession(与 channel 是 1:1 的关系,可取到对应的 userId、channel 等信息。) 登录相关(提供重复登录、顶号等相关增强功能) 业务参数基础类型 自动装箱、拆箱(解决协议碎片) 适合人群? 长期从事 web 内部系统开发人员, 想了解游戏的 刚从事游戏开发的 未从事过游戏开发,但却对其感兴趣的 对设计模式在实践中的应用和 sofa-bolt 有兴趣的学习者 可以接受新鲜事物的 想放弃祖传代码的 推荐实际编程经验一年以上的人员。 ioGame提供了丰富的在线高质量使用文档,为你的团队助力,带上你们的小伙伴一起,这样就不用手把手的教了。

优秀的个人博客,低调大师

工信部给出了答案,网友:真现实

不知道,大家在5G来临的时候,有没有感受到我们自己的4G网络速度有了明显的变化?对此有很多的网友都说是因为三大运营商用了手段。而三大运营商却明确的表示根本没有这回事,并且还说就算5G的网络来临,4G的网络也不会下降。 而对此,工信部作出了相关的回应,据工信部的多个测试之后,表示,4G网络的速度在整体上是没有下降的,只是某些区域,还有某一些时段,比如说用的人比较多的时候,这个网速就下降了。总之就是这不是三大运营商的锅,他们并不背。工信部的权威自然是让人相信的,可是问题是,以往还没有5G的时候,我们的4G网络是用的好好的,偏偏5G网络一出来,4G网络就开始降速了,要说真的没有运营商什么事,还真的是很难信服我们这些用户。毕竟事实摆在眼前。 所以就编编来说,还是认为运营商在很大程度上做出了手脚,因为他们就是要推5G网络。而推5G网络的目的,说白了就是圈钱。现在5G网络的套餐资费太高,很多的人根本没有办法享受,所以在我们的4G网络能够使用的情况下,很多人是不愿意去更换5G的。而运营商花了大价钱建立的基站,投入了那么多的成本,我们要是都不去用,他们怎么可能赚得到钱呢?本身5G的建设就是一个大成本的投入,迟迟不见收益他们也是很着急。所以说,现在对于4G网络的限速是最好逼迫用户们升级的办法。 不过,我们也可以往好的方面想,那就是他们确实是没有动任何的手脚,因为主要的精力都放在了5G上面,肯定对于4G就没有那么在意了,就人员上来说也抽调不过来。要知道未来本身就是一个5G的大趋势,如果在这个时候还过多的投入到4G的维护中不是白白的花钱吗?所以说为了不增加压力,他们也只有关注一个重点,那就是5G,自然4G也就任由发展了。不过让人尤其好奇的是,联通当时是花了巨资在5G来临的时候,投入到了4G的基站上面,可是事实上联通的网络速度下降是最离谱的,那么问题来了,联通投入这么大,4G信号还不好?也就是说4G网络都没做好?那么我们凭什么相信5G能够给我们带来更好的体验呢? 其实不管如何,编编觉得运营商更应该的是搞好4G,毕竟4G是一个承上启下的作用,何况现在本身就有很多的地方不能使用5G,你让这些不能使用5G的小伙伴守着一个降速的4G怎么玩儿?

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册