RegameDesk 开发笔记【2】Avro

数据交换采用 Avro。但在开始介绍之前,先进入大家最喜欢的答疑环节。

答疑

  1. 稣这是创业了吗?

没有。创业需要能赚钱的产品,还需要有投资(即,不是赚市场的钱,就是赚投资人的钱),稣两者都没有。再说创业肯定都是偷偷摸摸的。单纯就是回报社会主义社会。

  1. 不赚钱,你是 SX 吗?

众所周知,稣使用自然码双拼,普通的拼音简写早就看不懂了。SX 是啥?

经验

几乎在每家公司都用过 Google Protocol Buffers,除了它还没被开发之前。在网稣时,不仅大家推荐稣(在 Linux)使用 ProtoBuf,稣也推荐大家(在 Windows)用 ProtoBuf。但是在做远程桌面时,却和搭档很默契地不用它!最神奇的是,我俩一个写客户端,一个写服务端,期间也没对过具体协议,联调时数据结构居然 100% 一样,一跑就通。

当然这在工程上,并不是什么好事!因为这说明用的就是最简单的封装,相当于没用数据交换库,纯手工打造。要说为啥,那肯定就是偷懒,当时在 Windows 上使用 PB 还比较麻烦,为了“快速实现”就先牺牲扩展性。

到金斗云时,有的工程不用 ProtoBuf,有的用。原本设计上是:网络通信用 ProtoBuf,IPC 则用 FlatBuffers。但由于采用驱动抓图的方式,避开了 IPC 的使用(具体原因以后分析),所以其实 FlatBuffers 没用上。想用 FlatBuffers 是因为它效率高,但又因为它并不安全,所以不能用于网络上,只能用于 IPC。

在雪蛤油时,采用 ProtoBuf 是原有团队的积累,自己也熟悉,没理由不继续用。但发现两个版本的 Debug 版都会被 Boost.Test 报内存泄漏,这时候就凸显库臃肿的坏处了……ProtoBuf 这么大坨,根本没人愿意去分析,如果升级版本,又会出现两份(因为别组开发的 DLL 库里也用,稣只能升自己这边的,那边的他们不升),最后,看着没啥表面问题就直接弃疗。

分析

目前,RegameDesk 采用 Apache Avro 1.12.0。实际上,但凡有点名气的数据交换(或者叫数据序列化)格式和库都被研究得很多了,大可不必自己去选型,看看论文即可。比如这篇:[2201.03051] A Benchmark of JSON-compatible Binary Serialization Specifications

在大部分的对比项中,Avro 和 Microsoft Bond 常常是前两名。

ASN.1 也挺极致的,但它不好用。

Bond 的代码里有 Haskell,学习成本高于只有纯 C++ 的 Avro。而且它已经快停止维护了,详见 Bond project ending March 2025 #1215

简单看过 Avro 的代码,里面有 Zigzag 算法,用于压缩整形,这可太数学、太极致了,是基于统计学的——0 附近的数字被用得多,所以它们用更少的字节来表示。能考虑到这种点,光是想象中,基本上性能就不会落后(当然前面的论文也有实际验证)。

另外,Avro 本身很小巧,第三方依赖主要是 Boost.Iostreams,但 Boost 本来就是稣惯用的库,可以说维护上毫无压力。相对的,ProtoBuff 还带着不熟悉的 abseil,二进制整体加起来大 Avro 好几倍。以下对比基于 vcpkg 默认配置的编译结果:

文件 大小
ProtoBuff Lite abseil_dll.dll, libprotobuf-lite.dll 3.46MB
ProtoBuff abseil_dll.dll, libprotobuf.dll 14.9MB
Avro avrocpp.dll, boost_iostreams-vc143-mt-x64-1_86.dll, zlib1.dll, bz2.dll, liblzma.dll, zstd.dll 1.84MB

(实际上 4 个压缩库是能优化掉其中不用的几个,即还能更小)

使用案例

在设计上,RegameDesk 支持 4 种操作模式:

模式 具体功能
Assist 协助模式:适用于服务器前有人在操作的场景
1. 屏幕拓扑、分辨率等设置均以服务器为主,不改服务器的任何配置
2. 不会创建虚拟显示器
3. 允许多个此模式的客户端;兼容其它模式
Control 控制模式:适用于服务器为物理机,尤其是办公机
1. 屏幕拓扑、分辨率等设置均以客户端为主,将客户端的配置同步到服务器
2. 必定会创建虚拟显示器
3. 会断开服务器上所有物理显示器,防止服务器侧隐私泄露
4. 会使服务器静音,防止服务器侧隐私泄露,而且会阻止用户取消静音
5. 退出时,还原服务端设置
6. 只允许一个此模式客户端;与 headless 模式互斥
Headless 无头模式:适用于服务器不插显示器的场景(物理机或虚拟机皆可)
1. 假定服务器只有虚拟显示器,如果有物理屏幕,则断开,防止服务器侧隐私泄露
2. 屏幕拓扑、分辨率等设置均以客户端为主,将客户端的配置同步到服务器
3. 会使服务器静音,防止服务器侧隐私泄露,但不会阻止用户取消静音
4. 退出时,不还原服务端任何设置
5. 只允许一个此模式客户端;与 control 模式互斥
View 仅查看模式:不能操控
1. 屏幕拓扑、分辨率等设置均以服务器为主,不改服务器的任何配置
2. 不会创建虚拟显示器
3. 允许多个此模式的客户端;兼容其它模式

用 Avro 来表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"type": "record",
"name": "OperationMode",
"fields": [
{
"name": "mode",
"type": {
"type": "enum",
"name": "Mode",
"symbols": [ "kAssist", "kControl", "kHeadless", "kView" ]
}
}
]
}

人类都说:JSON 的可读性最好。是这样没错!

如果您使用微信,也可以关注公众号 UMU618,在公众号文章里评论。