下一代MongoDB驱动程序中的服务器选择

我喜欢烹饪。有时候,我的客人们非常喜欢这些食物,以至于会向我索要食谱。通常,我不得不坦白并没有相关食谱,我只是自己编造了一个!

即兴创作在厨房是件非常棒的事情,但是对于软件开发中的一致性而言并不是一个好方法。

MongoDB驱动程序团队负责10种语言11个驱动程序的编码和维护。我们希望我们的驱动程序在保持每种语言特色的前提下,有相同的行为。

实现上述功能的一个方法是:通过制定及共享驱动程序规范的文档,记录我们期望在各驱动程序上都拥有的一致行为 。就像是一个菜谱能够帮助一个厨师持续制作出美味的食物一样,这些规范可以引导MongoDB和社区中所有驱动程序上达到软件开发的一致性。

最近我们开发的规范之一涉及到了服务器选择。MongDB部署的生产一般由多个服务器组成,要么作为一个复制集或者作为一个分片集群。服务器的选择描述了一个驱动程序如何根据所有服务器最近的状态,为特定的读取或者写入操作选择合适服务器的流程。规范也提及了重新检查服务器状态的时间间隔,以及在多长时间内找不到合适服务器时候就决定放弃的时间间隔。

这篇文章的剩下部分描述了我们的设计目标以及在下一代MongoDB驱动程序中服务器选择的工作机制。

设计目标

最重要的目标是:服务器选择必须是可预测的。如果一个应用在一个单机服务器上开发,然后在生产中部署了一个复制集,最后在一个分片集群中使用,应用代码应该不需要改变的,只需要修改一些合适的配置就可以了。

例如,如果一个应用的一些部分需要读一个从节点时,程序应该能在一台单机服务器上运行(哪怕这个时候没有主节点和从节点的概念),正常运行于复制集中,或者在分片集群中通过mongos代理节点来读取。

第二个设计目标是:在任何可能的时候服务器选择都应该是韧性的。这就意味着:面临可检测到的服务器故障时,驱动程序应该继续使用备用服务器而不是出现故障返回错误。

对于一个写操作,这意味着需要等待主节点变得可用或者切换到另一个mongos(分片集群下)。对于一个读操作,在使用读选项(read preference)的情况下,这意味着选择一个备用服务器。

第三个设计目标是:服务器选择应该是低延迟的。这意味着:对一个操作而言,如果多台服务器都是合适的,那么一个更低平均轮询时间(RTT)应该要优先于其它服务器。

服务器选择规范概述

服务器选择规范[1]有4个主要部分:

  1. 配置
  2. 平均通信往返时间(RTT)
  3. 复制集读选项(Read Preference)
  4. 服务器选择算法

配置

服务器选择主要由两个配置变量控制:

  • serverSelectionTimeoutMS:serverSelectionTimeoutMS变量指定了驱动程序在服务器选择时,无法成功执行而放弃和报告错误的时间间隔(以毫秒为单位)。用户可以根据他们自己的意愿性,如耐心等待或者将错误快速返回给用户(例如,fail whale网页)来设置参数的大小。默认为30秒,这个时间对于在经典的故障恢复阶段中选举出新主节点来说已经足够。
  • localThresholdMS:如果对某个操作而言,存在多个合适的服务器,我们可以用localThresholdMS变量来确定一个基于延迟时间(RTT) 的可接受的”延迟窗口范围”(Latency Window)。以延迟最小的服务器作为基准,所有的服务器如其延迟时间和最小延迟时间的差值小于这个变量定义值,则这些服务器都可以有资格被随机的选中。如果变量设为0,则不使用随机算法,而是选择延迟时间最小的服务器。默认值是15毫秒,意味着有资格的服务器的延迟时间只能有比较微小(15ms)的不同。

如下图表所示,服务器A到E都是对某个操作而言合适的服务器:也许所有mongos服务器都可以处理一个写操作,并且所有的localThresholdMS变量已经被设置为100。服务器A有最低的平均轮询时间,为15ms,因此它定义了延迟窗口的下界。而上限是115ms,因此,只有A、B和C服务器在延迟窗口内。

image00_6

服务器A,B和C都在延迟窗口内

“localThresholdMS”变量经常被称为secondaryAcceptableLatencyMS,但是为了与mongos(已经使用localThreshold作为配置选项)保持更多的一致性而进行了重命名,因为它已经不再是只运用于从节点中。

平均通信往返时间(RTT)

另外一个驱动程序规范,服务器发现和监控[2],定义了驱动程序应该如何从一个种子列表中选择服务器并且一直监控服务器状态。

在监控过程中,驱动程序定期记录超级管理员命令的通信往返时间(RTT)。服务器选择规范使用一个指数加权的移动平均方程对这些数据进行计算。

如果将之前的平均值表示为RTTt-1,新的平均值(RTTt)由一个新的RTT度量值(Xt)和权重因子(α)使用下面的公式计算得到:

RTTt=α·Xt+(1-α)·RTTt-1

权重因子设为0.2,将平均通信往返值的85%权重放在最新的9个数值中,给最近的数据更多的权重意味着均值能够快速响应延迟方面的突然变化。

复制集读选项

一个复制集读选项表明在一个复制部署下哪台服务器应该处理读操作,经常在驱动程序中的连接字符串或者高级别的客户端对象中对复制集读选项进行配置。一些驱动程序也可能会允许在数据库、集合甚至在单个查询级别对复制集读选项进行设置。

