多数据中心路由

简介

在部署多数据中心集群时,通常需要利用地域性来降低延迟并提高性能。例如,最好是在本地数据中心以局域网延迟而非远距离数据中心以广域网延迟执行图密集型工作负载。Neo4j 针对多数据中心场景的负载均衡追赶策略插件正是为了实现这一点。

Neo4j 的负载均衡是一个协作系统,驱动程序会定期向集群询问其不同类型工作负载(例如,写入和读取)应导向何处。这允许驱动程序在很长一段时间内独立工作,同时不时检查以适应诸如为增加容量而添加新服务器等变化。在出现故障情况时,例如无法使用任何已分配的服务器时,驱动程序也会立即再次询问。

从客户端的角度来看,这在很大程度上是透明的。在服务器端,负载均衡行为使用简单的领域特定语言 (DSL) 进行配置,并以命名的负载均衡策略形式暴露给驱动程序进行绑定。所有服务器端配置都在主服务器上执行。

追赶策略插件是一组规则,用于定义辅助服务器如何联系集群中的上游服务器以同步事务日志。Neo4j 带有一组预定义的策略,并且也可以使用相同的 DSL 创建用户定义的策略。最后,Neo4j 支持一个 API,高级用户可以使用该 API 来增强上游推荐。

一旦追赶策略插件解析出令人满意的上游服务器,它将用于拉取事务以更新本地辅助服务器进行一次同步。对于后续更新,该过程会重复进行,以便始终解析出最优的可用上游服务器。

先决条件配置

服务器标签

跨多个数据中心的负载均衡和用户定义的追赶策略都基于服务器标签概念。

为了根据特定要求优化集群服务器的使用,它们使用服务器标签进行排序。服务器标签可以映射到数据中心、可用区或操作员领域中的任何其他重要拓扑元素,例如 usus-east。将相同的标签应用于多个服务器,可以将它们逻辑地分组在一起。请注意,服务器可以有多个标签。

服务器标签定义为一个键,该键映射到集群中的一组服务器。服务器标签在每个服务器上使用 neo4j.conf 中的 initial.server.tags 参数定义。集群中的每个服务器都可以标记为零个或多个服务器标签。服务器标签可以通过 ALTER SERVER 命令在运行时更改,详见更改服务器选项

示例 1. 使用服务器标签定义分组服务器

使用服务器标签对服务器进行分组是在 neo4j.conf 中实现的,如下例所示

usus-east 标记当前实例
initial.server.tags=us,us-east
london 标记当前实例
initial.server.tags=london
eu 标记当前实例
initial.server.tags=eu

请注意,服务器标签所隐含的组成员身份是明确的。例如,标记为 gb-london 的服务器不会自动成为标记为 gbeu 的服务器所在的标签组的一部分,除非该服务器也明确标记有这些标签。

用于读取的主服务器

根据部署和集群中可用服务器的数量,将读取工作负载路由到主服务器的策略有所不同。以下配置允许将读取工作负载路由到主服务器。有效值为 truefalse

dbms.routing.reads_on_primaries_enabled=true

负载均衡框架

在多数据中心 Neo4j 部署中,客户端应用程序有不同的拓扑感知负载均衡选项。有多种方法可以配置集群的负载均衡,以便客户端应用程序可以将其工作负载导向最合适的集群成员,例如附近的成员。

负载均衡系统基于插件架构,以实现未来的可扩展性和允许用户自定义。当前版本附带一个名为服务器策略插件的预设插件。

通过设置以下属性来选择服务器策略插件

dbms.routing.load_balancing.plugin=server_policies

在服务器策略插件下,可以在服务器端配置许多负载均衡策略,并以唯一名称暴露给驱动程序。驱动程序反过来必须在实例化时通过指定其名称来选择适当的策略。策略命名的常见模式是根据地理区域或预期的应用程序组。

