使用IOStatistics API进行统计收集

@InterfaceAudience.Public
@InterfaceStability.Unstable

IOStatistics API旨在以标准化的方式为各个IO类(例如输入输出流)提供统计信息,应用程序可以查询这些信息

许多与文件系统相关的类已经实现了统计信息收集功能,并提供了私有/不稳定的查询方式,但由于这些实现并不通用,应用程序引用这些值是不安全的。例如:S3AInputStream及其统计API。这被用于内部测试,但不能在下游应用程序(如Apache Hive或Apache HBase)中使用。

IOStatistics API 旨在

  1. 实例特定:而非在类的多个实例间共享,或线程本地。
  2. 足够公开和稳定,可供应用程序使用。
  3. 易于在Java、Scala以及通过libhdfs的C/C++编写的应用程序中使用
  4. hadoop-common JAR中包含基础接口和类。

核心模型

任何类可以实现IOStatisticsSource以提供统计信息。

包装器I/O类如FSDataInputStreamFSDataOutputStream应当实现该接口并将其转发给被包装的类(如果被包装类也实现了该接口)——如果未实现则返回null

IOStatisticsSource 实现中的 getIOStatistics() 方法会返回一个 IOStatistics 实例,该实例枚举了特定实例的统计信息。

IOStatistics 接口导出五种统计类型:

分类 类型 描述
counter long a counter which may increase in value; SHOULD BE >= 0
gauge long an arbitrary value which can down as well as up; SHOULD BE >= 0
minimum long an minimum value; MAY BE negative
maximum long a maximum value; MAY BE negative
meanStatistic MeanStatistic an arithmetic mean and sample size; mean MAY BE negative

其中四个是简单的long值,包含它们可能的变化方式以及如何被聚合的变体。

统计值聚合

对于不同的统计类别,aggregate(x, y)的结果是

分类 聚合
counter max(0, x) + max(0, y)
gauge max(0, x) + max(0, y)
minimum min(x, y)
maximum max(x, y)
meanStatistic calculation of the mean of x and y )

MeanStatistic

org.apache.hadoop.fs.statistics

该软件包包含供应用程序使用的公共统计API。

MeanStatistic 是一个由 (mean, samples) 组成的元组,用于支持聚合操作。

一个样本为 0MeanStatistic 被视为空统计量。

所有MeanStatistic实例中,当sample = 0时被视为相等,与mean值无关。

计算均值的算法:

if x.samples = 0:
    y
else if y.samples = 0 :
    x
else:
    samples' = x.samples + y.samples
    mean' = (x.mean * x.samples) + (y.mean * y.samples) / samples'
    (samples', mean')

隐含的意思是,如果两个样本都为空,那么聚合值也为空。

public final class MeanStatistic implements Serializable, Cloneable {
  /**
   * Arithmetic mean.
   */
  private double mean;

  /**
   * Number of samples used to calculate
   * the mean.
   */
  private long samples;

  /**
   * Get the mean value.
   * @return the mean
   */
  public double getMean() {
    return mean;
  }

  /**
   * Get the sample count.
   * @return the sample count; 0 means empty
   */
  public long getSamples() {
    return samples;
  }

  /**
   * Is a statistic empty?
   * @return true if the sample count is 0
   */
  public boolean isEmpty() {
    return samples == 0;
  }
   /**
   * Add another mean statistic to create a new statistic.
   * When adding two statistics, if either is empty then
   * a copy of the non-empty statistic is returned.
   * If both are empty then a new empty statistic is returned.
   *
   * @param other other value
   * @return the aggregate mean
   */
  public MeanStatistic add(final MeanStatistic other) {
    /* Implementation elided. */
  }
  @Override
  public int hashCode() {
    return Objects.hash(mean, samples);
  }

  @Override
  public boolean equals(final Object o) {
    if (this == o) { return true; }
    if (o == null || getClass() != o.getClass()) { return false; }
    MeanStatistic that = (MeanStatistic) o;
    if (this.isEmpty()) {
      return that.isEmpty();
    }
    return Double.compare(that.mean, mean) == 0 &&
        samples == that.samples;
  }

  @Override
  public MeanStatistic clone() {
    return new MeanStatistic(this);
  }

  public MeanStatistic copy() {
    return new MeanStatistic(this);
  }

}

org.apache.hadoop.fs.statistics.IOStatisticsSource

/**
 * A source of IO statistics.
 * These statistics MUST be instance specific, not thread local.
 */
@InterfaceStability.Unstable
public interface IOStatisticsSource {

  /**
   * Return a statistics instance.
   * It is not a requirement that the same instance is returned every time.
   * {@link IOStatisticsSource}.
   * If the object implementing this is Closeable, this method
   * may return null if invoked on a closed object, even if
   * it returns a valid instance when called earlier.
   * @return an IOStatistics instance or null
   */
  IOStatistics getIOStatistics();
}

这是一个对象实例必须实现的接口,如果它们是IOStatistics信息的来源。

不变式

getIOStatistics() 的结果必须是以下之一

  • null
  • 一个不可变的IOStatistics,其中每个条目映射都是空映射。
  • 一个IOStatistics实例,其统计信息必须对于实现IOStatisticsSource接口的该类的该实例是唯一的。

非正式地说:如果返回的统计信息映射不为空,则所有统计信息必须从当前实例收集,而不能像某些FileSystem统计信息那样从其他实例收集。

