老东家所使用的联飞服务软件是从 2019 年 8 月份计划重构的,主要使用的是 Golang 做的开发。在这之前,我一直部署的是开源的 FSD 3.000 draft 9
版本,相信做模拟飞行开发的同行应该不陌生。通俗来说,代码实现了 FSD 的关键的三个端口上的服务,Client、System、Server,分别对应着连飞、管理、服务器间互联。
14 年拿到这份源码,也是打开了我写 C++ 程序的大门。而随着业务发展,对于功能扩展的需求也正逐步的提高。使用 C++ 处理字符串是一件非常痛苦的事情,而 FSD 在设计上,使用的是 PlainText 进行传输。从源码以及外网资料中可以知道,FSD 应该是上世纪末的产物,所以也不难理解,为什么 ProtoBuf、gRPC 广泛运用的今天,联飞服务器依旧在使用着十分传统的方式进行通信。这份通信协议几乎成了国内模拟飞行的一种标准,而为了兼容性,确切来说是一种妥协,推翻这套协议几乎是不可能的事情。
在设计和重构联飞服务的过程中,走了非常多的弯路。我一开始认为它可以套用不少流行的游戏服务器框架。但事实效果并不尽人意。
这里顺带说说游戏服务框架的事儿,目前市面上大部分的游戏服务器框架都是基于:Gate,Login,Room 的业务模型,即网关节点,登陆节点,最后是房间节点。其分布式主要体现在网关节点结合服务发现或服务注册提供其他剩余节点的服务地址。对客户端的套接字是否连续无显著要求(有可能登陆节点和房间节点并不为同一个套接字),或者换一种表达方法就是,网关和登陆更多设计成无状态接口,只有真正的房间节点才采用长链接(当然也要看业务情况,比如 RGP 网游的副本可能可以做成离线的,但对战节点一定一定是长链接通讯的。)。而模拟飞行服务需要的是一个套接字的生命周期即为一个用户整个联飞或管制用户的生命周期,此外,在现有的情况下,联飞服务器也无需做到分房间(或许未来有这样的需求,例如动态开启训练服务器)。
得益于 Golang 开发的便捷性,实现一个处理 PlainText 的服务并不困难,这个项目也不过 3000 行左右。管理功能也可以通过 HTTP 重新封装。而唯独互联功能的优化比较困难。现存的开源内容,只能帮助我们构建一个环状网络或星形网络,当节点出现问题,整个网络就可能会被割裂成两个部分;从源码来看,FSD 应当是设计有路由部分的,但实际资料中几乎没有相关技术描述。
节点较少的情况下,两两相连就是最省资源的方式,但节点多起来之后,路由是必不可少的。现代常用的办法是由一个路由中心分发处理节点间的数据,实现互联。路由中心附带一部分在线用户管理功能,整体实现应该不是十分复杂,但架构问题依旧值得探讨。
联飞服务总体代码结构如下(2021 年 7 月 16 日注:本结构已过期,可公开):
- │ build.bat //编译操作脚本
- │ go.mod //GolangMod
- │ go.sum
- │ Rum.go //初始化入口
- │
- ├─AuxM //目前所有的辅助操作均在此实现,基本上一个文件实现一个对内或对外暴露的接口。
- │ AuxM.go
- │ httpApi.go //通过直接webAPI的方式对外提供服务器运行信息
- │ metarGetter.go //Metar读取器,用于服务器内获取METAR信令的功能
- │ wzpFile.go //生成一个WZPfile,类同FSD,提供给httpApi
- │ wzpJson.go //WZPFile的JSON版本,提供给httpApi
- │
- ├─Cert
- │ cert.go //账密认证部分,账密表生成。
- │
- ├─config
- │ config.go //配置读取部分
- │
- ├─core //核心部分
- │ │ client.go //在线用户相关操作的主要实现部分
- │ │ clientGroup.go //用户池,已废弃
- │ │ clientPool.go //用户池,在实际使用中一个用户会大用户池和分用户池,以便多播
- │ │ flightplan.go //按照C版本重写,负责单个飞行计划的管理
- │ │ group.go //用户池的Interface
- │ │ killConst.go //按照C版本重写,枚举下线原因
- │ │ killGuarder.go //用于预防Flood上下线行为,此处设计时存在一些问题,不利于后期调试,日后将修改成中间件模式
- │ │
- │ └─fsd //基本上是C版本的重写,不详细阐释
- │ auxManager.go
- │ clientMiddleware.go
- │ clinterface.go
- │ errorDefine.go
- │ fsdPraser.go
- │ metarManager.go
- │ ProtocolAdapter.go
- │ servinterface.go
- │ tmCmdAdapter.go //此处是实现了一些类似于PDC的功能
- │
- ├─cron
- │ cron.go //定时任务检查
- │
- ├─define
- │ define.go //全局定义
- │
- ├─global
- │ global.go
- │ version.go
- │ version_template.go
- │
- ├─log
- │ log.go
- │
- ├─server
- │ server.go
- │
- ├─system
- │ system.go
- │
- ├─util //重用
- │ map.go
- │ NaviMethod.go
- │ NaviMethod_test.go
- │ support.go
- │ timer.go
- │ unsafeStrByte.go
- │
- └─Voice //语音实现部分,暂不公开
本文标题:开发联飞服务时的一些思考 ①
本文连接:https://blog.dextercai.com/archives/51.html
除另行说明,本站文字内容采用创作共用版权 CC-BY-NC-ND 4.0 许可协议,版权归本人所有。
除另行说明,本站图片内容版权归本人所有,未经许可前,严禁以任何形式的使用。
即日起视情况关闭全站评论区,您可以通过关于页面的电邮地址和我取得联系,谢谢
那么用户数据怎么存储是用原来的文件存储还是用数据库存储
这俩没有什么区别吧
数据库的话可以和虚航,运控系统对接做统一登录,还可以修改密码,文件存储的话就比较麻烦,vatsim 就很麻烦,账户都不能修改密码,每次登录都要看邮件
联飞服务作为认证侧,只需要考虑抽象类就行了。现在我的实现是走微服务,最后再 Fallback 回 cert。就联飞服务自己本身而言,fsNotify + 定时刷新 Cert 这种模式也完全够用了。