在所有服务器上定义完全相同的策略至关重要,因为这应被视为集群范围的配置,否则会导致不可预测的行为。同样,不应删除或重命名正在使用的策略,因为它会破坏尝试使用这些策略的应用程序。然而,在相同名称下修改策略是完全可以接受和预期的。

如果驱动程序请求的策略名称不可用,则驱动程序无法使用集群。未指定任何名称的驱动程序将获得默认策略的行为(如果未更改)。默认策略(如果未更改)会将负载分配到所有服务器。可以将默认策略更改为任何命名策略可以具有的行为。

配置错误的驱动程序或负载均衡策略会导致次优的路由选择,甚至可能完全阻止与集群的成功交互。

如何编写自定义插件的详细信息在此处未作说明。如果您认为需要自定义插件,请联系 Neo4j 专业服务。

使用 Neo4j 驱动程序的负载均衡

启用并配置后,驱动程序将使用自定义负载均衡功能按预期路由流量。有关如何配置驱动程序以使用自定义负载均衡的说明,请参阅Neo4j 驱动程序手册

策略定义

负载均衡策略的配置对客户端应用程序透明,并通过简单的 DSL 表示。语法由一组按顺序考虑的规则组成。第一个产生非空结果的规则是最终结果。

rule1; rule2; rule3

每个规则又由一组过滤器组成,这些过滤器限制了被考虑的服务器,从完整集合开始。请注意,每个规则的评估都从完整的可用服务器集开始。

有一组固定的过滤器组成一个规则,它们使用箭头链接在一起。

filter1 -> filter2 -> filter3

如果在最后一个过滤器之后仍有任何服务器留下,则规则评估已产生结果,并将其返回给驱动程序。但是,如果没有服务器留下,则考虑下一个规则。如果没有规则能够产生可用结果,则驱动程序将发出失败信号。

策略名称

这些策略在 server_policies 插件的命名空间下配置,并根据需要命名。策略名称可以包含字母数字字符和下划线,并且区分大小写。下面是名为 mypolicy 的策略的属性键。

dbms.routing.load_balancing.config.server_policies.mypolicy=

实际策略使用 DSL 在值部分定义。

default 策略名称保留给默认策略。可以像配置其他策略一样配置此策略,它由未指定策略的驱动程序客户端使用。

此外,可以使用唯一的策略名称创建任意数量的策略。策略名称可以暗示其预期使用的特定区域或应用程序。

过滤器

有四种过滤器可用于指定规则,详情如下。语法类似于带参数的方法调用。

  • tags(name1, name2, …​)

    • 只有标记有指定标签中的任何一个的服务器才能通过过滤器。

    • 定义的名称必须与服务器标签的名称匹配。

    • 在 5.4 之前,tags() 被称为 groups(),它仍然有效但现已弃用。

  • min(count)

    • 只允许最少数量的服务器通过(或没有)。

    • 允许管理过载情况。

  • all()

    • 无需指定,因为它在每个规则的开头都是隐含的。

    • 隐式地是最后一个规则(使用 halt 覆盖此行为)。

  • halt()

    • 只在最后一个规则的最后一个过滤器中才有意义。

    • 停止处理任何其他规则。

标签过滤器本质上是一个 OR 过滤器,例如 tags(A,B),它通过带有标签 A、B 或两者(服务器标签的并集)的任何服务器。AND 过滤器也可以通过链式连接两个过滤器来创建,例如 tags(A) -> tags(B),它只通过带有这两个标签的服务器(服务器标签的交集)。

负载均衡示例

关于多数据中心集群的讨论介绍了一个四区域、多数据中心设置。那里使用了按区域划分的罗盘方向和区域内编号的数据中心,这里也使用了相同的假设设置。

nesw regions and dcs
图 1. 将区域和数据中心映射到服务器标签

负载均衡器的行为在属性 dbms.routing.load_balancing.config.server_policies.<policy-name> 中配置。指定的规则允许精细调整集群在负载下如何路由请求。

