集群服务器发现

为了加入正在运行的集群,任何新成员都必须知道集群中至少一些其他服务器的地址。此信息对于连接到服务器、运行发现协议以及获取有关集群的所有信息都是必需的。

Neo4j 提供了几种机制,用于集群成员相互发现并根据配置、集群运行的环境以及使用的 Neo4j 版本来形成集群。

在 Neo4j 5.23 中,引入了发现服务 v2。建议在新部署中使用新版本 v2。

当前版本 v1 已弃用,将在未来的版本中移除。因此,强烈建议从 v1 迁移到 v2。有关详细信息,请参阅 从发现服务 v1 迁移到 v2

新版本 v2 引入了以下配置设置来控制发现服务

以下部分描述了服务器发现的不同方法、配置设置以及从发现服务 v1 迁移到 v2 的示例。

服务器发现方法

根据当前使用的 dbms.cluster.discovery.resolver_type 类型,发现服务可以使用服务器地址列表、DNS 记录或 Kubernetes 服务来发现集群中的其他服务器。发现配置用于初始发现,并持续交换有关集群拓扑更改的信息。

使用服务器地址列表进行发现

如果事先知道其他集群成员的地址,则可以显式列出它们。但是,此方法存在一些限制,例如

  • 如果替换服务器并且新成员具有不同的地址,则列表将过时。可以通过确保可以通过与旧成员相同的地址访问新成员来避免列表过时,但这并不总是可行的。

  • 在某些情况下,配置集群时地址未知。例如,当使用容器编排部署集群时,可能会出现这种情况。

要使用此方法,请设置 dbms.cluster.discovery.resolver_type=LIST 并在每个服务器的配置中硬编码地址。例如

dbms.cluster.discovery.resolver_type=LIST

server.cluster.advertised_address=server01.example.com:6000
dbms.cluster.discovery.v2.endpoints=server01.example.com:6000,server02.example.com:6000,server03.example.com:6000

dbms.cluster.discovery.version=V2_ONLY
dbms.cluster.discovery.resolver_type=LIST

server.discovery.advertised_address=server01.example.com:5000
dbms.cluster.discovery.endpoints=server01.example.com:5000,server02.example.com:5000,server03.example.com:5000

dbms.cluster.discovery.version=V1_ONLY

使用发现服务 v1 的此方法的示例由 配置具有三个服务器的集群 说明。

使用具有多个记录的 DNS 进行发现

在无法或不方便显式列出要发现的集群成员的地址的情况下,可以使用基于 DNS 的机制。在这种情况下,服务器启动时会根据配置设置执行 DNS 记录查找。服务器加入集群后,集群中的服务器之间会进一步通信拓扑更改,作为发现服务的一部分。

可以使用以下基于 DNS 的机制来获取集群中其他服务器的地址以进行发现

dbms.cluster.discovery.resolver_type=DNS

使用此配置,初始发现成员将从DNS A 记录解析以查找要联系的 IP 地址。在版本 1 中,dbms.cluster.discovery.endpoints 的值应设置为单个域名和发现服务的端口,而在版本 2 中,dbms.cluster.discovery.v2.endpoints 的值应设置为单个域名和集群公告端口。例如

dbms.cluster.discovery.resolver_type=DNS

server.cluster.advertised_address=server01.example.com:6000
dbms.cluster.discovery.v2.endpoints=cluster01.example.com:6000

dbms.cluster.discovery.version=V2_ONLY
dbms.cluster.discovery.resolver_type=DNS

server.discovery.advertised_address=server01.example.com:5000
dbms.cluster.discovery.endpoints=cluster01.example.com:5000

dbms.cluster.discovery.version=V1_ONLY

执行 DNS 查找时,域名会为集群中的每个服务器返回一个 A 记录,其中每个 A 记录包含服务器的 IP 地址。配置的服务器使用来自 A 记录的所有 IP 地址加入或形成集群。

使用此配置时,所有服务器上的发现端口必须相同。如果无法做到这一点,请考虑使用发现类型 SRV

dbms.cluster.discovery.resolver_type=SRV