getIOStatistics()的返回结果(若非空)可能在每次调用时都是不同的实例。

org.apache.hadoop.fs.statistics.IOStatistics

这些是由实现IOStatisticsSource接口的对象提供的每个实例的统计信息。

@InterfaceAudience.Public
@InterfaceStability.Unstable
public interface IOStatistics {

  /**
   * Map of counters.
   * @return the current map of counters.
   */
  Map<String, Long> counters();

  /**
   * Map of gauges.
   * @return the current map of gauges.
   */
  Map<String, Long> gauges();

  /**
   * Map of minumums.
   * @return the current map of minumums.
   */
  Map<String, Long> minumums();

  /**
   * Map of maximums.
   * @return the current map of maximums.
   */
  Map<String, Long> maximums();

  /**
   * Map of meanStatistics.
   * @return the current map of MeanStatistic statistics.
   */
  Map<String, MeanStatistic> meanStatistics();

}

统计命名

统计信息的命名策略旨在具备可读性、可共享性,并尽可能在IOStatisticSource实现中保持一致性。

  • 键名中的字符必须匹配正则表达式 [a-z|0-9|_],但首字符除外,首字符必须在 [a-z] 范围内。因此,有效统计名称的完整正则表达式为:

    [a-z][a-z|0-9|_]+
    
  • 在可能的情况下,统计指标名称应采用通用定义的标准命名。

    org.apache.hadoop.fs.statistics.StreamStatisticNames
    org.apache.hadoop.fs.statistics.StoreStatisticNames
    

注1:这些内容在不断演进中;为了让客户端能安全地通过名称引用其统计信息,建议将这些字符串复制到应用程序中。(例如,针对编译时使用hadoop 3.4.2但需链接hadoop 3.4.1的应用程序,应复制这些字符串)。

注意2:这些类中定义的键在后续Hadoop版本中不得被移除。

  • 一个通用的统计名称不得用于报告任何其他统计信息,并且必须使用预定义的测量单位。

  • 一个统计名称不应在另一个映射中重复使用。这有助于诊断记录的统计数据。

统计地图

对于返回的每个统计映射:

  • 不支持添加/删除条目的操作:返回的映射可能被统计源修改。

  • 映射表可以为空。

  • 每个映射键代表一个测量统计量。

  • 映射中的键集合应保持不变,且不得删除键。

  • 统计信息应该是动态的:每次查找条目都应返回最新值。

  • 这些值可能会在调用 Map.values()Map.entries() 时发生变化

  • 更新可能发生在返回的迭代器的iterable()调用中,也可能发生在实际的iterable.next()操作中。也就是说:无法保证评估何时发生。

  • 返回的Map.Entry实例在重复调用getValue()时必须返回相同的值(即一旦获取该条目,它就是不可变的)。

  • 统计查询应快速且非阻塞,即使在长时间操作期间调用,也应优先快速返回而非获取最新值。

  • 统计信息可能存在延迟;特别是对于通过单独操作收集的统计数据(例如由文件系统实例提供的流IO统计信息)。

  • 表示时间的统计信息应使用毫秒作为单位。

  • 表示时间并使用不同单位的统计信息必须记录所使用的单位。

线程模型

  1. IOStatistics的实例可以在多个线程间共享;

  2. 对提供的统计映射的读取访问必须是线程安全的。

  3. 从maps返回的迭代器禁止在线程间共享。

  4. 收集的统计信息必须包含所有线程为被监控对象执行的所有操作。

  5. 报告的统计数据绝不能仅限于当前活跃线程。

这与FileSystem.Statistics的行为不同,后者会收集并报告每个线程的统计信息。

该机制支持收集共享同一FS实例的不同工作线程的有限读写统计信息,但由于收集是线程本地的,它总是会低估其他线程代表工作线程执行的IO操作。

统计快照

可以通过调用IOStatisticsSupport.snapshotIOStatistics()获取当前统计值的快照

  public static <X extends IOStatistics & Serializable> X
      snapshotIOStatistics(IOStatistics statistics)

该快照可通过Java序列化和Jackson库实现与JSON之间的相互转换。

辅助类

org.apache.hadoop.fs.statistics.IOStatisticsSupport

这提供了用于处理IOStatistics源和实例的辅助方法。

请查阅其操作的javadocs文档。

org.apache.hadoop.fs.statistics.IOStatisticsLogging

支持高效记录IOStatistics/IOStatisticsSource实例。

这些旨在辅助日志记录,仅在日志级别需要时枚举IOStatistics实例的状态。

LOG.info("IOStatistics after upload: {}", demandStringify(iostats));

// or even better, as it results in only a single object creations
Object latest = demandStringify(iostats);
LOG.info("IOStatistics : {}", latest);
/* do some work. */
LOG.info("IOStatistics : {}", latest);

org.apache.hadoop.fs.statistics.impl

这里包含实现类,用于支持向应用程序提供统计信息。

应用程序不得使用这些内容。如果需要此包中的功能,可以通过Hadoop开发渠道提出公开实现的请求。

这些可能被Hadoop FileSystemAbstractFileSystem及相关类的实现所使用,但这些实现不在hadoop源码树中。实现者必须注意,此代码的实现不稳定,可能在Hadoop的小版本更新中发生变化。