HDFS慢节点监控原理源码分析

背景

HDFS集群规模日益扩大之后,集群中难免会出现一些“慢节点“,主要表现为网络数据传输变慢、磁盘读写变慢。平常这些慢节点很难被发现,只有当业务作业数据读写涉及到这些节点,导致作业运行时间延长,我们才会发现集群读写变慢了,进而去定位具体变慢的节点。

所以慢节点一直是HDFS集群运维中需重点关注的问题,在Hadoop2.9之后,社区支持了从Namenode jmx上查看慢节点的功能。

metrics格式如下,需要注意的是这里最多展示Top5个节点/磁盘:

"SlowPeersReport":[{
   "SlowNode":"node4","ReportingNodes":["node1"]},{
   "SlowNode":"node2","ReportingNodes":["node1","node3"]},{
   "SlowNode":"node1","ReportingNodes":["node2"]}]

"SlowDisksReport":[{
   "SlowDiskID":"dn3:disk1","Latencies":{
   "WRITE":1000.1}},{
   "SlowDiskID":"dn2:disk2","Latencies":{
   "WRITE":1000.1}},{
   "SlowDiskID":"dn1:disk2","Latencies":{
   "READ":1000.3}},{
   "SlowDiskID":"dn1:disk1","Latencies":{
   "METADATA":1000.1,"READ":1000.8}}]

网络慢监控

原理

监控一个DN网络传输变慢的原理是,记录集群中各个DN间的packet数据传输耗时,找出其中的异常值并作为慢节点上报给NN。正常情况下各节点间的传输速率基本一致,不会相差太多,假如出现了A传B耗时异常,A就往NN上报B是慢节点。

为了计算DN往下游传数据的平均耗时,DN内部维护了一个Map<String, LinkedBlockingDeque<SumAndCount>>,Map的key为下游DN的ip,value是一个存放SumAndCount对象的队列,SumAndCount对象用于记录往下游DN传输packet的数量与耗时。

DN在发送心跳的时候会判断是否需要生成SlowPeerReport,并将其作为心跳信息的一部分发送给NN。SlowPeerReport的生成周期由dfs.datanode.outliers.report.interval 参数控制,默认30min。首先从队列中取出所有packet传输耗时求平均值averageLatency,然后根据这些averageLatency,计算出慢节点上报阈值upperLimitLatency。当有节点的averageLatency大于upperLimitLatency,即认为该节点属于一个网络慢节点,且由DN1上报。最后生成对应的SlowPeerReport,通过心跳上报给NN。

慢节点阈值upperLimitLatency的计算逻辑

先算出所有下游DN传输耗时的中位数median,再算出中位数绝对偏差mad:

// MAD_MULTIPLIER = 1.4826
mad = median(|list[i]-median(list)|) * MAD_MULTIPLIER

最终upperLimitLatency为:

// lowThresholdMs = 5ms
upperLimitLatency = max(lowThresholdMs, median * 3, median + mad * 3)

代码详情如下:
org.apache.hadoop.hdfs.server.datanode.metrics.OutlierDetector.java

public Map<String, Double> getOutliers(Map<String, Double> stats) {
   
    // minNumResources=10,节点少于10个不参与计算
    if (stats.size() < minNumResources) {
   
      LOG.debug("Skipping statistical outlier detection as we don't have " +
              "latency data for enough resources. Have {}, need at least {}",
          stats.size(), minNumResources);
      return ImmutableMap.of();
    }
    final List<Double> sorted = new ArrayList<>(stats.values());
    Collections.sort(sorted);
    // 计算中位数median
    final Double median = computeMedian(sorted);
    // 计算中位数绝对偏差值mad
    final Double mad = computeMad(sorted);
    // 计算异常阈值upperLimitLatency
    Double upperLimitLatency = Math.max(
        lowThresholdMs, median * MEDIAN_MULTIPLIER);
    upperLimitLatency = Math.max(
        upperLimitLatency, median + (DEVIATION_MULTIPLIER * mad));

    final Map<String, Double> slowResources = new HashMap<>();

    // 找出大于异常阈值的节点
    for (Map.Entry<String, Double> entry : stats.entrySet()) {
   
      if (entry.getValue() > upperLimitLatency) {
   
        slowResources.put(entry.getKey(), entry.getValue());
      }
    }

    return slowResources;
}

public static Double computeMad(List<Double> sortedValues) {
   
    ...
    // 计算出中位数
    Double median = computeMedian(sortedValues);
    List<Double> deviations = new ArrayList<>(sortedValues);

    // 计算绝对偏差值
    for (int i = 0; i < sortedValues.size(); ++i) {
   
      deviations.set(i, Math.abs(sortedValues.get(i) - median));
    }

    Collections.sort(deviations);
    // MAD_MULTIPLIER = 1.4826
    return computeMedian(deviations) * MAD_MULTIPLIER;
}

public static Double computeMedian(List<Double> sortedValues) {
   
    ...
    Double median = sortedValues.get(sortedValues.size() / 2);
    if (sortedValues.size() % 2 == 0) {
   
      median += sortedValues.get((sortedValues.size() / 2) - 1);
      median /= 2
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值