使用此配置,初始发现成员将从DNS SRV 记录解析以查找要联系的 IP 地址/主机名和发现服务端口 (v1) **或** 集群公告端口 (v2)。

dbms.cluster.discovery.endpoints 的值必须设置为单个域名,端口设置为 0。当执行 DNS 查找时,域名会返回单个 SRV 记录。例如

dbms.cluster.discovery.resolver_type=SRV

server.cluster.advertised_address=server01.example.com:6000
dbms.cluster.discovery.v2.endpoints=cluster01.example.com:0

dbms.cluster.discovery.version=V2_ONLY

DNS 返回的 SRV 记录应包含 IP 地址或主机名,以及要发现的服务器的**集群**端口。配置的服务器使用来自 SRV 记录的所有地址加入或形成集群。

dbms.cluster.discovery.resolver_type=SRV

server.discovery.advertised_address=server01.example.com:5000
dbms.cluster.discovery.endpoints=cluster01.example.com:0

dbms.cluster.discovery.version=V1_ONLY

DNS 返回的 SRV 记录应包含 IP 地址或主机名,以及要发现的服务器的**发现**端口。配置的服务器使用来自 SRV 记录的所有地址加入或形成集群。

Kubernetes 中的发现

一个特殊情况是,当集群在 Kubernetes 中运行并且每个服务器都作为 Kubernetes 服务运行时。然后,可以使用列表服务 API 获取其他服务器的地址,如 Kubernetes API 文档 中所述。

以下设置用于配置此场景

使用此配置时,不会使用 dbms.cluster.discovery.endpoints,并且分配给它的任何值都将被忽略。

  • 运行 Neo4j 的 Pod 必须使用具有列出服务的权限的服务帐户。有关更多信息,请参阅 Kubernetes 文档中关于 RBAC 授权ABAC 授权 的内容。

  • 配置的 server.discovery.advertised_address 必须与 Kubernetes 内部 DNS 名称完全匹配,该名称的格式为 <service-name>.<namespace>.svc.cluster.local

发现配置用于初始发现,并持续交换有关集群拓扑更改的信息。

从发现服务 v1 迁移到 v2

从 Neo4j 5.23 开始,您可以从当前的发现服务 v1 迁移到新版本 v2。v1 和 v2 发现服务旨在能够并行运行。它们彼此完全独立,因此允许您在从 v1 切换到 v2 时保持集群正常运行。

根据环境和集群的要求,有三种方法可以从当前的发现服务 v1 迁移到新版本 v2。

准备工作

以下示例假设您有一个运行了三个服务器的集群,并且您希望从当前的发现服务 v1 迁移到新版本 v2。

v1 only

在从当前的发现服务 v1 迁移到新版本 v2 之前,请确保根据使用的 dbms.cluster.discovery.resolver_type 类型将新设置添加到配置中

  • 如果 dbms.cluster.discovery.resolver_type=LIST,则将 dbms.cluster.discovery.v2.endpoints 设置为 server.cluster.advertised_address 的逗号分隔列表。在操作过程中,设置 dbms.cluster.discovery.endpointsdbms.cluster.discovery.v2.endpoints 非常重要。有关更多信息,请参阅 使用服务器地址列表进行发现

  • 如果 dbms.cluster.discovery.resolver_type=DNS,则将 dbms.cluster.discovery.v2.endpoints 设置为单个域名和集群端口。或者,如果 dbms.cluster.discovery.resolver_type=SRV,则将 dbms.cluster.discovery.v2.endpoints 设置为单个域名,并将端口设置为 0。在操作过程中,设置 dbms.cluster.discovery.endpointsdbms.cluster.discovery.v2.endpoints 非常重要。有关更多信息,请参阅 使用具有多个记录的 DNS 进行发现

  • 如果 dbms.cluster.discovery.resolver_type=K8S,则将 dbms.kubernetes.discovery.v2.service_port_name 设置为 Kubernetes 服务定义中用于集群端口的服务端口名称。在操作过程中,设置 dbms.kubernetes.service_port_namedbms.kubernetes.discovery.v2.service_port_name 非常重要。有关更多信息,请参阅 Kubernetes 中的发现

