在 X-Plane 上使用 Elixir 引导多人服务器

2021-07-30 00:42:18

欢迎阅读我们关于在生产中使用 Elixir 的公司的系列案例研究。查看我们迄今为止发布的所有案例。 X-Plane 11 是世界上最全面、最强大的个人电脑和移动设备飞行模拟器。 X-Plane 不是游戏,而是由 Laminar Research 创建的工程工具,可用于以令人难以置信的准确度预测固定翼和旋翼飞机的飞行质量。 X-Plane 特许经营权有消费者版和 FAA 认证的专业版。最近,X-Plane 团队接受了添加多人游戏体验的挑战,目标是在同一会话中容纳 10000 名以北的用户。本文探讨了他们选择 Elixir 的原因,以及一个由一个开发人员组成的团队(之前没有语言经验)如何在 6 个月内学习该语言并部署了广受欢迎的多人游戏体验。整体解决方案采用 Elixir 中 RakNet 通信协议的全新开源实现,并在遇到意外负载时超过了原始要求。 X-Plane 团队长期以来一直在模拟器中提供点对点多人游戏,但从未提供服务器托管的多人游戏。这对他们来说是一个新的旅程,他们可以完全自由地选择技术堆栈。根据他们的博客文章,他们的目标是:构建一个具有错误隔离功能的坚如磐石的服务器。例如,客户端更新期间的异常不应导致整个服务器停机。实现一个可以扩展到数万个并发试验的共享世界。快速迭代:因为这是 Laminar 研究团队第一次提供托管多人游戏环境,他们希望快速行动以发布该系统。这将允许用户立即开始组队飞行,并作为一个平台来衡量对进一步功能开发的兴趣。

要快速和一致。多人游戏具有“软实时”约束,他们需要始终如一地准时为所有客户提供服务。从数量上讲,这意味着 99% 的响应时间比平均值或中位数重要得多。从这些需求来看,对稳定性和快速迭代的需求排除了低级语言,即使是那些他们有大量内部经验的语言。对速度和垂直可扩展性的需求排除了许多现代 Web 语言,例如 Ruby 和 Python,在这些语言中,扩展模型通常会投入更多的服务器。必须避免在多台机器之间同步状态,这需要更多的开发时间,并且由于延迟的增加而恶化了用户体验。他们最终确定了三个顶级竞争者:Rust、Go 和 Elixir。 Elixir 的优势在于两个独特的功能:容错和可预测的延迟。两者都内置于 Erlang 虚拟机的核心——Elixir 运行的强大平台。 X-Plane 负责该实施的工程师 Tyler Young 强调说:“我们想要一个可以最大化服务器容量的堆栈。我们宁愿运行 64 核机器,也不愿运行数十个 4 核 VM。 Saša Jurić 的演讲,Erlang 和 Elixir 的灵魂,向我们展示了平台提供的并发模型、进程隔离和部分重启是我们正在寻找的抽象。”准备尝试 Elixir 时,Tyler 拿起了几本书,但很快意识到该语言的入门指南提供了他需要的背景知识。他解释说:“虽然介绍性指南涵盖了语言结构,但网站上的高级指南让您使用 TCP 连接构建一个实际项目,以及我们将在生产中使用的基本架构模式。”然而,他并没有一头扎进多人服务器,而是决定让 Elixir 尝试解决一个较小的问题。他为美国国家海洋和大气管理局 (NOAA) 气象服务编写了一个网络代理,并将其投入生产。这次经历教会了他利用 Erlang VM 提供的所有工具和指标的重要性。他们选择 AppSignal 来帮助消费和消化这些信息。两周后,他通过在 Elixir 中实现以 UDP 为中心的 RakNet 协议,开始在服务器上工作。不幸的是,文档很少,因此大多数时候他们不得不参考 C++ 中的参考实现。幸运的是,由于其根植于电信和网络服务,Elixir 和 Erlang 内置了对解析二进制数据包的支持,这让这项任务变得很有趣。该团队还将每个 UDP 连接映射到 Elixir 中不同的轻量级执行线程,我们称之为进程。 Elixir 进程便宜、隔离、并发,并且由运行时合理调度。这种设计使 X-Plane 团队能够充分利用最初将他们吸引到平台的稳健性和可预测延迟的特性。他们的实现是在 Erlang 的 gen_udp 之上编写的,并且是开源的。

在选择 Elixir 五个月后,他们开始欢迎 beta 测试人员加入服务器。社区的反应非常积极,新的多人游戏体验在一个月后上线后导致订阅数量大幅增加。目前,X-Plane 在北美的玩家群由一台服务器驱动,运行在 1 台 16GB 内存的八核机器上,尽管有效使用了 200MB 或 300MB 的内存。每个连接的播放器每秒发送 10 个更新。对于部署,他们使用蓝绿策略,在相同容量的两台服务器之间交替。 Tyler 解释说:“我们知道 Erlang VM 提供热代码交换和分发,但我们尽可能采取最简单的路线。我们在部署期间在两台服务器之间交替使用要容易得多,因为服务器很稳定,而且我们不经常部署。同样,在分销方面,我们更喜欢垂直扩展或为全球玩家在不同地区设置不同的服务器。”项目启动后加入的 Paul McCarty 可以证明它的简单性:“即使没有 Elixir 之前的经验,我也能够尽早加入并为我们的 HTTP API 添加新功能。”这些 API 构建在 Plug 之上,以支持其聊天服务、提供有关已连接用户的信息等。他总结道:“在添加新功能时,服务器开发永远不是瓶颈。”保罗和泰勒用一个奇怪的轶事结束了我们的谈话:几个月前,他们发布了一个更新的客户端版本,其中包含调试代码。这个额外的代码导致每个连接的用户每 100 毫秒不断地 ping 服务器,即使不是在多人模式下也是如此。这导致他们的流量增加了 1000 倍!两周后,当他们看到 Elixir 服务器中的 CPU 使用率从 5% 增加到 21% 时,他们才发现这种增长。一旦他们发现了根本原因以及系统如何处理它,他们就会意识到他们不必急于更新客户端来删除调试代码,他们选择保持正常的发布周期。归根结底,这是他们围绕语言和平台获得和建立的信心的完美例子。