最近零碎的事太多了,拖了好久没写blog。一些小的碎片话的东西也不值得写,另一方面是这次大幅优化了 atframework 的一些流程细节,特别是针对我们这两年来业务的需求,对 libatbus 进行了一次大重构。这里记录一下重构的内容吧。

重构工程配置脚本

为了拥抱Modern CMake,我对 atframework 中所有的项目(当然也包含 libatbus )进行了重构,主要是一方面重构整理最近几个项目编译和交叉编译 openssl , protobuf 等几个构建流程比较恶心的库的自动化构建流程和工具脚本。如果不追求全部功能的话,还可以用 libressl 来代替 openssl 。也合并适应了给客户端用的交叉编译iOS、Android、Unreal Engine等平台使用的构建流程。

另外atframework 中所有的C++项目都用cmake的export功能导出了,这样无论是作为某个工程的子项目,还是prebuilt之后直接可以使用Modern CMake推荐的find_package(MODULE)机制导入。

符号隐藏和DLL导出

之前写的时候没太注意跨平台符号这块,所以都是建议使用静态库。我们前段时间预研了一段时间的Unreal Engine引擎,涉及一个问题就是把服务器通信组件集成到Unreal Engine中,然后使用Unreal Engine的Dedicated Server和我们自己的服务器通信。这时候就碰到了一些问题。首先Unreal Engine官方提供的预编译包是不开RTTI和Exception的。而如果使用静态编译版本的 libatbus 得开启RTTI和Exception,这就导致得完全重编Unreal Engine,这非常慢切很麻烦。所以这次重构也梳理了一遍符号导出问题。

现在Linux和Windows下得符号导出和符号隐藏规则统一了,并且这样以后,对PE这种独立堆的ABI,也可以编译成dll使用了。这样也就可以编译成外部模块给Unreal Engine使用。

移除msgpack

之前使用msgpack是因为它宣称效率是protobuf的4倍。但是自从protobuf 3.0以后,可以使用Arena来减少碎片,我预估这个对性能的影响还挺明显的。反倒是我之前对msgpack的时候也是暴力new/delete的。所以综合来说我觉得protobuf 3.0+更好一些。另外protobuf生成的接口和反射支持比msgpack方便太多了。

第二个原因也是和上面一条一样,想要支持关闭RTTI和异常。而msgpack代码里写死的异常处理,而protobuf只要开一个编译宏就能完全关闭异常。

新的替代品是 protobuf ,我用protobuf完全替换掉了原来msgpack提供的功能。在切到protobuf之前,我还考虑过使用flatbuffer,写了个简单的对比,有兴趣的童鞋可以看看 《protobuf、flatbuffer、msgpack 针对小数据包的简单对比》cap’n protoflatbuffer 的原理一样我就没特意去再写一个测试了。简单的说就是我觉得flatbuffer对小包太不友好了,对向后兼容也不太友好,感觉可能还是只适合配置和数据转储。

支持使用字符串路径的共享内存通道

现在支持字符串路径的共享内存(使用: shm_open/ftruncate/mmap/munmap/shm_unlink/close/fstat 来管理),支持字符串命名的共享内存(长度限定为NAME_MAX(255))。但是posix标准里这个路径是不能有子目录的,所以新版本版本的glibc里也不允许(一些老版本的glibc允许)。另外linux下英文功能内存会放 tmpfs 的目录 /dev/shm 里,可以建立子目录。但是为了统一跨平台兼容性,我还是没有支持多级目录的。另外这个长度也受glibc库的限制,所以我设置了按NAME_MAX的长度。

增加协议跨版本向前向后兼容管理,增加简单的验证功能

这主要是考虑到后续如果同时多个业务使用或者多个环境使用的时候可能更新有先后。然后如果又不兼容的版本,集群之间可以互相不主动连接,以防不断地尝试重连然后断开。另外增加共享内存通道的版本号检查和架构校验,也是防止共享内存通道如果版本不兼容,随意attach上去可能会破坏数据。atproxy已经接入了这个特性。

额外增加了一个简单的验证功能,这是为了防止多个项目使用的时候误操作,发送了指令到别的业务(如果互通的话)。并不提供严谨的安全机制。

支持多个自定义子网

这是为了atproxy可以部署再自己的前缀分组里,然后管理其他前缀分组的子网。这是为了方便更灵活的运维层面的部署结构。这个改动也影响了协议层变化。不过反正都换protobuf了也是大换血了。

其他细节优化

还有一些其他细节优化列举如下:

  1. 增加连接层错误计数,超出容忍值直接断开连接
  2. 增加大量错误流程的单元测试,优化Unix Sock的单元测试,以支持WSL2
  3. 规范版本号规则:第一位变化表示有向前不兼容的变更;第二位表示有功能增加,向前兼容,第三位表示BUG修复和优化
  4. 更换数据回包的回调名字。

最早的想法是仅失败的时候回包通知,但是后来为了适应有些需求需要尽快知道是否发送成功,所以加了强制回包功能。所以现在的数据转发回包不仅仅是错误处理了,叫on_fail也不合适。所以改成了on_receive_response

不打算支持 protobuf 2.X 了, protobuf 2.X 的最后一个版本到现在已经快5年了,而且proto3的支持现在也已经很完善了。所以我并没有测试protobuf 2.6的兼容性。

未来计划

  1. endpoint 离线后保留一段时间,如果期间重连成功了自动重发
  2. 增加data sequence,事件去重和切换节点的sequence重置
  3. 共享endpoint的写缓冲区(也许后面可以一句这个支持多路并行发包)
  4. 重发消息的超时机制,增加不确定是否成功的错误码
  5. 兄弟节点离线后自动重连几次
  6. 增加endpoint尝试轮询连接数据通道,减少不必要的数据通道,仅保留最快的方式。