多数据中心路由

简介

在部署多数据中心集群时,通常希望利用本地性来降低延迟并提高性能。例如,最好是在本地数据中心的局域网延迟下执行图形密集型工作负载,而不是在远程数据中心的广域网延迟下执行。Neo4j 的负载均衡追赶策略插件可以轻松实现多数据中心场景。

Neo4j 的负载均衡是一个协作系统,其中驱动程序定期询问集群它应该将其工作负载的不同类别(例如,写入和读取)路由到哪里。这允许驱动程序在很长一段时间内独立工作,但时不时地检查以适应更改,例如添加新的服务器以提高容量。还有一些故障情况,例如驱动程序无法使用其任何分配的服务器时,它会立即再次询问。

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

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

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

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

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-to-server-group

随机连接到逗号分隔的列表server.cluster.catchup.connect_randomly_to_server_group中指定的服务器组中的任何可用辅助服务器。已弃用,请使用connect-randomly-to-server-tags

connect-randomly-within-server-group

随机连接到此服务器所属的任何服务器组中的任何可用辅助服务器。已弃用,请使用connect-randomly-to-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. 将使用来自策略的第一个令人满意的响应。

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

配置用户定义的追赶策略

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的数据库将从跨集群的领导权自动均衡中排除。因此,建议除非必要,否则不要使用此配置。