前言


在复杂的分布式系统中,存在着各种性能指标,比如系统请求数,请求响应时间等等。这些指标在一定程度上可以反映出系统运行的快慢程度。但是这里我们如何做到更加准确的判断,而不是说只要出现异常指标,就认定系统有问题,显然这是不合理的。今天,笔者来为大家讲述基于滑动窗口的性能比较算法。如何收集,利用历史数据,来进行当前性能指标的比较。

基于滑动窗口的数据采集


当我们说系统出现“变慢”现象的时候,这个其实是和“过去的时间”作对比,所以我们感觉到它有点慢。同样的,我们要想更加科学地比较这其中的性能差异,就需要用到历史数据。

对于历史数据而言,因为时间是连续的,所以我们要对时间做分片,也就是说,是一段,一段的。这一段的周期可以是10分钟,或半小时等等。在这里,我们用更加专业的词语描述,就叫窗口。每个窗口对应一定的时间区间,随着时间向前滑动。对于每个窗口内,都会有对应时间区间内的性能统计指标数据,比如说我们有该窗口内的总请求数,以及总耗时,这个时候我们可以求出这个窗口的平均响应时间。

那么有了这些窗口数据,我们如何去使用这些数据呢?一个重要的原则是保证数据指标的平滑性。简单地说,我们不能简单暴力地直接使用上个窗口的数据,然后规定出一个规则,比如超出上个指标多少多少倍以上,当前系统就认定为“慢”的。

一种更平滑的做法是,在当前窗口即将结束时,获取到上个窗口的数据,乘上衰减因子,再叠加当前窗口的即时数据,然后把这2个数据的和作为新的“上个窗口”的指标数据。等这个时间窗口过去了,这个衡量阈值就是刚刚过去的窗口的指标平均值。

图示过程如下:
基于滑动窗口的性能指标衡量算法-LMLPHP

通过以上步骤算出的上个窗口的性能数据,就可以拿来与当前数据进行比较,如果数值超过前面的阈值数据,就表明,系统变得异常了。

基于滑动窗口的衰减算法


下面是基于滑动窗口的衰减算法(以系统响应时间为衡量指标),大家可以对照上面笔者阐述的过程。

  /**
   * 在当前窗口的尾声阶段,做窗口的滑动
   * @param enableDecay
   */
  void updateAverageResponseTime(boolean enableDecay) {
    for (int i = 0; i < numLevels; i++) {
      double averageResponseTime = 0;
      // 获取当前窗口的指标数据,算出平均响应时间
      long totalResponseTime = responseTimeTotalInCurrWindow.get(i);
      long responseTimeCount = responseTimeCountInCurrWindow.get(i);
      if (responseTimeCount > 0) {
        averageResponseTime = (double) totalResponseTime / responseTimeCount;
      }
      // 获取上个窗口的数据
      final double lastAvg = responseTimeAvgInLastWindow.get(i);
      if (lastAvg > PRECISION || averageResponseTime > PRECISION) {
        if (enableDecay) {
          // 算出新的值,当前平均时间+上个窗口的衰减值得到
          final double decayed = decayFactor * lastAvg + averageResponseTime;
          // 新的值作为上个窗口的新的数据值
          responseTimeAvgInLastWindow.set(i, decayed);
        } else {
          // 不考虑衰减的情况
          responseTimeAvgInLastWindow.set(i, averageResponseTime);
        }
      } else {
        responseTimeAvgInLastWindow.set(i, 0);
      }
      responseTimeCountInLastWindow.set(i, responseTimeCount);
      if (LOG.isDebugEnabled()) {
        LOG.debug("updateAverageResponseTime queue: {} Average: {} Count: {}",
            i, averageResponseTime, responseTimeCount);
      }

      // 重置当前窗口数据,准备下个窗口的数据统计
      responseTimeTotalInCurrWindow.set(i, 0);
      responseTimeCountInCurrWindow.set(i, 0);
    }
  }
10-06 16:34