这些示例使用行继续符 \ 以提高可读性。在neo4j.conf 中也是有效语法,建议使用它将复杂的规则定义分解开来,每行一个新规则。

最严格的策略是坚持使用特定数据中心,排除所有其他数据中心

示例 2. 仅特定数据中心
dbms.routing.load_balancing.config.server_policies.north1_only=\
tags(north1)->min(2); halt();

此示例表明,其意图是将查询发送到标记为 north1 的服务器,该服务器映射到特定的物理数据中心,前提是其中有两个可用。如果无法提供至少两个标记为 north1 的服务器,则操作应 halt(),即不尝试任何其他数据中心。

虽然前面的例子展示了负载均衡规则的基本形式,但也可以更具扩展性

示例 3. 首选特定数据中心
dbms.routing.load_balancing.config.server_policies.north1=\
tags(north1)->min(2);

在这种情况下,如果至少有两个服务器标记为 north1,则负载会在它们之间进行均衡。否则,将使用整个集群中的任何服务器,回退到隐式的最终 all() 规则。

前面的例子在求助于整个集群之前只考虑了一个数据中心。如果通过服务器组暴露了层级或区域概念,回退可以更优雅

示例 4. 优雅地回退到邻近节点
dbms.routing.load_balancing.config.server_policies.north_app1=\
tags(north1,north2)->min(2);\
tags(north);\
all();

这个例子表示,集群应该在带有 north1north2 标签的服务器之间进行负载均衡,前提是它们至少有两个可用的机器。如果不能满足,则可以使用 north 区域中的任何服务器,如果整个 north 区域都离线,则可以使用集群中的任何服务器。

追赶策略插件

追赶策略插件是一组规则,用于定义辅助服务器如何联系集群中的上游服务器以同步事务日志。Neo4j 带有一组预定义的策略,并利用DSL 灵活创建用户定义的策略。最后,Neo4j 支持一个 API,高级用户可以使用该 API 来增强上游服务器推荐。

一旦追赶策略插件解析出令人满意的上游服务器,它将用于拉取事务以更新本地辅助服务器进行一次同步。对于后续更新,该过程会重复进行,以便始终解析出最优的可用上游服务器。

使用预定义的追赶策略配置上游选择策略

Neo4j 附带以下预定义的追赶策略插件。这些插件提供了用于选择上游服务器的粗粒度算法

插件名称 结果行为

connect-to-random-primary-server

随机连接到当前可用的任何主服务器

typically-connect-to-random-secondary

连接到任何可用的辅助服务器,但约 10% 的时间会连接到任何随机的主服务器。

connect-randomly-to-server-tags

随机连接到带有逗号分隔列表 server.cluster.catchup.connect_randomly_to_server_tags 中指定的任何服务器标签的任何可用辅助服务器

leader-only

仅连接到主服务器的当前 Raft 领导者。

connect-randomly-within-server-tags

随机连接到带有此服务器所拥有的任何服务器标签的任何可用辅助服务器

预定义策略通过配置 server.cluster.catchup.upstream_strategy 选项来使用。这样做允许指定事务数据上游提供者的有序策略偏好。列表中较早提供的是策略插件名称的逗号分隔列表,其中包含首选策略。追赶策略是通过按列表顺序询问每个策略是否可以提供可从中拉取事务的上游服务器来选择的。

示例 5. 定义上游服务器选择策略

考虑以下配置示例

server.cluster.catchup.upstream_strategy=connect-randomly-to-server-tags,typically-connect-to-random-secondary

通过此配置,辅助服务器首先尝试连接到 server.cluster.catchup.connect_randomly_to_server_tags 中指定的带有标签的任何其他服务器。如果找不到带有这些标签的任何活动服务器,则它会连接到一个随机的辅助服务器。

pipeline of strategies
图 2. 策略的第一个满意响应将被使用。

为了确保下游服务器在上游故障时仍能访问实时数据,任何服务器的最后手段始终是联系随机主服务器。这等同于将 server.cluster.catchup.upstream_strategy 配置以 connect-to-random-primary-server 结束。