就地滚动升级

就地滚动升级会暂时降低容错能力,因为您正在重新启动正在运行的服务器。为了保持容错能力,您可以暂时引入第四台服务器。

  1. 对于所有服务器,请确保根据 准备工作 部分中的详细信息将新设置添加到配置中。

    例如,对于使用列表解析器的用户,所有服务器的设置应包括

    dbms.cluster.discovery.resolver_type=LIST
    
    dbms.cluster.discovery.endpoints=server01.example.com:5000,server02.example.com:5000,server03.example.com:5000
    dbms.cluster.discovery.v2.endpoints=server01.example.com:6000,server02.example.com:6000,server03.example.com:6000
  2. 使用新的设置 dbms.cluster.discovery.version=V1_OVER_V2 重新启动 server01。

    in place 1 v1 over v2

  3. 运行 SHOW SERVERS 并确保所有三个成员都处于活动状态。

  4. 然后对 server02 和 server03 重复步骤 2 和 3。确保它们设置为 dbms.cluster.discovery.version=V1_OVER_V2。按顺序重启它们,而不是并行重启。

    in place 23 v1 over v2

  5. 使用bolt://连接到服务器1、2、3的系统数据库,并运行以下过程。例如,可以使用./cypher-shell -a bolt://localhost:7681 -d system来完成此操作。

    CALL dbms.cluster.showParallelDiscoveryState();

    它们应该在stateComparison列中显示“Matching”。

    预期结果
    +----------------------------------------------------------------+
    | mode         | stateComparison | v1ServerCount | v2ServerCount |
    +----------------------------------------------------------------+
    | "V1_OVER_V2" | "Matching"      | "3"           | "3"           |
    +----------------------------------------------------------------+

    如果它们没有显示,请等待并重试。

  6. 使用新的设置dbms.cluster.discovery.version=V2_OVER_V1重新启动服务器01。

    in place 1 v2 over v1

  7. 运行 SHOW SERVERS 并确保所有三个成员都处于活动状态。

  8. 然后对服务器2和3重复步骤6和7。确保它们设置为dbms.cluster.discovery.version=V2_OVER_V1。依次重启它们,不要并行重启。

    in place 23 v2 over v1

  9. 类似于步骤5,验证stateComparison是否显示Matching

  10. 重复步骤6、7、8、9,依次重新启动服务器1、2和3,并使用新的设置dbms.cluster.discovery.version=V2_ONLY in place 123 v2 only

  11. 验证CALL dbms.cluster.showParallelDiscoveryState()现在是否显示正在运行的V2_ONLY。请注意,stateComparisonN/A,因为您不再有v1来比较状态。

新服务器滚动

