使用 Hadoop 服务注册表

Hadoop 服务注册表可用于多种方式:

  1. 使用与 YARN 应用程序生命周期匹配的条目来注册动态部署的 YARN 应用程序。服务记录可以设置为在 YARN 应用程序、应用程序尝试或单个容器完成后删除。
  2. 查找静态或动态应用程序以及与它们通信的机制。这些机制包括:HTTP(S) URL、Zookeeper 路径、主机名和端口,甚至 Hadoop 文件系统中指向配置数据的路径。
  3. 在安全集群上,验证服务绑定是否已由特定用户或系统帐户发布。这可以通过简单地查看条目放置的路径来完成。
  4. 注册静态应用程序。这些应用程序将保留在注册表中,直至删除。可以根据需要更新它们。

注册表用户既可以是条目(服务记录)的发布者,也可以是通过其服务记录找到的其他服务的使用者。分布式应用程序的不同部分也可以将其用于不同的目的。例如,YARN 应用程序的应用程序主服务器可以发布绑定,以便其工作器容器使用。然后,在容器中运行的代码可以查找绑定,以便与该管理器通信,即使该管理器在集群的不同节点上重新启动。客户端应用程序可以通过公共 API 查找外部服务端点,以便与 AM 交互。

注册表不能用于:-

注册表应用程序设计模式

注册其公共服务端点的短期 YARN 应用程序主服务器。

  1. 部署 YARN 应用程序。在安全集群中,它会获得 Kerberos 令牌以写入注册表。
  2. 启动时,它会在已知路径下创建服务记录
  3. 此记录可能具有应用程序尝试持久性策略和应用程序尝试的 ID
    yarn:persistence = "application_attempt"
    yarn:id = ${application_attemptId}
    

    这意味着即使创建了新的尝试,该记录也会在应用程序尝试完成后被删除。每个应用程序尝试都必须重新注册端点,而无论如何可能需要找到该服务。

  4. 或者,该记录可能具有“应用程序”的持久性策略
    yarn:persistence = "application_attempt"
    yarn:id = application_attemptId
    

    这意味着即使在应用程序尝试之间,该记录也会持久存在,尽管端点信息已过时。

  5. 客户端应用程序通过路径查找服务。

路径的选择是特定于应用程序的。对于 YARN 应用程序名称保证唯一的服务,我们建议采用以下约定

/users/${username}/applications/${service-class}/${instance-name}

或者,可以在路径中使用应用程序 ID

/users/${username}/applications/${service-class}/${applicationId}

后者使得将 YARN 应用程序列表条目映射到服务记录变得简单。

客户端应用程序可以找到服务

  • 通过枚举服务类的所有实例并根据特定标准选择一个实例。
  • 从提供的服务类和实例名称
  • 如果按应用程序 ID 列出,则从服务类和应用程序 ID。

找到服务记录后,客户端可以枚举external绑定并找到具有所需 API 的条目。

注册其公共服务端点的 YARN 容器

此处,YARN 应用程序中的所有容器都在发布供公开使用的服务端点。

  1. 已部署的容器会传递它们应该在其中注册自己的基本路径。
  2. 长生命周期容器必须传递一个 id:password 对,该对赋予它们在没有用户的 kerberos 凭据的情况下更新这些条目的权限。这允许容器在授予 AM 对注册表路径的写访问权限的用户令牌过期后更新它们的条目。
  3. 容器使用 id:password 对实例化注册表操作实例。
  4. 然后它们在一个由以下内容组成的路径上注册服务记录
    ${base-path} + "/" + RegistryPathUtils.encodeYarnID(containerId)
    

    此记录应该具有容器持久性策略和容器 ID

    yarn:persistence = "container"
    yarn:id = containerId
    

    当容器终止时,该条目将自动删除。

  5. 此容器部署的服务的导出服务端点应该列在服务记录的 external 端点列表中。

  6. 客户端可以通过列出 ${base-path} 下的条目来枚举 YARN 应用程序导出的所有容器。

注册静态集群服务。

通常在集群中固定的服务,但需要发布绑定和配置信息的,可以发布在注册表中。示例:Apache Oozie 服务。部署应用程序可能也发布的集群外部服务。示例:Amazon Dynamo 实例。

这些服务可以注册在属于运行该服务的用户的路径下,例如 /users/oozie/users/hbase。客户端应用程序将使用此路径。虽然这可以验证服务记录的有效性,但它确实依赖于客户端应用程序知道服务部署在哪个用户名上,或使用完整路径进行配置。