一个复制集读选项可以被认为是一个有mode字段和可选的tag_sets字段组成的文档。

mode字段决定了优先主节点还是从节点:

  • primary:只读主节点
  • secondary:只读从节点
  • primaryPreferred:尽可能读主节点,不行再读从节点
  • secondaryPreferred:尽可能读从节点,不行再读主节点
  • nearest:按ping的网络延迟决定,读最近的节点

如果提供了tag_sets字段,包括可用于过滤从节点的标签集(因此,只会在mode不是“primary”的时候应用)

标签和标签集的相关术语会有一点混乱,因此服务器选择规范将它们定义为:

  • tag:一个单一的键值对
  • tag set:包括0或多个标签的集合
  • tag set list:一个标签集的有序列表

在一个复制集中,可以将一个标签集分配到每个服务器来表明每个服务器的用户定义属性。

如果复制集读选项标签集是服务器标签集的子集,那么复制集读选项的标签集就可以匹配服务器标签集。

例如,一个复制集读选项标签集{ dc: ‘ny’, rack: 2 }将会匹配标签集为{ dc: ‘ny’, rack: 2, size: ‘large’ }的服务器:

{ dc: ‘ny’, rack: 2 } ⊆ { dc: ‘ny’, rack: 2, size: ‘large’ }

由于标签集列表是有序的,能够匹配任何从节点的第一个标签集将被用于选择合适的从节点。例如,考虑下面的标签集列表(其中‘dc’表示‘数据中心’):

[ { dc: 'ny', rack: 2 }, { dc: 'ny' }, { } ]

首先,驱动程序尝试选择任何在ny(纽约)数据中心rack2上的从节点。如果没有,任何在ny(纽约)数据中心中的从节点将会被选择。如果ny(纽约)数据中心宕机了,最后的空标签集将会允许任何从节点被选择。

空标签集({})的行为看上去很奇怪,记住:在数学中,空集是任何集合的子集,因此空集匹配所有从节点。对于那些希望尽可能使用特定节点但是又不希望在从节点不可用情况下出现故障的应用而言,这是一个很好的备选方案。

服务器选择算法

当驱动程序需要选择服务器时,会遵循一系列步骤,要么选择出一个服务器,要么继续尝试,直到到达服务器选择超时为止。

在算法中,不同的部署类型有些细小的差别,以保证全部选择流程实现可预测的设计目标。

算法[3]的高层次概述如下:

1.记录服务器选择开始的时间

当选择开始时,驱动程序将会记录开始的时间以知道什么时候会发生超时。

2.根据拓扑类型选择合适的服务器

一个“合适的”服务器指的是在执行一个操作时能够满足所有准则的服务器。例如,对于写操作,服务器必须可以接受写入。

适用性的特殊规则根据部署类型的不同会发生变化:

  • 单个服务器:单个服务器适用于读操作和写操作。任何复制集读选项都将被忽略。
  • 复制集:只有主节点适用于写操作。对于读操作,服务器在能够满足复制集读选项的规则的条件下可以选用。
  • 分片集群:由于mongos是分片服务器的代理,任何mongos服务器都适用于读和写。对读操作,有效的复制集读选项将会传送给被选中的mongos,并将施用在每个分片上。

3. 随机选择延迟窗口(Latency Window)内合适的服务器

如果只有一个合适的服务器,那么选择该服务器,算法结束。

如果存在多台合适的服务器,则进一步过滤出在延迟窗口内的服务器。如果窗口内有超过一台满足条件的服务器,则随机选择一台满足均匀平衡负载的服务器,算法结束。

由于最短平均通信往返时间(RTT)的服务器定义了延迟窗口的下界,因此该服务器经常会被选中。

4.如果没有合适的服务器,等待服务器状态更新

如果没有选中任何服务器,例如:驱动程序需要找一个复制集的主节点,但是复制集出现故障并且正在进行新主节点的选举,那么驱动程序就会尝试更新它正在监控的服务器状态并且等待状态的变化。

5.如果超过服务器选择超时,报告一个错误

如果时间从服务器选择的开始到结束已经超过serverSelectionTimeoutMS毫秒,驱动程序会向应用报告一个错误。

6.返回步骤2

如果时间没有超时并且服务器的状态被更新,选举算法会继续寻找合适的服务器。

总结

服务器选择规范将会以一种持续的方式引导下一代MongoDB驱动程序进行服务器选择,实现可预测、弹性及低延迟等三个目标。

用户将可以使用serverSelectionTimeoutMS配置变量来控制服务器选择的时长,并且使用localThresholdMS配置变量来控制可接受延迟窗口的大小。

请查阅我们的博客下一代MongoDB驱动程序发布来了解更多关于下一代MongoDB驱动程序的信息。

关于作者-David

David是开发者体验团队的高级工程师。他一直活跃于开源软件超过15年,尤其是在Perl语言以及社区方面。在他没有编写规范文档之前,David维护MongoDB的Perl驱动程序并且尽可能避免社交媒介。

[1] http://goo.gl/HM3tgS

[2] http://goo.gl/HM3tgS

[3]在这篇文章中,已经对一些步骤进行了简化,并且一些客户端-服务器之间的互操作检查已经被忽略了。

翻译:周颖敏

审稿:TJ

本文译自:https://www.mongodb.com/blog/post/server-selection-next-generation-mongodb-drivers

发表评论