新服务器滚动需要三个正在运行的服务器和三个新的服务器。

  1. 使用设置dbms.cluster.discovery.version=V1_OVER_V2启动三个新的服务器。

    v1 over v2

    新服务器应该具有在准备部分中详细说明的更新设置。发现地址应包含新成员和先前成员的地址。

    例如,对于使用列表解析器的用户,新服务器的设置应包含

    dbms.cluster.discovery.resolver_type=LIST
    dbms.cluster.discovery.version=V1_OVER_V2
    
    dbms.cluster.discovery.endpoints=server01.example.com:5000,server02.example.com:5000,server03.example.com:5000,server04.example.com:5000,server05.example.com:5000,server06.example.com:5000
    dbms.cluster.discovery.v2.endpoints=server01.example.com:6000,server02.example.com:6000,server03.example.com:6000,server04.example.com:6000,server05.example.com:6000,server06.example.com:6000
  2. 使用bolt://连接到服务器4、5、6的系统数据库,并运行以下过程。例如,可以使用./cypher-shell -a bolt://localhost:7685 -d system来完成此操作。

    CALL dbms.cluster.showParallelDiscoveryState();

    预期结果应显示v2ServerCount为3。stateComparison在此阶段预计不会匹配,因为原始服务器对V2发现服务不可见。

    预期结果
     +---------------------------------------------------------------------------------------------------------+
     | mode         | stateComparison                                          | v1ServerCount | v2ServerCount |
     +---------------------------------------------------------------------------------------------------------+
     | "V1_OVER_V2" | "States are not matching after PT55M36.693S: (score:29)" | "6"           | "3"           |
     +---------------------------------------------------------------------------------------------------------+
  3. 释放、删除并关闭服务器1、2、3。

  4. 启动服务器7、8、9,这次使用设置dbms.cluster.discovery.version=V2_OVER_V1

    v2 over v1

    设置中的发现地址应包含新成员和先前成员的地址。

    例如,对于使用列表解析器的用户,新服务器的设置应包含

    dbms.cluster.discovery.resolver_type=LIST
    dbms.cluster.discovery.version=V2_OVER_V1
    
    dbms.cluster.discovery.endpoints=server04.example.com:5000,server05.example.com:5000,server06.example.com:5000,server07.example.com:5000,server08.example.com:5000,server09.example.com:5000
    dbms.cluster.discovery.v2.endpoints=server04.example.com:6000,server05.example.com:6000,server06.example.com:6000,server07.example.com:6000,server08.example.com:6000,server09.example.com:6000
  5. 使用bolt://连接到服务器7、8、9的系统数据库并运行以下过程

    CALL dbms.cluster.showParallelDiscoveryState();

    输出应在stateComparison列中显示Matching。如果它们没有显示,请等待并重试,直到匹配。

    预期结果
    +----------------------------------------------------------------+
    | mode         | stateComparison | v1ServerCount | v2ServerCount |
    +----------------------------------------------------------------+
    | "V2_OVER_V1" | "Matching"      | "6"           | "6"           |
    +----------------------------------------------------------------+
  6. 释放、删除并关闭服务器4、5和6。

  7. 启动服务器10、11、12,这次使用设置dbms.cluster.discovery.version=V2_ONLY

    v2 only

    设置中的发现地址应包含新成员和先前成员的地址。请注意,只需要v2设置。

    例如,对于使用列表解析器的用户,新服务器的设置应包含

    dbms.cluster.discovery.resolver_type=LIST
    dbms.cluster.discovery.version=V2_ONLY
    
    dbms.cluster.discovery.v2.endpoints=server07.example.com:6000,server08.example.com:6000,server09.example.com:6000,server10.example.com:6000,server11.example.com:6000,server12.example.com:6000
  8. 释放、删除并关闭服务器7、8、9。

  9. 最后,使用bolt://连接到服务器10、11、12的系统数据库,并运行以下过程以检查发现服务的版本

    CALL dbms.cluster.showParallelDiscoveryState();
    预期结果
    +-------------------------------------------------------------+
    | mode      | stateComparison | v1ServerCount | v2ServerCount |
    +-------------------------------------------------------------+
    | "V2_ONLY" | "N/A"           | "N/A"         | "3"           |
    +-------------------------------------------------------------+

