使用 Speakeasy(赞助)生成您的 MCP 服务器
不管你喜欢与否,你的 API 有了一个新用户:人工智能代理。使用 MCP(模型上下文协议)服务器让他们轻松访问您的 API 服务。 Speakeasy 使用您的 OpenAPI 规范生成 MCP 服务器,其中包含用于所有 API 操作的工具,从而使构建代理工作流程变得容易。
生成服务器后,使用 Speakeasy 平台开发评估、提示和自定义工具集,将您的 AI 开发人员平台提升到新的水平。
免责声明:本文中的详细信息来自 Meta 工程团队撰写的文章。技术细节的所有功劳都归功于元/线程工程团队。原始文章和视频的链接位于帖子末尾的参考部分。我们试图分析细节并提供我们的意见。如果您发现任何错误或遗漏,请发表评论,我们将尽力修复。
Meta 最新的社交平台 Threads 于 2023 年 7 月 5 日推出,是一个实时的公共对话空间。
该产品由一个小型工程团队在不到五个月的时间内开发完成,立即获得了动力。基础设施团队必须立即响应令人难以置信的需求。
当一款新应用程序在一周内达到 1 亿注册量时,我们本能地会假设有人在一夜之间构建了一个奇迹后端。线程的情况并非如此。没有时间构建新系统或定制扩展计划。唯一的选择是相信已经就位的机器。
那台机器运转得相当顺利。随着 5 天内有数百万人注册,后端系统仍在运行,从用户的角度来看,一切都按预期进行。
线程没有扩展是因为它很幸运。它之所以能够扩展,是因为它继承了 Meta 的强化基础设施:这些平台是根据 Facebook、Instagram 和 WhatsApp 十年来的经验教训塑造的。
本文探讨了在 Threads 成功推出过程中发挥关键作用的两个平台:
-
ZippyDB,分布式键值存储,支持状态和搜索。
-
异步,无服务器计算引擎,可卸载数十亿后台任务。
这些系统都不是为线程构建的。但如果没有它们,Threads 就无法工作。
ZippyDB 每天已经在分布式区域管理数十亿次读取和写入。此外,异步一直在超过 100,000 台服务器上处理数万亿个后台作业,默默地为从提要生成到遵循建议的一切提供支持。
ZippyDB:超大规模的键值
ZippyDB 是 Meta 的内部分布式键值存储,旨在提供大规模的强一致性、高可用性和地理弹性。
其核心是,它构建在 RocksDB 上进行存储,使用 Meta 的 Data Shuttle(基于 Multi-Paxos 的协议)扩展复制,并通过称为 Shard Manager 的系统管理放置和故障转移。
与针对单一产品定制的专用数据存储不同,ZippyDB 是一个多租户平台。数十个用例(从元数据服务到产品功能状态)共享相同的基础设施。这种设计可确保更高的硬件利用率、集中可观察性以及跨工作负载的可预测隔离。
ZippyDB 的架构
ZippyDB 并不将部署视为一个整体。它分为多个部署层:分布在各个地理区域的计算和存储资源的逻辑组。
每一层服务于一个或多个用例,并提供故障隔离、容量管理和复制边界。最常用的是通配符层,它充当多租户默认值,平衡硬件利用率和操作简单性。对于具有严格隔离或延迟限制的用例,存在专用层。
在每一层中,数据被分解为碎片,这是分发和复制的基本单位。每个分片都是独立管理的,并且:
-
在一定数量的 Paxos 节点上同步复制,以实现持久性和一致性。这保证了写入能够承受区域故障并满足强一致性要求。
-
异步复制到跟随者副本,这些副本通常与高读取流量区域位于同一位置。这些副本以宽松的一致性提供低延迟读取,从而在不牺牲全局持久性的情况下实现快速访问。
这种混合复制模型(基于仲裁的强大写入与区域读取优化相结合)为 ZippyDB 提供了跨各种工作负载的灵活性。
请参见下图,该图显示了 ZippyDB 支持的基于区域的复制的概念。
为了进一步推动可扩展性,ZippyDB 在分片下引入了一层逻辑分区:μshard(微分片)。这些是小的、相关的关键范围,可以对数据局部性和移动性提供更细粒度的控制。
应用程序不直接处理物理分片。相反,它们写入μshards,ZippyDB 根据访问模式和负载平衡要求将其动态映射到底层存储。
ZippyDB 支持两种主要策略来管理μ分片到分片的映射:
-
紧凑映射:最适合数据分布相对静态的工作负载。仅当分片变得太大或太热时,映射才会发生变化。该模型优先考虑稳定性而不是敏捷性,并且在具有可预测访问模式的系统中很常见。
-
Akkio Mapping:专为动态工作负载而设计。一个名为 Akkio 的系统持续监控访问模式并重新映射 μshard 以优化延迟和负载。这对于用户需求全天在不同地区变化的全球产品来说尤其有价值。 Akkio 减少了数据重复,同时提高了局部性,使其成为提要个性化、元数据密集型工作负载或动态键空间等场景的理想选择。
在 ZippyDB 中,分片管理器充当领导和故障转移的外部控制器。它不参与数据路径,但在保持系统协调方面发挥着关键作用。
分片管理器为每个分片分配一个主副本并定义一个纪元:版本化的领导租约。纪元确保在任何给定时间只有一个节点具有写权限。当主节点发生变化时(例如,由于故障),分片管理器会增加纪元并分配新的领导者。主节点定期向分片管理器发送心跳。如果心跳停止,分片管理器会认为主节点不健康,并通过提升新节点和提升纪元来触发领导者选举。
下图显示了分片管理器的角色:
ZippyDB 中的一致性和持久性
在分布式系统中,一致性很少是非黑即白的。 ZippyDB 通过为客户提供对一致性和持久性级别的每个请求控制来实现这一点,从而允许团队根据工作负载特征调整系统行为。
1 – 强一致性
ZippyDB 中的强一致性可确保读取始终反映最新确认的写入,无论读取或写入源自何处。为了实现这一点,ZippyDB 将这些读取路由到主副本,该副本保存分片的当前 Paxos 租约。租约确保任何时候只有一个主数据库存在,并且只有它可以提供线性化读取服务。
如果租约状态不清楚(例如,在领导层变更期间),读取可能会回退到仲裁检查以避免脑裂情况。这增加了一些延迟,但保持了正确性。
2 – 有界陈旧性(最终一致性)
ZippyDB 中的最终一致性并不是它在其他系统中所暗示的松散承诺。在这里,它意味着有限的陈旧性:读取可能会稍微落后于最新的写入,但它永远不会提供超出定义阈值的陈旧数据。
追随者副本(通常距离用户更近)为这些读取提供服务。 ZippyDB 使用心跳来监控追随者延迟,并且仅提供来自可接受延迟窗口内的副本的读取。这可以实现快速的区域本地读取,而不会影响操作顺序。
3 – 读你所写的一致性
对于需要因果保证的客户,ZippyDB 支持读你所写的模型。
写入后,服务器返回版本号(基于 Paxos 序列排序)。客户端缓存该版本并将其附加到后续读取中。然后,ZippyDB 确保读取反映该版本或之后的数据。
此模型非常适合会话绑定的工作负载,例如配置文件更新后立即刷新。
4 – 快速确认写入模式
在写入延迟比持久性更重要的场景中,ZippyDB 提供了快速确认写入模式。一旦写入在主服务器上排队进行复制,就会立即得到确认,而不是在它们完全保留在仲裁中之后。
这可以提高吞吐量和响应能力,但也需要权衡:
-
持久性较低(如果主服务器在复制之前崩溃,数据可能会丢失)。
-
一致性较弱(在复制完成之前,读取器可能看不到写入内容)。
此模式非常适合可以容忍偶尔丢失或使用幂等重试的系统。
事务和条件写入
ZippyDB 支持需要跨多个键进行原子读-修改-写操作的应用程序的事务语义。与提供可调隔离级别的系统不同,ZippyDB 使事情变得简单且安全:默认情况下所有事务都是可序列化的。
交易
ZippyDB 使用乐观并发控制来实现事务:
-
客户端读取数据库快照(通常来自追随者)并组装写入集。
-
它们将读取和写入集以及快照版本发送到主服务器。
-
主要检查冲突,自快照以来是否有任何其他事务修改了相同的键。
-
如果没有冲突,事务将提交并通过 Paxos 进行复制。
如果检测到冲突,则事务被拒绝,并且客户端重试。这可以避免锁争用,但在写入冲突很少发生时效果最佳。
ZippyDB 维护最近的写入历史记录以验证事务资格。为了保持较低的开销,它会定期修剪旧状态。跨纪元(即跨主故障转移)的事务会被自动拒绝,这简化了正确性保证,但代价是领导者更改期间的一些可用性。
条件写入
对于更简单的用例,ZippyDB 公开了一个条件写入 API,该 API 在内部映射到服务器端事务。该 API 允许执行以下操作:
-
“只有当这个键不存在时才设置它。”
-
“仅当该值与 X 匹配时才更新该值。”
-
“仅当该密钥存在时才将其删除。”
这些操作避免了客户端读取和往返的需要。在内部,ZippyDB 评估前提条件,检查冲突,如果通过则将写入作为事务提交。
在逻辑取决于当前密钥的存在或状态的情况下,此方法简化了客户端代码并提高了性能。
为什么 ZippyDB 对线程至关重要?
Threads 没有几个月的时间来构建自定义数据基础设施。它需要从第一天起就进行大规模的读写。 ZippyDB 处理几个核心职责,例如:
-
计数器:点赞数、关注者计数和其他快速变化的指标。
-
Feed 排名状态:用于对用户主页 Feed 中显示的内容进行排序和过滤的持续信号。
-
搜索状态:支持实时发现的基础索引。
ZippyDB 的价值不仅在于性能,还在于适应性。作为一个多租户系统,它支持新服务的快速上线。团队无需配置自定义分片或复制架构设置。他们配置了所需的内容,并从第一天起就受益于全球分布、一致性保证和监控。
在发布时,Threads 预计会增长。但很少有人预测到其速度:不到一周的时间就有 1 亿用户。这种增长不允许手动分片规划或最后一刻的迁移。
ZippyDB 的重新分片协议将潜在的瓶颈变成了小事。其客户端将数据映射到逻辑分片中,这些逻辑分片动态路由到物理机器。当负载增加时,系统可以:
-
提供新的物理分片。
-
实时重新分配逻辑到物理映射,无需停机。
-
使用后台工作人员迁移数据,通过原子切换确保一致性。
无需更改应用程序代码。该系统透明地处理重新映射和移动。自动化工具会协调这些转换,从而在需要时实现横向扩展,而不是在数小时或数天后。
这种方法允许线程从小规模开始,在早期开发过程中节省资源,并随着使用量的爆炸式增长而自适应扩展,而不会面临中断或性能下降的风险。
在 Threads 的启动窗口期间,该平台在几个小时内吸收了数千台机器。多租户在这里发挥了关键作用。重新分配使用率较低的密钥空间的闲置容量,隔离边界确保线程可以扩展,而不会导致其他工作负载匮乏。
异步 – 元规模无服务器
Async 是 Meta 的内部无服务器计算平台,正式名称为 XFaaS(可扩展功能即服务)。
在高峰期,异步每天可在超过 100,000 台服务器上处理数万亿次函数调用。它支持多种语言,例如 HackLang、Python、Haskell 和 Erlang。
Async 的独特之处在于它抽象了编写函数和在全局范围内运行函数之间的所有内容。无需提供服务。将代码放入异步中,它继承了元级扩展、执行保证和灾难恢复能力。
异步在线程中的作用
当 Threads 启动时,最关键的功能之一在 UI 中不可见。只需点击一下即可复制用户的 Instagram 关注图。这一操作的背后是数百万个函数调用:每个新的 Threads 用户可能会批量关注数百或数千个帐户。
同步地这样做是行不通的。阻止图形复制上的 UI 会导致超时、响应能力差和用户沮丧。相反,线程将这项工作卸载给异步。
异步对这些作业进行排队,将它们分布在整个队列中,并以受控的方式执行它们。每次名人加入 Threads 时都会重复相同的模式 – 数百万用户收到关注推荐和通知,所有这些都通过异步管道传输,而不会增加数据库负载或淹没下游服务。
异步如何应对激增
异步不需要针对线程进行调整。它一如既往地进行扩展。
有几个功能是 Threads 成功的关键:
-
排队会推迟不太紧急的作业,以防止与实时任务发生争用。
-
批处理将许多轻量级作业合并为更少、更重的作业,从而减少调度程序的开销并提高缓存效率。
-
当下游系统(如 ZippyDB 或社交图服务)出现饱和迹象时,容量感知调度会限制作业执行。
这不是被动调整。这是一种主动的适应。异步观察系统负载并自动调整流量。 Threads 工程师不需要寻呼任何人或重新配置服务。异步将其执行速度与生态系统可以处理的速度相匹配。
开发者经验
异步最强大的方面之一是线程工程师不需要考虑规模。一旦业务逻辑被编写并加入到异步中,平台就会处理剩下的事情:
-
交付窗口:作业可以指定执行时间,允许推迟或确定优先级。
-
重试:透明地,通过退避重试暂时失败。
-
自动调节:根据系统运行状况动态调整作业率。
-
多租户隔离:一种产品的激增不会影响另一种产品。
这些保证使工程师能够专注于产品行为,而不是操作限制。异步提供了一层可预测的弹性,吸收了可能导致不太成熟的系统瘫痪的流量峰值。
结论
Threads 的发布就像是对 Meta 基础设施的压力测试。结果不言而喻。不到一周就有一亿用户加入。但没有发生重大停电。
这种规模并非偶然发生。
ZippyDB 和 Async 并不是在构建时考虑到线程。但 Threads 之所以成功,是因为这些系统已经就位,并且经过十年来为 Meta 核心应用程序的数十亿用户提供服务而得到强化。他们提供了一致性、耐用性、弹性和可观察性,而无需产品团队进行定制工作。
这是高速工程的发展方向:可组合且经过深度实战测试的模块化基础设施。并非所有系统都需要是尖端的。然而,处理关键路径(例如状态、计算和消息传递)的路径必须是可预测的、可靠的和不可见的。
参考:
赞助我们
让您的产品展示在超过 1,000,000 名技术专业人士面前。
我们的时事通讯将您的产品和服务直接呈现在重要受众面前 – 数十万工程领导者和高级工程师 – 他们对重大技术决策和大宗采购具有影响力。
空间很快就满了 – 立即预订
广告位通常会提前约 4 周售完。为了确保您的广告吸引到这些有影响力的受众,请立即发送电子邮件至[email protected]来预订您的空间。
原文: https://blog.bytebytego.com/p/how-meta-built-threads-to-support