自适应微服务治理背后的算法

简介: 自适应微服务治理背后的算法

go-zero 群里经常有同学问:

服务监控是通过什么算法实现的?

滑动窗口是怎么工作的?能否讲讲这块的原理?

熔断算法是怎么设计的?为啥没有半开半闭状态呢?

本篇文章,来分析一下 go-zero 中指标统计背后的实现算法和逻辑。

指标怎么统计

这个我们直接看 breaker

type googleBreaker struct {
  k     float64
  stat  *collection.RollingWindow
  proba *mathx.Proba
}

go-zero 中默认的 breaker 是以 google SRE 做为实现蓝本。

breaker 在拦截请求过程中,会记录当前这类请求的成功/失败率:

func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error {
  ...
  // 执行实际请求函数
  err := req()
  if acceptable(err) {
    // 实际执行:b.stat.Add(1)
    // 也就是说:内部指标统计成功+1
    b.markSuccess()
  } else {
    // 原理同上
    b.markFailure()
  }
  return err
}

所以其实底层说白了就是:请求执行完毕,会根据错误发生次数,内部的统计数据结构会相应地加上统计值(可正可负)。同时随着时间迁移,统计值也需要随时间进化。

简单来说:时间序列内存数据库【也没数据库这么猛,就是一个存储,只是一个内存版的】

下面就来说说这个时间序列用什么数据结构组织的。

滑动窗口

我们来看看 rollingwindow 定义数据结构:

type RollingWindow struct {
    lock          sync.RWMutex
    size          int
    win           *window
    interval      time.Duration
    offset        int
    ignoreCurrent bool
    lastTime      time.Duration
  }

上述结构定义中,window 就存储指标记录属性。

在一个 rollingwindow 包含若干个桶(这个看开发者自己定义):

每一个桶存储了:Sum 成功总数,Count 请求总数。所以在最后 breaker 做计算的时候,会将 Sum 累计加和为 accepts,Count 累计加和为 total,从而可以统计出当前的错误率。

滑动是怎么发生的

首先对于 breaker 它是需要统计单位时间(比如1s)内的请求状态,对应到上面的 bucket 我们只需要将单位时间的指标数据记录在这个 bucket 即可。

那我们怎么保证在时间前进过程中,指定的 Bucket 存储的就是单位时间内的数据?

第一个想到的方式:后台开一个定时器,每隔单位时间就创建一个 bucket ,然后当请求时当前的时间戳落在 bucket 中,记录当前的请求状态。周期性创建桶会存在临界条件,数据来了,桶还没建好的矛盾。

第二个方式是:惰性创建 bucket,当遇到一个数据再去检查并创建 bucket。这样就有时有桶有时没桶,而且会大量创建 bucket,我们是否可以复用呢?

go-zero 的方式是:rollingwindow 直接预先创建,请求的当前时间通过一个算法确定到bucket ,并记录请求状态。

下面看看 breaker 调用 b.stat.Add(1) 的过程:

func (rw *RollingWindow) Add(v float64) {
  rw.lock.Lock()
  defer rw.lock.Unlock()
  // 滑动的动作发生在此
  rw.updateOffset()
  rw.win.add(rw.offset, v)
}
func (rw *RollingWindow) updateOffset() {
  span := rw.span()
  if span <= 0 {
    return
  }
  offset := rw.offset
  // 重置过期的 bucket
  for i := 0; i < span; i++ {
    rw.win.resetBucket((offset + i + 1) % rw.size)
  }
  rw.offset = (offset + span) % rw.size
  now := timex.Now()
  // 更新时间
  rw.lastTime = now - (now-rw.lastTime)%rw.interval
}
func (w *window) add(offset int, v float64) {
  // 往执行的 bucket 加入指定的指标
  w.buckets[offset%w.size].add(v)
}

上图就是在 Add(delta) 过程中发生的 bucket 发生的窗口变化。解释一下:

  1. updateOffset 就是做 bucket 更新,以及确定当前时间落在哪个 bucket 上【超过桶个数直接返回桶个数】,将其之前的 bucket 重置
  • 确定当前时间相对于 bucket interval的跨度【超过桶个数直接返回桶个数】
  • 将跨度内的 bucket 都清空数据。reset
  • 更新 offset,也是即将要写入数据的 bucket
  • 更新执行时间 lastTime,也给下一次移动做一个标志
  1. 由上一次更新的 offset,向对应的 bucket 写入数据

