主要更新
[160] 轻量小部件 - 压测&模拟客户端请求模块
文档:https://www.yuque.com/iohao/game/tc83ud
介绍
此模块是用于模拟客户端,简化模拟工作量,只需要编写对应请求与回调。
使用该模块后,当我们与前端同学联调某个功能时,不需要跟前端哥们说:在点一下、在点一下、在点一下了。这种“在点一下”的交流联调方式将成为过去式。
除了可以模拟简单的请求外,通常还可以做一些复杂的请求编排,并支持复杂业务的压测。模拟测试的过程是可互动的,但也支持测试自动化。
与单元测试不同的是,该模块可以模拟真实的网络环境,并且在模拟测试的过程中与服务器交互是可持续的、可互动的。
可互动模式是用于调试测试某些功能。在互动的过程中,开发者可以在控制台中指定执行某个模拟请求命令,并且支持在控制台中输入一些动态的请求参数,从而让我们轻松的测试不同的业务逻辑走向。
关于可互动部分,需要将后续文档阅读完后才能知道其具体意思。
特点
- 使用简单
- 压测支持
- 可以模拟客户端请求
- 可以模拟真实的网络环境
- 可以编排复杂的业务请求
- 同样的模拟测试用例,支持在多种连接方式下工作(tcp、udp、websocket)
- 可持续的与服务器交互,模拟测试的过程是可互动的,但也支持测试自动化
入门级演示
文档有点多,但在实际的使用上是比较简单的。
图一
图左边是我们提供的 action,图右边则是我们编写的模拟请求。
图二
控制台中是可交互的部分,可以查看提供了哪些模拟的客户端请求。
通过在控制台中输入 【cmd-subCmd】 来触发对应的请求。
图三
触发请求后,当服务器有响应数据时,会进入到对应模拟请求的回调中。
小结
比如需要做一个简单的:私聊系统、聊天频道、好友系统时,这个模拟客户端就派上用场了。
模拟客户端模块还提供了一个 ClientUser(玩家)对象,一个模拟客户端对应一个 ClientUser。ClientUser 是客户端的用户(玩家)对象,开发者可以通过动态属性 options 来扩展业务,比如可以在动态属性中保存货币、战力值、血条 …等。也可以通过继承的方式来扩展。
其他更新
文档生成增强,增加 action 参数注释说明、返回值注释说明。
ioGame 简介
你是否想要开发一个高性能、稳定、易用、自带负载均衡、避免类爆炸设计、可跨进程跨机器通信、集群无中心节点、集群自动化、有状态多进程的分步式的网络游戏服务器呢?如果是的话,这里向你推荐一个由 java 语言编写的网络游戏服务器框架 ioGame。下面将会从多个方面来对框架做一些简单的介绍。
ioGame 是一个 java 网络游戏服务器框架,有以下特点:
- 无锁异步化、事件驱动的架构设计
- 同时支持 TCP、WebSocket、UDP 多种连接方式,并且可扩展
- 支持 protobuf、json 等不同的通信协议
- 集群无中心节点、集群自动化、分布式的设计
- 真轻量级,不依赖任何第三方中间件或数据库就能支持集群、分布式
- 提供多种通讯方式,且逻辑服之间可以相互跨机器通信
- 与 spring 和其他框架融合方便
- 学习成本低,开发体验好
- 支持多服单进程、多服多进程的启动和部署方式
- 提供游戏文档生成的辅助功能
- 包体小、启动快、内存占用少
- 提供优雅的路由访问权限控制
- 提供了灵活的线程扩展、设置
ioGame 是一个专为网络游戏服务器设计的轻量级框架,它可以帮助你快速地搭建和运行自己的游戏服务器。它适用于各种类型和规模的网络游戏,无论是 H5、手游还是 PC 游戏,无论是简单的聊天室,还是复杂的全球同服、回合制游戏、策略游戏、放置休闲游戏、即时战斗、MMORPG 等,ioGame 都可以满足你的需求。
ioGame 在打包、内存占用、启动速度等方面也是优秀的。打 jar 包后大约 15MB,应用通常会在 0.x 秒内完成启动,内存占用小。详细请看 快速从零编写服务器完整示例。
在生态融合方面,ioGame 可以很方便的与 spring 集成(5 行代码);除了 spring 外,还能与任何其他的框架做融合,如:solon … 等,从而使用其他框架的相关生态。
在轻量级方面,ioGame 不依赖任何第三方中间件或数据库就能支持集群、分布式,只需要 java 环境就可以运行。这意味着在使用上简单了,在部署上也为企业减少了部署成本、维护难度。使用 ioGame 时,只需一个依赖即可获得整个框架,而无需在安装其他服务,如: Nginx、Redis、MQ、Mysql、ZooKeeper、Protobuf协议编译工具 … …等。
在通讯方式方面,大部分框架只能支持推送(广播)这一类型的通讯方式;而 ioGame 则提供了 5 种类型的通讯方式,分别是单次请求处理、推送、单个逻辑服间的相互通讯、与同类型多个逻辑服相互通讯、脉冲通讯。通过对各种通讯方式的组合使用,可以简单完成以往难以完成的工作,并且这些通讯方式都支持跨进程、跨机器通信。
在连接方式方面,ioGame 允许开发者使用一套业务代码,同时支持多种连接方式,无需进行任何修改。ioGame 已经支持了 TCP、WebSocket 和 UDP 连接方式,并且也支持在这几种连接方式之间进行灵活切换。连接方式是可扩展的,并且扩展操作也很简单,这意味着之后如果支持了 KCP,无论你当前项目使用的是 TCP、WebSocket 还是 UDP,都可以切换成 KCP;注意了,即使切换到 KCP 的连接方式,现有的业务代码也无需改变。
在通信协议方面,ioGame 让开发者用一套业务代码,就能轻松切换和扩展不同的通信协议,如 Protobuf、JSON 等。只需一行代码,就可以从 Protobuf 切换到 JSON,无需改变业务方法。
在集群方面,ioGame 的 Broker (游戏网关)采用无中心节点、自动化的集群设计,所有节点平等且自治,不存在单点故障。集群能够自动管理和弹性扩缩,节点加入或退出时,能够自动保证负载均衡和数据一致性,不影响服务可用性。
在分布式方面,ioGame 的逻辑服使用了分布式设计思想,将服务器分为游戏对外服、游戏逻辑服等不同层次,并且每一层都有明确的职责和接口。这样可以提高代码可读性和可维护性,并且方便进行水平扩展。
在学习成本方面,ioGame 的学习成本非常低,可以说是零学习成本,即使没有游戏编程经验,也能轻松上手。开发者只需掌握普通的 java 方法或 webMVC 相关知识,就能用框架开发业务。框架不要求开发者改变编码习惯,而是自身适应开发者的需求。
在同进程亲和性方面,在同一进程内,不同 Netty 实例之间的通信,是通过内存进行传输的,不需要经过网络传输,数据传输速度极快。同进程亲和性指的是,优先访问同进程内的游戏逻辑服,当同进程内没有能处理请求的游戏逻辑服时,才会去其他进程或机器中查找能处理请求的游戏逻辑服;简单点说,框架对于请求的处理很智能,会优先将请求给同进程内的逻辑服消费。
在开发体验方面,ioGame 非常注重开发者的开发体验;框架提供了 JSR380验证、断言 + 异常机制、业务代码定位… …等诸多丰富的功能,使得开发者的业务代码更加的清晰、简洁;
在业务的并发方面,框架为开发者解决了单个玩家的并发问题,也提供了解决同一房间或业务内多个玩家并发问题的解决方法;框架在线程的扩展性上提供了友好的支持,并不是只能提供呆板的线程数量设置;详细请看 ioGame 线程相关。
在分布式开发体验方面,通常在开发分布式应用时是需要启动多个进程的。这会让调试与排查问题变得非常困难,从而降低开发者的效率、增加工作量等,这也是很多框架都解决不了的问题,但 ioGame 做到了!ioGame 支持多服单进程的启动方式,这使得开发者在开发和调试分步式系统时更加简单。
与前端对接联调方面,ioGame 提供了游戏文档生成的辅助功能,可以做到代码即对接文档。简单地说,当业务代码编写完后,框架会自动生成最新的文档。如果没有游戏文档的生成,那么你将要抽出一些时间来编写、维护对接文档的工作,而且当团队人数多了之后,文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。
在部署方面,ioGame 支持多服单进程的方式部署,也支持多服多进程多机器的方式部署;在部署方式上可以随意的切换而不需要更改代码。日常中我们可以按照单体思维开发,到了生产可以选择使用多进程的方式部署。
在模拟客户端测试方面,ioGame 提供了压测&模拟客户端请求模块。此模块是用于模拟客户端,简化模拟工作量,只需要编写对应请求与回调。除了可以模拟简单的请求外,通常还可以做一些复杂的请求编排,并支持复杂业务的压测。模拟测试的过程是可互动的,但也支持测试自动化。与单元测试不同的是,该模块可以模拟真实的网络环境,并且在模拟测试的过程中与服务器交互是可持续的、可互动的。
在架构灵活性方面,ioGame 的架构由三部分组成:1.游戏对外服、2.Broker(游戏网关)、3.游戏逻辑服;三者既可相互独立,又可相互融合。所以,使用 ioGame 几乎可以满足任意的部署方式,可以根据你的需求来适应不同类型的游戏,并且在 ioGame 中做这些工作是简单的。
开发者基于 ioGame 编写的项目模块,通常是条理清晰的,得益于框架对路由的合理设计,同时也为路由提供了优雅的访问权限控制。当我们整理好这些模块后,对于其他开发者接管项目或后续的维护中,会是一个不错的帮助(模块的整理与建议)。或许现阶段你感受不到这块的威力,随着你深入地使用实践就能体会到这么设计的诸多好处与优势。
开发者基于 ioGame 编写的项目,通常是语法简洁的、高性能的、低延迟的;框架最低要求使用 JDK17,这样即可以让项目享受到 ZGC 带来的改进,还能享受语法上的简洁。从 JDK17 开始 ZGC 远低于其亚毫秒级暂停时间的目标,可以在不影响游戏速度的情况下,清理掉多余的内存。这样就不会出现卡顿或者崩溃的问题了,相当于在项目中变相的引入了一位 JVM 调优大师,详细请看 JDK 17 垃圾回收 GC 性能飞跃提升。
综上所述,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 万次业务逻辑。
业务框架性能报告JMH--点我展开上面是在单线程中的测试数据,业务框架平均每秒执行 1152 万次。
架构简图
通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分步式的网络游戏服务器!
无锁异步化与事件驱动的架构设计、集群无中心节点、自带负载均衡、分布式支持、可动态增减机器、避免类爆炸的设计;
图中的每个游戏对外服、每个游戏逻辑服、每个 broker (游戏网关)都可以在单独的进程中部署,逻辑服之间可以跨进程通信(游戏对外服也是逻辑服的一种)。
游戏网关集群
broker (游戏网关)支持集群的方式部署,集群的使用是简单的,集群无中心节点、集群自动化、自带负载均衡。ioGame 本身就包含服务注册,你不需要外接一个服务注册中心,如 Eureka,ZooKeeper 等(变相的节约服务器成本)。
通过 broker (游戏网关) 的介入,之前非常复杂的负载均衡设计,如服务注册、健康度检查(后续版本提供)、到服务端的连接维护等这些问题,在 ioGame 中都不需要了,结构也简单了很多。实际上单台 broker (游戏网关) 性能已经能够满足了,因为游戏网关只做了转发。
逻辑服
逻辑服通常说的是游戏对外服和游戏逻辑服。逻辑服可以有很多个,逻辑服扩展数量的理论上限是 netty 的连接上限。
游戏对外服
对外服保持与用户(玩家)的长连接。先来个假设,假如我们的一台硬件支持我们建立用户连接的上限是 5000 人,当用户量达到 7000 人时,我们可以多加一个对外服务器来进行分流减压。由于游戏对外服扩展的简单性,意味着支持同时在线玩家可以轻松的达到百万、千万甚至更多。
即使我们启动了多个游戏对外服,开发者也不需要关心这些玩家连接到了哪个游戏对外服的问题,这些玩家总是能接收到广播(推送)消息的,因为框架已经把这些事情给做了;在玩家的角度我们只有“一个”服务器,同样的,在开发者的角度我们只有“一个”游戏对外服;
在结构组合上(部署多样性)
在部署上,支持多服单进程的方式部署(类似单体应用、在分步式开发时,调试更加方便)、也支持多服多进程多机器的方式部署。
架构由三部分组成:1.游戏对外服、2.Broker(游戏网关)、3.游戏逻辑服;三者既可相互独立,又可相互融合,如:
- 游戏对外服、Broker(游戏网关)、游戏逻辑服这三部分,在一个进程中;【单体应用;在开发分步式时,调试更加方便】
- 游戏对外服、Broker(游戏网关)、游戏逻辑服这三部分,在多个进程中;【分布式】
- 游戏对外服、Broker(游戏网关)这两部分在一个进程中;而游戏逻辑服在多个进程中;【类似之前游戏的传统架构】
- 甚至可以不需要游戏对外服,只使用Broker(游戏网关)和游戏逻辑服这两部分,用于其他系统业务;
因为 ioGame 遵循面向对象的设计原则(单一职责原则、开闭原则、里式替换原则、依赖倒置原则、接口隔离原则、迪米特法则)等,所以使得架构的职责分明,可以灵活的进行组合;
游戏对外服是架构的三部分之一,默认的游戏对外服是基于 netty 实现的。如果有需要,将来我们还可以使用基于 mina、smart-socket 等通信框架编写,额外提供一个游戏对外服的实现;即使是使用 mina、smart-socket 提供的游戏对外服,也并不会影响现有的游戏逻辑服业务逻辑,因为游戏对外服满足单一职责原则,只维护用户(玩家)长连接相关的。
开发人员几乎都遇见过这么一种情况;在项目初期阶段,通常是以单体项目的方式进行开发,随着需求不断的增加与迭代,会演变成一个臃肿的项目;此时在对一个整体进行拆分是困难的,成本是极高的。甚至是不可完成的,最后导致完全的重新重构;
ioGame 提供了在结构组合上的部署多样性,通过组合的方式,在项目初期就可以避免这些拆分问题。在开发阶段中,我们可以使用单体应用开发思维,降低了开发成本。通过单体应用的开发方式,在开发分步式项目时,调试更加的方便;这既能兼顾分步式开发、项目模块的拆分,又能降低团队的开发成本;
架构其他疑问
问题一:如果一个请求经过 ioGame 再返回到客户端,对比直接用 Netty 搭个简单服务器,延迟会增加很多吗?纳秒级别还是毫秒级别?
答:是内存级。将三者部署在一起后,三者是通过内存进行传输的,不需要经过网络传输,数据传输速度极快。
原因分析,为了更好理解,现在把三者用字母代替,A.游戏对外服、B.Broker(游戏网关)、C.游戏逻辑服
- ABC :三者在一个进程中,他们之间使用内存通信;
- AB + C :【游戏对外服和游戏网关】在一个进程中,他们之间使用内存通信;
- A + BC :【游戏网关和游戏逻辑服】在一个进程中,他们之间使用内存通信;
此外,ioGame 还支持同进程亲和性特性;如果我们启动了同类型的多个游戏逻辑服。【游戏网关】会优先选择与同进程的那个游戏逻辑服进行通信,使用内存通信;在同进程中没有找到时,会在同类型的多个游戏逻辑服中随机选出一个通信。
简单的一句话概括就是:同进程亲和性是指,优先访问同进程内的游戏逻辑服,当同进程内没有能处理请求的游戏逻辑服时,才会去其他进程或机器中查找能处理请求的游戏逻辑服;
所以,想要什么样的通信效果,取决于你所选择的启动方式(部署方式)。
问题二:如何更好的理解 ioGame 架构由三部分组成:1.游戏对外服、2.Broker(游戏网关)、3.游戏逻辑服;三者既可相互独立,又可相互融合?
这就好比编码时常用的三层 controller、service、dao
- 将这三部分写在一个类也是可以的,【controller、service、dao】
- 或者 【controller、service】+ dao
- 又或者 controller +【service、dao】
不要将三者看成一台台生硬的机器,而是三份独立的代码。三者是可以随意组合的,这样可以满足开发者当前项目的不同需求。
架构优点
架构有很高程度的抽象,让设计者更加关注于业务,而无需考虑底层的实现、通信参数等问题。
逻辑服的位置透明性;同时,由于模块化、抽象化,使得整个架构各服务器之间耦合度很低,逻辑服注册即可用,大大增加了可伸缩性、可维护性,动态扩展变得简单而高效。由于逻辑服是注册到 Broker(游戏网关) 上的,所以逻辑服可以动态的增加、删除、改变;由于逻辑服之间耦合度较小,调试和测试的工作也是可控的;
架构比较清晰的就是,游戏对外服负责维护客户端的接入(用户、玩家的连接),游戏逻辑服专心负责业务逻辑,他们之间的调度由 Broker(游戏网关)来负责;因为架构拆分的合理,所以特别方便用 k8s 来自由伸缩部署这三种服,哪个服水位高就扩容哪个,水位过去了又可以缩容。