HDFS NN refreshNodes操作的可用性和效率的改进

本文分析了HDFS中NN refreshNodes操作存在的DNS解析延迟问题,导致的集群节点丢失和数据块丢失。提出了引入重试机制和增量refreshNodes命令的改进方案,以提高NN的可用性和执行效率。重试机制通过收集并重试解析失败的主机,增量refreshNodes则允许指定主机进行刷新,减少全量解析的开销。这些改进能有效应对大规模集群的挑战,并提供更好的稳定性。
摘要由CSDN通过智能技术生成

前言


我们知道在HDFS里面有,存着一类白名单和黑名单的列表来控制其下允许进行注册的DN节点。这样可以防止一些外部恶意节点注册到我们的NN上来。在HDFS的概念里,这个黑白名单叫做include file和exclude file。在一般情况下,exclude file的使用范围会更管一些,因为DN的decommission下线需要将待下线机器加到此exclude file中,然后再手动执行dfsadmin的refreshNodes命令进行刷新即可。至于include file白名单,它的管理其实比较复杂。在默认情况下,include file是为空的,意味着默认所有的注册的上来的节点都是被允许的。但是这样会有严重的安全隐患,所以在注重Security环境的集群内,需要管理员每次进行include file的更新来确保当前服务的DN都是有效的。Include file/execlude file的更新需要触发NN的refreshNodes操作来生效。因此这里会存在大量refreshNodes操作的发生。最近笔者在生产环境中就遇到了NN refreshNodes的一些性能问题,本文将讲述围绕refreshNodes的性能问题以及其改进点设计实现。

NN refreshNodes的可用性以及效率问题


笔者在最近工作中遇到了由于refreshNodes失败导致的集群出现大量missing block的情况,鉴于大量missing block造成了比较大的impact。笔者于是开始对此问题进行了深入的分析。

我们从结果开始往前分析,missing block的出现是因为NN上掉了大量节点。NN掉了大量节点的时间段,发现某一台挂掉的DN log里显示了很多下面的Disallow异常:

2021-05-23 07:10:07,609 WARN org.apache.hadoop.hdfs.server.datanode.DataNode: RemoteException in offerService
org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.hdfs.server.protocol.DisallowedDatanodeException): Datanode denied communication with namenode because the host is not in the include-list: xx.xx.xx.xx:50010
        at org.apache.hadoop.hdfs.server.blockmanagement.DatanodeManager.handleHeartbeat(DatanodeManager.java:1499)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.handleHeartbeat(FSNamesystem.java:4978)
        at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.sendHeartbeat(NameNodeRpcServer.java:1539)
        at org.apache.hadoop.hdfs.protocolPB.DatanodeProtocolServerSideTranslatorPB.sendHeartbeat(DatanodeProtocolServerSideTranslatorPB.java:118)
        at org.apache.hadoop.hdfs.protocol.proto.DatanodeProtocolProtos$DatanodeProtocolService$2.callBlockingMethod(DatanodeProtocolProtos.java:31228)

看到这个的第一反应,是这个节点不在NN的inlcude host列表里面了,然后才发生的上述拒绝错误。再往上进行分析,又发现了很多诸如“fail to resolve”这类的错误,基本断定是NN解析include host时的dns解析问题。

2021-05-23 07:09:59,030 INFO org.apache.hadoop.util.HostsFileReader: Adding a node "xx.xx.xx.xx" to the list of included hosts from /xxx/hadoop/hosts
2021-05-23 07:09:59,932 WARN org.apache.hadoop.hdfs.server.blockmanagement.HostFileManager: Failed to resolve address `xx.xx.xx.xx` in `/xxx/hadoop/hosts`. Ignoring in the included list.

而触发上面resolve行为的是dfsadmin refreshNodes命令,不过这个refreshNodes执行的耗时相当长。

2021-05-23 07:01:39,001 WARN org.apache.hadoop.ipc.Server: Slow RPC : refreshNodes took 5269MILLISECONDS to process from client Call#0 Retry#0 org.apache.hadoop.hdfs.protocol.ClientProtocol.refreshNodes from xx.xx.xx.xx

后来经过分析发现,refershNodes失败的原因是短时段内大量的dns查询导致了dns解析的延迟,这个延时在NN的代码调用层面来看到的情况,就是解析失败的情况。

在这个问题里,有很多可以值得讨论的点:

  • 第一,为什么这里会有这么大量的dns查询请求?后来发现是因为集群DN数总量在持续不断变多,导致每次refreshNodes的cost越来越高。因为NN的refreshNodes行为会重刷所有的DN节点,相当于这是一个全量解析host地址的行为。

  • 第二,为什么这里没有NN解析重试行为?refreshNodes执行失败了,最好能够有一个重试机制,否则失败造成的掉节点,missing block对用户影响太大。

  • 第三,现有refreshNodes的效率不高,它是一种全量解析的方式,是否我们能够支持增量式地refresh node,对于大部分分状态不变的节点,NN完全没必要再对其做dns的重新解析。在增量式 refesh node的情况里,admin管理员能够指定特定的host进行node的refresh,从而使得refreshNode的操作变得非常轻量级。

针对上面的第二,第三点,我们内部对refreshNode逻辑进行了调整和改进,使其在可用性以及执行效率上得到一个号的提升。

NN refreshNodes的改造升级


针对上节部分提出的几个关键点,我们对此进行了如下两部分的改进。

refreshNodes重试机制的引入


在原有逻辑里,NN refreshNodes会触发重新读取所有的include/exclude文件中的host信息。在这个过程里,这些文件里可能会包含有上千甚至上万个节点的host信息,每次读取到一个新的host后,NN尝试会进行对其host的ip地址的解析,相关代码如下:

  private static HostSet readFile(String type, String filename)
          throws IOException {
   
    HostSet res = new HostSet();
    if (!filename.isEmpty()) {
   
      HashSet<String> entrySet = new HashSet<String>();
      HostsFileReader.readFileToSet(type, filename, entrySet);
      for (String str : entrySet) {
   
        // 构造InetSocketAddress地址时,会进行地址解析
        InetSocketAddress addr = parseEntry(type, filename, str);
        if (addr != null) {
   
          res.add(addr);
        }
      }
    }
    return res;
  }

  @VisibleForTesting
  static InetSocketAddress parseEntry(String type, String fn, String line) {
   
    try {
   
      URI uri = new URI("dummy", line, null, null, null);
      int port = uri.getPort() == -1 ? 0 : uri.getPort();
      InetSocketAddress addr = new InetSocketAddress(uri.getHost(), port);
      if (addr.isUnresolved()) {
   
        LOG.warn(String.format("Failed to resolve address `%s` in `%s`. " +
                "Ignoring in the %s list.", line, fn, type));
        return null;
      }
      return addr;
    } catch (URISyntaxException e) {
   
      LOG.warn(String.format("Failed to parse `%s` in `%s`. " + "Ignoring in " +
              "the %s list.", line, fn, type));
    }
    return null
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值