使用过程

  1. 对于所有服务器,请确保新设置已更新到准备部分中详述的配置。

    例如,对于使用列表解析器的用户,所有服务器的设置应包括

    dbms.cluster.discovery.resolver_type=LIST
    
    dbms.cluster.discovery.endpoints=server01.example.com:5000,server02.example.com:5000,server03.example.com:5000
    dbms.cluster.discovery.v2.endpoints=server01.example.com:6000,server02.example.com:6000,server03.example.com:6000
  2. 在Cypher Shell中,使用bolt://连接到服务器01的system数据库。通过bolt://连接非常重要,因为否则过程可能会被路由并在非目标服务器上执行。

    ./cypher-shell -a bolt://localhost:7681 -d system
  3. 运行过程

    CALL dbms.cluster.showParallelDiscoveryState();

    输出指示模式V1_ONLY,即只有v1在此服务器上运行。

    预期结果
    +-------------------------------------------------------------+
    | mode      | stateComparison | v1ServerCount | v2ServerCount |
    +-------------------------------------------------------------+
    | "V1_ONLY" | "N/A"           | "3"           | "N/A"         |
    +-------------------------------------------------------------+
  4. 运行以下过程以在后台打开v2,但保持v1在前台运行

    CALL dbms.cluster.switchDiscoveryServiceVersion("V1_OVER_V2");
  5. 再次检查状态

    CALL dbms.cluster.showParallelDiscoveryState();

    现在此服务器的返回模式必须为V1_OVER_V2,并且stateComparison必须显示状态尚未匹配。

    预期结果
    +-------------------------------------------------------------------------------------------------------+
    | mode         | stateComparison                                        | v1ServerCount | v2ServerCount |
    +-------------------------------------------------------------------------------------------------------+
    | "V1_OVER_V2" | "States are not matching after PT1M9.518S: (score:18)" | "3"           | "1"           |
    +-------------------------------------------------------------------------------------------------------+

    分数是衡量状态差异程度的指标。serverCounts分别显示v1和v2发现服务可以找到多少个服务器。当状态匹配时,分数为0。当某些成员仅运行一个发现服务(v1或v2),而其他成员运行两个服务时,分数将永久保持较高。这无需担心。

  6. 为了实现此收敛,在不同的终端中,通过bolt://连接到服务器02和服务器03,并在两个服务器上重复步骤3和4。

  7. 再次检查所有服务器上的状态。它应该显示状态为Matching。两个serverCounts都应为3。

    预期结果
    +----------------------------------------------------------------+
    | mode         | stateComparison | v1ServerCount | v2ServerCount |
    +----------------------------------------------------------------+
    | "V1_OVER_V2" | "Matching"      | "3"           | "3"           |
    +----------------------------------------------------------------+
  8. 在所有三个服务器上,运行

    CALL dbms.cluster.switchDiscoveryServiceVersion("V2_OVER_V1");

    此时,v2是运行集群的服务,v1在后台运行。

    预期结果
    +----------------------------------------------------------------+
    | mode         | stateComparison | v1ServerCount | v2ServerCount |
    +----------------------------------------------------------------+
    | "V2_OVER_V1" | "Matching"      | "3"           | "3"           |
    +----------------------------------------------------------------+
  9. 最后,通过在所有三个服务器上运行以下过程来关闭v1

    CALL dbms.cluster.switchDiscoveryServiceVersion("V2_ONLY");
  10. 验证CALL dbms.cluster.showParallelDiscoveryState();现在是否显示正在运行的V2_ONLY。请注意,stateComparisonN/A,因为您不再有v1来比较状态。

    预期结果
    +-------------------------------------------------------------+
    | mode      | stateComparison | v1ServerCount | v2ServerCount |
    +-------------------------------------------------------------+
    | "V2_ONLY" | "N/A"           | "N/A"         | "3"           |
    +-------------------------------------------------------------+
    重要

    请记住更新所有服务器的neo4j.conf文件。使用过程进行切换不会将任何内容持久化到磁盘。因此,当服务器重新启动时,它会立即重新启动,并且仅运行v1。因此,请确保dbms.cluster.discovery.version=V2_ONLY,并且dbms.cluster.discovery.v2.endpointsdbms.kubernetes.discovery.v2.service_port_name根据需要设置,以便服务器在下次重新启动时以v2运行。

监控进度和指标

从当前发现服务v1迁移到新版本v2时,可以使用过程CALL dbms.cluster.showParallelDiscoveryState()监控进度。此过程显示服务器上发现服务的当前状态以及v1和v2发现服务状态之间的差异分数。差异分数是衡量状态差异程度的指标。过程报告的差异分数并不总是保持为0。以下是一些需要考虑的场景

  • 在集群的情况下,当某些成员仅运行一个发现服务(v1或v2),而其他成员运行两个服务时,分数将永久保持较高。这无需担心。

  • 当集群中发生更改(例如数据库/服务器的启动/停止或领导者切换)时,差异分数将暂时大于0。它应该很快再次达到0。

  • 如果差异分数在较长时间内大于0,则实际差异将打印在debug.log中。

您还可以使用以下指标来监控发现服务