Facebook「配置大规模服务器」的兵刃:位置感知分发(LAD)

421 阅读7分钟
原文链接: mp.weixin.qq.com

Facebook的基础设施包括许多地域分散的数据中心,它们托管运行数百万台服务器。这些服务器运行众多系统,从前端Web服务器、新闻源(News Feed)聚合系统到消息传递和实时视频应用,不一而足。除了常规的代码推送外,我们还每天将千上万的配置变更发送到服务器。因此,我们的服务器执行数万亿次配置检查相当普遍。配置检查的一个例子是,为一个用户显示英语文本,为另一个用户显示葡萄牙语文本,确保Web服务器对用户体验实现个性化。

我们在本文中描述了位置感知分发(LAD),这个新的点对点(P2P)系统处理将配置变更分发到数百万台服务器的任务。我们发现,LAD在分发大型更新时出色得多,LAD可分发100 MB,之前只能分发5 MB;还灵活扩展,每个分发器可以支持约40000个订户,之前支持2500个订户。

背景:我们在LAD之前如何分发配置?

我们在2015年的SOSP论文中曾描述(https://research.fb.com/publications/holistic-configuration-management-at-facebook/),Facebook的配置管理系统(名为Configerator)使用开源分布式同步服务ZooKeeper来分发配置更新。

ZooKeeper中的所有数据存储在单个一致的数据存储区中。每个ZooKeeper节点(znode)可能含有数据或其他子节点znode。可以使用分层路径(比如/root/znode/my-node)来访问每个znode。从ZooKeeper读取数据的客户端可以对znodes进行监视;更新znode时,ZooKeeper会通知那些客户端,以便它们可以下载更新。

ZooKeeper强大的一致性和严格顺序保证对于我们可以可靠地扩展和运行系统来说至关重要。然而,随着基础设施的规模扩大到数百万台机器,我们发现ZooKeeper成了瓶颈。

  • 严格顺序保证:ZooKeeper的一项关键特性是,它提供严格顺序保证,这意味着写入始终由单个线程按顺序处理。现在,为了确保读取不至于等待时间过长,ZooKeeper使读取处理和写入处理交错进行。我们发现,针对特定znode的更新可能会触发大量监视,这反过来会触发客户端的大量读取。几个这样的写入可能导致停顿,从而阻碍更新。

  • 惊群问题:更新数据znode时,ZooKeeper会通知所有对更新有兴趣的客户端。这可能会触发惊群(thundering herd)问题,因为入站的客户端请求让ZooKeeper不堪重负。

  • 大型更新会使网卡无力处理:我们的一些配置文件最大可达5 MB。鉴于网卡速率为10 Gbps,我们发现一个设备只能为约2000个客户端提供服务。如果频繁更新大文件,我们会发现更新可能需要10秒才能分发到所有有兴趣的客户端。

设计着眼未来的系统

我们开始设计新的分发系统时提出了几个要求,包括如下:

  • 支持大型更新:文件大小从5 MB增加到100 MB。

  • 独立的数据存储区:ZooKeeper将数据存储区与其分发框架结合起来。我们希望数据存储和分发这两部分分开来,那样每个部分可以独自选型和扩展。

  • 分发容量:轻松支持数百万客户端。

  • 延迟:将分发延迟限制在5秒以内。

  • 配置文件:支持数量比之前基于ZooKeeper的系统多10倍的配置文件。

  • 灵活应对惊群问题和更新速度激增的能力。

位置感知分发(LAD)简介

LAD由两个主要组件组成,它们通过Thrift RPC相互联系:

  • 代理:在每个机器上运行的守护程序,负责将配置文件发送到任何有兴趣的应用程序。

  • 分发器:该组件负责两个任务。首先,它轮询数据存储区,查找有无新的更新。其次,它为一组对更新有兴趣的代理构建分发树。

上图显示了LAD如何将代理组织到分发树中,分发树实际上是一个组织有序的点对点网络。如步骤1所示,代理代表在设备上运行的应用程序向分发器发送“订阅”请求。分发器通过向父节点发送“添加peer”请求(步骤2),将代理添加到分发树。一旦代理添加到了分发树,它开始接收元数据(步骤3)更新。这些被过滤,代理响应内容请求(步骤4)。如果父节点含有内容,它会立即发送到子节点;不然,它会从父节点下载内容(步骤5)。

通过利用分发树,LAD确保仅将更新发送到有兴趣的代理,而不是发送到所有机器。此外,父节点机器可以将更新直接发送到子节点,这确保根(root)附近没有哪个机器不堪重负。

我们之前使用ZooKeeper时的一个重要心得是,将元数据更新和分发与内容分发分开来。 LAD的架构依赖代理不断接收元数据更新。如果每个代理都要接收所有元数据更新,请求量会太大。我们在LAD中通过使用分片(shard)解决了这个问题:我们不是依赖将整个数据树作为一颗分发树的一部分来提供,而是将它拆分成较小的分发树,每颗小分发树负责树的某个部分。

我们实现分片设计的方法是,让每个分片植根于目录结构中的某个点,然后将所有对象都包括在其下面。分片为订阅提供了一种便利的手段:它限制了分发树的总数,同时均衡了每个代理收到的元数据数量。

控制和数据流

控制平面(上图中的左侧)是分发器与每个订户之间的轻量级Thrift RPC。分发器使用控制平面发送树命令,树命令向父节点告知新的子节点,并检查订户的活跃度。订户也使用控制平面向分发器发送订阅请求。分发器将这些请求映射到该订阅所属的分片,并将订户添加到分发树中。

数据流平面(上图中的右侧)是Thrift RPC,位于分发树中的peer之间,负责完成繁重任务。父节点使用数据流平台,将源自分发器的元数据更新发送到子节点。子节点也使用数据流平面向父节点请求它们感兴趣的内容。

拥有独立的控制平面和数据流平面使每个分发器能够处理约40000个订户;ZooKeeper之前处理约2500个订户。

若干经验

我们已在构建和部署LAD方面获得了几个宝贵的经验。尤其是这几点:

  1. 工具和监控对于生产部署至关重要。我们明白,由于不清楚在任何某个请求的发送或接收路径中有哪些节点,基于P2P的系统运行和调试起来颇具挑战性。

  2. 故障注入和灾难准备测试在大规模环境下至关重要。除了详细记录的操作程序外,我们发现,运行测试、制定策略以便出现问题时积极有效地应对也很重要。具体来说,我们运行了一系列测试,有意引入了各种类型的应用程序、主机、网络和集群层面的故障,以验证LAD的弹性,又不影响任何客户端。这个做法颇有成效,因为我们不光发现了程序和工具方面的漏洞,还发现了代码缺陷。

  3. 持续定期的测试对于确保系统长期可靠至关重要。上面列出的测试仅仅运行一次远远不够,因为在Facebook节奏很快,系统或工具方面的观点可能随时有变。我们在使测试流程实现自动化,以便定期对故障做出响应。

LAD的下一步是什么?

LAD目前作为我们配置管理系统的数据分发框架部署到生产环境中。我们还在评估是否可用于其他场合的大规模内容分发。