而在这个过程中,如何确定确定 bucket 过期点,以及更新时间。滑动窗口最重要的就是时间更新,下面用图来解释这个过程:

而  bucket 过期点,说白就是 lastTime 即上一个更新时间跨越了几个 buckettimex.Since(rw.lastTime) / rw.interval


这样,在 Add() 的过程中,通过 lastTimenowTime 的标注,通过不断重置来实现窗口滑动,新的数据不断补上,从而实现窗口计算。

总结

本文分析了 go-zero 框架中的指标统计的基础封装、滑动窗口的实现 rollingWindow。当然,除此之外,store/redis 也存在指标统计,这个里面的就不需要滑动窗口计数了,因为本身只需要计算命中率,命中则对 hit +1,不命中则对 miss +1 即可,分指标计数,最后统计一下就知道命中率。

滑动窗口适用于流控中对指标进行计算,同时也可以做到控流。

关于 go-zero 更多的设计和实现文章,可以关注『微服务实践』公众号。

项目地址

https://github.com/tal-tech/go-zero

相关文章
|
4月前
|
算法 计算机视觉
使用积分图的自适应二值化算法
使用积分图的自适应二值化算法
|
11月前
|
监控 算法 安全
基于伽马变换自适应修正的全景首尾融合算法
基于伽马变换自适应修正的全景首尾融合算法
|
编解码 算法
基于自适应运动补偿的双向运动估计算法matlab仿真
基于自适应运动补偿的双向运动估计算法matlab仿真
|
22天前
|
存储 负载均衡 监控
自适应负载均衡算法原理和实现
自适应负载均衡算法原理和实现
|
2月前
|
算法 vr&ar
基于自适应波束成形算法的matlab性能仿真,对比SG和RLS两种方法
```markdown - MATLAB2022a中比较SG与RLS自适应波束成形算法。核心程序实现阵列信号处理,强化期望信号,抑制干扰。RLS以其高效计算权重,而SG则以简单和低计算复杂度著称。[12345] [6666666666] [777777] ```
|
3月前
|
算法 调度 决策智能
基于自适应遗传算法的车间调度matlab仿真,可以任意调整工件数和机器数,输出甘特图
这是一个使用MATLAB2022a实现的自适应遗传算法解决车间调度问题的程序,能调整工件数和机器数,输出甘特图和适应度收敛曲线。程序通过编码初始化、适应度函数、遗传操作(选择、交叉、变异)及自适应机制进行优化,目标如最小化完工时间。算法在迭代过程中动态调整参数,以提升搜索效率和全局优化。
|
3月前
|
算法
基于ADM自适应增量调制算法的matlab性能仿真
该文主要探讨基于MATLAB的ADM自适应增量调制算法仿真,对比ADM与DM算法。通过图表展示调制与解调效果,核心程序包括输入输出比较及SNR分析。ADM算法根据信号斜率动态调整量化步长,以适应信号变化。在MATLAB中实现ADM涉及定义输入信号、初始化参数、执行算法逻辑及性能评估。
|
4月前
|
机器学习/深度学习 自然语言处理 算法
深度解析深度学习中的优化算法:从梯度下降到自适应方法
【4月更文挑战第28天】 在深度学习模型训练的复杂数学迷宫中,优化算法是寻找最优权重配置的关键导航者。本文将深入探讨几种主流的优化策略,揭示它们如何引导模型收敛至损失函数的最小值。我们将比较经典的批量梯度下降(BGD)、随机梯度下降(SGD)以及动量概念的引入,进一步探索AdaGrad、RMSProp和Adam等自适应学习率方法的原理与实际应用。通过剖析这些算法的理论基础和性能表现,我们旨在为读者提供一个关于选择合适优化器的参考视角。
|
4月前
|
算法 数据可视化
R语言使用Metropolis-Hastings采样算法自适应贝叶斯估计与可视化
R语言使用Metropolis-Hastings采样算法自适应贝叶斯估计与可视化
|
机器学习/深度学习 传感器 算法
【图像去噪】基于自适应小波阈值算法实现图像去噪附matlab源码
【图像去噪】基于自适应小波阈值算法实现图像去噪附matlab源码