布景

为了基于网络状况做更详尽的事务策略,需求一套网速检测方案,尽量低成本的评价当时网络状况,所以咱们期望检测数据来自于过往的网络恳求,而不是专门耗费资源去网络恳求来准确评价。

方针核算

一般 RTT 作为网速的首要评价方针,拿到批量的前史恳求 RTT 值后,要怎么去核算得到较为准确的方针 RTT 值呢?

影响 RTT 值的变量首要是:

  1. 网络状况会随时刻变化;
  2. 恳求来自不同的服务器,性能有差异,简单受到长尾数据影响;

首先参阅 Chrome 的 nqe 源码:chromium.googlesource.com/chromium/sr…

权重规划

查阅相关源码后,发现前史恳求的 RTT 值会相关一个权重,用于终究的核算,找到核算 RTT 权重的核心逻辑:

void ObservationBuffer::ComputeWeightedObservations(
    const base::TimeTicks& begin_timestamp,
    int32_t current_signal_strength,
    std::vector<WeightedObservation>* weighted_observations,
    double* total_weight) const {
…
    base::TimeDelta time_since_sample_taken = now - observation.timestamp();
    double time_weight =
        pow(weight_multiplier_per_second_, time_since_sample_taken.InSeconds());
    double signal_strength_weight = 1.0;
    if (current_signal_strength >= 0 && observation.signal_strength() >= 0) {
      int32_t signal_strength_weight_diff =
          std::abs(current_signal_strength - observation.signal_strength());
      signal_strength_weight =
          pow(weight_multiplier_per_signal_level_, signal_strength_weight_diff);
    }
    double weight = time_weight * signal_strength_weight;

可以看到权重首要来自两个方面:

  1. 信号权重:与当时信号强度差异越大的 RTT 值参阅价值越低;
  2. 时刻权重:距离当时时刻越久的 RTT 值参阅价值越低;

这个处理能减小网络状况随时刻变化带来的影响。

半衰期规划

在核算两个权重的时候都是用pow(衰减因子, diff)核算的,那这个“衰减因子”怎么得到的呢,以时刻衰减因子为例:

double GetWeightMultiplierPerSecond(
    const std::map<std::string, std::string>& params) {
  // Default value of the half life (in seconds) for computing time weighted
  // percentiles. Every half life, the weight of all observations reduces by
  // half. Lowering the half life would reduce the weight of older values
  // faster.
  int half_life_seconds = 60;
  int32_t variations_value = 0;
  auto it = params.find("HalfLifeSeconds");
  if (it != params.end() && base::StringToInt(it->second, &variations_value) &&
      variations_value >= 1) {
    half_life_seconds = variations_value;
  }
  DCHECK_GT(half_life_seconds, 0);
  return pow(0.5, 1.0 / half_life_seconds);
}

其实便是规划一个半衰期,核算得到“每秒衰减因子”,比方这儿便是一个 RTT 值和当时时刻差异 60 秒则权重衰减为开始的一半。延伸思考一下,可以得到两个结论:

  1. 同等前史 RTT 值量级下,半衰期越小,可信度越高,因为越挨近当时时刻的网络状况;
  2. 同等半衰期下,前史 RTT 值量级越大,可信度越高,因为会抹平更多的服务器性能差异;

所以更进一步的话,半衰期可以依据前史 RTT 值的量级来进行调理,找到它们之间的平衡点。

加权算法规划

拿到权值后怎么核算呢,咱们最简单想到的是加权平均值算法,但它同样会受长尾数据的影响。

比方当某个 RTT 值比正常值大几十倍且权重稍高时,加权平均值也会很大,更优的做法是获取加权中值,这也是 nqe 的做法,伪代码为:

//按 RTT 值从小到大排序
samples.sort()
//方针权重是总权重的一半
desiredWeight = 0.5 * totalWeight
//找到方针权重对应的 RTT 值
cumulativeWeight = 0
for sample in samples
  cumulativeWeight += sample.weight
  If (cumulativeWeight >= desiredWeight) 
    return sample.RTT

进一步优化

经过前史网络恳求样本数据核算加权中值,依据核算后的 RTT 值区间确认网速状况供事务使用,比方 Bad / Good,这种策略能掩盖大部分状况,但有两个特殊状况需求优化。

无网络访问场景

当用户一段时刻没有访问网络缺少样本数据时,引入主动探测策略,建议恳求实时核算 RTT 值。

网络状况快速劣化场景

若在某一个时刻网络忽然变得很差,很多恳求堆积在行列中,因为咱们 RTT 值依赖于网络恳求落地,这时核算的方针 RTT 值具有滞后性。

为了解决这个问题,可以记载一个“未落地恳求”的行列,每次核算 RTT 值之前,前置判别一下“超越某个阈值”的未落地恳求“超越某个份额”,视为弱网状况,达到快速感知网络劣化的效果。