布景
为了基于网络状况做更详尽的事务策略,需求一套网速检测方案,尽量低成本的评价当时网络状况,所以咱们期望检测数据来自于过往的网络恳求,而不是专门耗费资源去网络恳求来准确评价。
方针核算
一般 RTT 作为网速的首要评价方针,拿到批量的前史恳求 RTT 值后,要怎么去核算得到较为准确的方针 RTT 值呢?
影响 RTT 值的变量首要是:
- 网络状况会随时刻变化;
- 恳求来自不同的服务器,性能有差异,简单受到长尾数据影响;
首先参阅 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;
…
可以看到权重首要来自两个方面:
- 信号权重:与当时信号强度差异越大的 RTT 值参阅价值越低;
- 时刻权重:距离当时时刻越久的 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 秒则权重衰减为开始的一半。延伸思考一下,可以得到两个结论:
- 同等前史 RTT 值量级下,半衰期越小,可信度越高,因为越挨近当时时刻的网络状况;
- 同等半衰期下,前史 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 值之前,前置判别一下“超越某个阈值”的未落地恳求“超越某个份额”,视为弱网状况,达到快速感知网络劣化的效果。