另一种方法是将服务部署在 /services 下的静态服务路径下。例如,/services/oozie 可以包含 Oozie 服务的注册。由于此路径的权限限制为预配置的系统帐户,因此在安全集群上此路径上的服务注册的存在确认它是由集群管理工具注册的。

  1. 该服务由某些管理工具或直接由集群操作员部署。
  2. 如果给出了注册表的绑定信息,已部署的应用程序可以在其自己的用户名下注册自身。
  3. 如果应用程序要注册在 /services 下,并且它是由系统用户帐户之一部署的,则它可以直接注册自身。
  4. 如果应用程序没有这样做的权限,则集群管理工具必须代替它注册服务。
  5. 客户端应用程序可以通过解析其众所周知/已配置的路径来找到服务。
  6. 如果服务已停止,管理工具可以删除该条目,或保留该条目但删除其所有服务端点。这是一个提议的约定,表示“服务已知但当前不可访问”。
  7. 当服务重新启动时,其绑定信息可能会更新,或者其整个注册表项会重新创建。

YARN 容器查找其应用程序主控

此处 YARN 容器使用某种心跳机制(定期报告)向其 AM 注册以接收工作,通常通过某种心跳机制。如果 AM 配置为容器比应用程序尝试存活更长时间,则当 AM 失败时,容器将继续运行。这些容器需要绑定到任何重新启动的 AM。它们可能还希望得出结论,如果 AM 没有重新启动,它们最终应该超时并自行终止。此类策略有助于应用程序对网络分区做出反应。

  1. YARN AM 发布其服务端点,例如用于 IPC 通信所需的 FQDN 和套接字端口,或用于 REST 通道所需的 HTTP/HTTPS URL。这些内容在 internal 端点列表中发布,其中 api 字段设置为容器使用的特定 API 的 URL。
  2. YARN 容器使用(某种方式)传递给它们的路径启动服务记录。环境变量或命令行参数是两种可行的机制。共享密钥也应通过这种方式传递:命令行参数在 unix ps 命令中可见。更安全的方法是将共享密钥保存到群集文件系统,然后将路径传递给容器。此类路径的 URI 可能为应用程序的注册内部端点之一。
  3. YARN 容器查找服务注册表以识别通信绑定。
  4. 如果找不到已注册的服务项,容器可能会执行以下操作之一:退出。使用某种(抖动)重试周期旋转,轮询该项,直到该项重新出现。这意味着已找到 AM。
  5. 如果找到服务项,客户端应尝试在其通道上与 AM 通信。共享身份验证详细信息可用于验证客户端与服务器,反之亦然。
  6. 客户端向 AM 报告,直到连接开始无法连接或无法通过身份验证,或者当长时间连接中断且无法重新启动时。
  7. 此时客户端可能会恢复到步骤 (3)。同样,某种带有抖动的退避策略有助于阻止新重新启动的 AM 过载。容器也可能在一段时间后得出结论,即 AM 不会回来并退出。
  8. 我们建议,除了 AM 可能向客户端发出的功能命令之外,还可以向容器发出“终止”命令。这允许系统处理 YARN 节点管理器终止而生成的容器继续运行的特定情况。

YARN 应用程序和容器发布其管理和指标绑定

管理端口和绑定只是要发布的其他端点。这些应发布为内部端点,因为它们不供公开使用。

客户端应用程序通过端点 API 枚举服务

客户端应用程序希望找到实现特定 API 的所有服务,例如 "classpath://org.apache.hbase"

  1. 客户端从注册表中的路径开始
  2. 客户端调用 registryOperations.list(path) 以列出该路径下所有直接节点,获取子节点的相对列表。
  3. 客户端通过对每个子节点调用 stat() 来枚举子记录状态。
  4. 对于所有状态条目,如果条目的 size 大于 ServiceRecordHeader.getLength() 的值,则它可能包含服务记录。
  5. 可以使用 resolve() 操作检索内容。如果成功,则它确实包含服务记录,因此客户端可以枚举 external 端点并找到具有所需 API 的端点。
  6. 应检查每个 RegistryPathStatus 状态条目的 children 字段。如果它 >= 0,则应在该条目的路径上递归执行枚举。
  7. 该操作最终完成,并提供所有条目的列表。
  8. 可以选中其中一个已枚举的端点,并将其用作服务的绑定信息

此算法描述了注册表树的深度优先搜索。当然,还可以进行各种变化,包括广度优先搜索或在找到单个入口点后立即停止搜索。还可以并行搜索不同的子树,这可能会减少搜索时间,但会增加客户端对注册表基础设施的负载。

实用程序类 RegistryUtils 为常见的注册表操作提供了静态实用程序方法,特别是,RegistryUtils.listServiceRecords(registryOperations, path) 执行指定路径的所有直接子记录条目的列出和收集。

客户端应用程序面临“当端点无效时该怎么办”的问题,特别是当服务未运行时,应该怎么做?

一些传输假设中断是暂时的,并且针对原始绑定的旋转重试是正确的策略。这是 Hadoop IPC 客户端的默认策略。

其他传输快速失败,立即通过异常或其他机制报告失败。这对于客户端是直接可见的,但允许客户端重新扫描注册表并重新绑定到应用程序。

最后,一些应用程序从一开始就设计为动态故障转移:它们发布的绑定信息实际上是一个 zookeeper 路径。Apache HBase 和 Apache Accumulo 就是此类示例。注册表用于初始绑定查找,之后客户端本质上具有故障恢复能力。