01
READY 划定状态边界。
新的 READY 会等旧任务结束后再更新服务器状态。后续事件只在更新完成后启动,避免旧任务覆盖新状态。
走进 Fjord 幕后
一个用 Rust 编写的机器人,一套自研图形引擎,以及 Fjord 所依赖的框架、库和数据库。
在这里,了解我们的自主开发成果,以及构成 Fjord 的各项技术。
Fjord
Fjord 按顺序处理 Discord 事件,并并行执行独立任务。Twilight 将机器人连接到 Discord,并在本地提供服务器数据。Dragonfly 存储待处理事件,并协助协调网关连接。
01
新的 READY 会等旧任务结束后再更新服务器状态。后续事件只在更新完成后启动,避免旧任务覆盖新状态。
02
同一服务器的事件按到达顺序启动。前一个任务结束后才开始下一个,避免该服务器的状态变更相互超越。
03
调度器按键区分任务。不同服务器可以同时处理事件;只有同一键的任务需要相互等待。
04
同一条消息会同时送达 AutoMod 和活动日志。两者分别完成自己的任务。
05
待处理的 Discord 事件及其数据存储在 Dragonfly 中。AutoMod 和活动日志也使用这个共享存储保存临时状态。任务执行仍由机器人控制。
06
某个已启用的 AutoMod 过滤器命中后,该消息的检查立即结束。其他过滤器不会对同一事件重复触发管理措施。
07
Twilight 在内存中维护服务器、成员、身份组、频道、表情符号、在线状态和语音状态的最新数据。消息不属于此缓存。
08
交互的调度独立于服务器事件。READY 在每条网关连接内部划定状态边界。
09
Dragonfly 协调各个分片(即网关连接)的归属。接管后,原持有者不能再领取或完成后续事件。
01 / 自主开发
Fjell 是我们用 Rust 自主开发的图形引擎。它将背景、头像与文字组合成个性化欢迎卡片。
我们希望掌控这些图像的生成过程。因此,我们为 Fjord 开发了专用方案,让设计更加自由。
成本效率同样重要。Fjell 在不牺牲画质的前提下实现最高性能,并尽可能高效地利用生成过程中的 CPU 和内存资源,从而大幅降低服务器成本。
我们的实现深入到像素处理。自行编写的 SIMD 循环使用 AVX2、FMA 和 BMI1/BMI2,并行计算多个像素。
性能测量
四种负载级别、受控的卡片变化和相同的测试设置。
| 并行数 | 卡片/秒 | 卡片/分钟 | 渲染平均 | 渲染 P50 | 渲染 P95 | 渲染 P99 | 总计平均 | 总计 P50 | 总计 P95 | 总计 P99 | CPU 平均 | RAM 平均 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 797,48 | 47.849 | 1,062 ms | 1,061 ms | 1,226 ms | 1,242 ms | 1,253 ms | 1,251 ms | 1,428 ms | 1,473 ms | 88,3 % | 381,2 MiB |
| 2 | 1.577,95 | 94.677 | 1,038 ms | 1,004 ms | 1,225 ms | 1,421 ms | 1,266 ms | 1,236 ms | 1,470 ms | 1,715 ms | 173,5 % | 360,1 MiB |
| 4 | 3.173,00 | 190.380 | 1,023 ms | 1,011 ms | 1,188 ms | 1,230 ms | 1,259 ms | 1,243 ms | 1,448 ms | 1,557 ms | 344,9 % | 375,3 MiB |
| 8 | 5.850,25 | 351.015 | 1,050 ms | 1,026 ms | 1,194 ms | 1,290 ms | 1,366 ms | 1,349 ms | 1,578 ms | 1,803 ms | 647,0 % | 385,8 MiB |
实测吞吐量与理想线性扩展的对比。
| 工作进程 | 实测 | 理想线性 | 扩展倍数 | 效率 |
|---|---|---|---|---|
| 1 | 797,48 | 797,48 | 1,00× | 100,0 % |
| 2 | 1.577,95 | 1.594,96 | 1,98× | 98,9 % |
| 4 | 3.173,00 | 3.189,92 | 3,98× | 99,5 % |
| 8 | 5.850,25 | 6.379,84 | 7,34× | 91,7 % |
四个本地测试均在 AMD Ryzen 9 9950X 上运行 60 秒,使用了 100 个采用普通颜色或插图的轻量背景,文件大小约为 100 至 300 KB,通常约 200 KB。液态图层保持关闭,字体保持不变;各卡片之间仅改变文字和圆形的颜色及其着色器位置。
渲染时间包括图像生成以及 PNG 编码和压缩,直至得到最终 PNG。网络传输和下载时间不包括在内。
负载生成器根据测试使用 1、2、4 或 8 个并行请求。只有在完整接收并验证前一个 PNG 后才开始新请求。所列 CPU 使用率正是在此负载下产生的。图形引擎可以将工作分配到多个核心。
* 所述条件下的内部测量,未经独立验证,不构成承诺的特性、保证或性能承诺。实际性能可能因系统、负载和环境而异。
高强度随机测试
更具变化性的测试设置:1,000 个背景、237 种字体,并为每次请求随机应用效果。
| 并行数 | 卡片/秒 | 卡片/分钟 | 渲染平均 | 渲染 P50 | 渲染 P95 | 渲染 P99 | 总计平均 | 总计 P50 | 总计 P95 | 总计 P99 | CPU 平均 | RAM 平均 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 92,65 | 5.559 | 9,775 ms | 10,478 ms | 12,781 ms | 13,401 ms | 10,788 ms | 11,553 ms | 14,006 ms | 14,784 ms | 93,6 % | 1.205,6 MiB |
| 2 | 184,53 | 11.072 | 9,944 ms | 10,621 ms | 12,858 ms | 13,550 ms | 10,832 ms | 11,576 ms | 13,938 ms | 14,698 ms | 189,1 % | 1.278,8 MiB |
| 4 | 358,82 | 21.529 | 10,209 ms | 10,855 ms | 13,240 ms | 13,997 ms | 11,138 ms | 11,857 ms | 14,363 ms | 15,212 ms | 377,7 % | 1.300,9 MiB |
| 8 | 660,52 | 39.631 | 10,897 ms | 11,610 ms | 14,343 ms | 15,336 ms | 12,101 ms | 12,889 ms | 15,838 ms | 16,880 ms | 744,8 % | 1.308,6 MiB |
实测吞吐量与理想线性扩展的对比。
| 工作进程 | 实测 | 理想线性 | 扩展倍数 | 效率 |
|---|---|---|---|---|
| 1 | 92,65 | 92,65 | 1,00× | 100,0 % |
| 2 | 184,53 | 185,30 | 1,99× | 99,6 % |
| 4 | 358,82 | 370,60 | 3,87× | 96,8 % |
| 8 | 660,52 | 741,20 | 7,13× | 89,1 % |
这四个本地测试均在同一台 AMD Ryzen 9 9950X 上运行 60 秒。每次请求从 1,000 个大小为 800 KB 至 2.4 MB 的高复杂度背景和 237 种字体中选择,并随机改变文本、透明度、字体和头像大小、描边、文字和圆形颜色,以及着色器的角度、强度和色彩强度。约一半卡片还会启用液态图层,并随机设置颜色、不透明度和模糊程度。
负载生成器在闭环中使用 1、2、4 或 8 个并行 worker。每个 worker 只有在完整接收并验证前一个 PNG 后才开始下一次请求。所有 60 秒测试均无错误、超时或无效 PNG。
渲染时间包括图像生成以及 PNG 编码和压缩,直至得到最终 PNG。网络传输和下载时间不包括在内。
* 所述条件下的内部测量,未经独立验证,不构成承诺的特性、保证或性能承诺。实际性能可能因系统、负载和环境而异。
02 / 我们的技术基础
除了自研引擎,我们还使用 Rust、Leptos、TypeScript、Vue、Twilight、Dragonfly、PostgreSQL、ScyllaDB 和 RustFS。每项技术在系统中都有明确的职责。
编程语言
Rust 是我们的 Discord 机器人、后端、卡片编辑器和 Fjell 图形引擎的技术基础。
Web 框架
我们的部分 Web 界面使用 Leptos。这个框架让 Rust 能够用于浏览器端,并与其余技术栈自然衔接。
编程语言
我们使用 TypeScript 编写公共网站的交互页面。类型检查有助于保持组件和状态的一致性。
Web 框架
Vue 用于公共网站的导航、文档及其他交互区域的组件,让界面更易于维护和扩展。
标志 © Evan You. CC BY-NC-SA 4.0 · 未经修改
Discord 库
我们使用 Twilight 连接 Discord。这个库将我们用 Rust 编写的机器人与 Discord 相连。
标志由 Fezhoof 创作。CC BY-SA 4.0 · 未经修改
数据库
Dragonfly 是我们的共享临时数据存储,用于 Discord 事件、分片和多条网关连接的协调,以及 AutoMod 和活动日志。
软件采用 BSL 1.1 许可,并提供额外使用授权。
关系型数据库
PostgreSQL 用于补充关系型数据和结构的数据层,并与 ScyllaDB 一同作为基础设施中的独立组成部分运行。
数据库
ScyllaDB 与 PostgreSQL 一同构成我们的数据层。两套系统在 Fjord 中承担不同的任务。
对象存储
我们使用 RustFS 提供兼容 S3 的对象存储,并在 PostgreSQL 与 ScyllaDB 之外增加独立的存储层。
软件许可 Apache 2.0. RustFS 是 RustFS, Inc. 的商标。