配置用户定义的追赶策略

Neo4j 集群支持一个小型 DSL,用于配置客户端-集群负载均衡。这在领域特定语言过滤器中有详细描述。相同的 DSL 用于描述服务器如何绑定到另一个服务器以请求事务更新的偏好。

通过选择 user-defined 追赶策略来启用 DSL,如下所示

server.cluster.catchup.upstream_strategy=user-defined

一旦指定了用户定义的策略,我们就可以根据为集群设置的服务器标签向server.cluster.catchup.user_defined_upstream_strategy设置添加配置。

此功能通过两个示例进行描述

示例 6. 定义用户定义的策略

为了说明目的,建议了四个区域:northsoutheastwest,每个区域内都有一些数据中心,例如 north1west2。服务器标签的配置使得每个数据中心都映射到其自己的服务器标签。此外,假定每个数据中心都独立于其他数据中心发生故障,并且一个区域可以充当其组成数据中心的超组。因此,位于 north 区域的服务器可能具有类似 initial.server.tags=north2,north 的配置,这使得它位于与我们物理拓扑匹配的两个组中,如下图所示。

nesw regions and dcs
图 3. 将区域和数据中心映射到服务器标签

一旦服务器被打上标签,下一个任务是根据它们定义一些上游选择规则。出于设计目的,假设 north 区域中任何一个数据中心中的任何服务器如果可以,都倾向于在该数据中心内进行追赶,否则则求助于任何北方实例。要配置该行为,请添加

server.cluster.catchup.user_defined_upstream_strategy=tags(north2); tags(north); halt()

配置按从左到右的优先级顺序排列。tags() 运算符生成一个服务器标签,从中进行追赶。在这种情况下,只有在没有服务器标记为 north2 时,操作才会进行到 tags(north) 规则,该规则生成标记为 north 的任何服务器。最后,如果无法通过任何先前的标签解析服务器,则规则链将通过 halt() 停止。

请注意,使用 halt() 明确结束规则链。如果在规则链的末尾没有使用 halt(),则隐式添加 all() 规则。all() 是包容性的:它提供所有服务器,因此增加了找到可用上游服务器的可能性。然而,all() 是不加选择的,它提供的服务器不保证在拓扑或地理位置上是本地的,这可能会增加同步的延迟。

上述示例展示了通过使用服务器标签表达的简单偏好层次结构。但层次结构可以更加复杂。例如,可以对标记的追赶服务器设置条件。

示例 7. 带条件的用户定义策略

在此示例中,需要在选择从何处追赶之前粗略地判断集群的健康状况。为此,使用 min() 过滤器,如下所示

server.cluster.catchup.user_defined_upstream_strategy=tags(north2)->min(3), tags(north)->min(3); all();

tags(north2)->min(3) 表示只有在有三个可用服务器的情况下才应从标记为 north2 的服务器进行追赶,这里这被解释为健康状况良好的指标。如果 north2 不能满足该要求,则应尝试从标记为 north 的任何服务器进行追赶,前提是至少有三个可用服务器,如 tags(north)->min(3) 所述。最后,如果无法从足够健康的 north 区域进行追赶,则操作会(明确地)回退到整个集群,使用 all()

min() 过滤器是同标签服务器集合的简单但合理的健康指标。

优先数据中心

在多数据中心场景中,尽管这种情况很少发生,但可以偏向于指定数据库的写入方向。可以应用 db.cluster.raft.leader_transfer.priority_tag 来指定一组具有给定标签的服务器,这些服务器在为给定数据库选择领导者时应具有优先级。优先级标签可以设置在一个或多个数据库上,这意味着集群会尝试将配置数据库的领导权保持在具有配置服务器标签的服务器上。

配置了 db.cluster.raft.leader_transfer.priority_tag 的数据库将排除在集群领导权的自动均衡之外。因此,除非必要,否则建议不要使用此配置。