自适应微服务治理背后的算法(代码片段)

kevinwan kevinwan     2022-12-10     799

关键词:

前言

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,也给下一次移动做一个标志
  2. 由上一次更新的 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

欢迎使用 go-zero 并 star 支持我们!

微信交流群

关注『微服务实践』公众号并点击 交流群 获取社区群二维码。

go-zero 系列文章见『微服务实践』公众号

使用istio治理微服务入门(代码片段)

 近两年微服务架构流行,主流互联网厂商内部都已经微服务化,初创企业虽然技术积淀不行,但也通过各种开源工具拥抱微服务。再加上容器技术赋能,Kubernetes又添了一把火,微服务架构已然成为当前软件架构设计的首选... 查看详情

springcloud:服务治理——springcloudeureka(代码片段)

  服务治理是微服务架构中最为核心和基础的模块,主要作用是实现各个微服务实例的自动化注册和发现。SpringCloudEureka是SpringCloudNetflix微服务套件中的一部分,基于NetflixEureKa做了二次封装,主要负责微服务架构中的服务治理... 查看详情

微服务相关原理与治理(代码片段)

微服务架构没有公认的技术标准和规范或者草案,但业界已经有一些很有影响力的开源微服务架构框架提供了微服务的关键思路,例如Dubbo和SpringCloud。目前微服务实现方式主要有两种Dubbo和SpringCloud:一、Dubbo:(https://www.cnblogs.co... 查看详情

软件工程应用与实践——微服务治理(代码片段)

2021SC@SDUSC文章目录一、概述二、核心代码2.1微服务搭建2.2注册和配置中心2.2.1服务注册中心2.2.2配置中心2.3服务间通信三、总结一、概述在老年健康管理系统的后端代码中,使用了当前火热的微服务技术。微服务架构改变... 查看详情

常见的3种微服务治理方式

1、应用程序中包含微服务治理逻辑在微服务架构中,服务间不再是在朴素的进程内通信,取而代之的是通过轻量级的网络协议进行通信。那么,如何找到服务提供方?如何超时重试?当存在多个服务提供方时如何实现负载均衡... 查看详情

微服务治理的手段

节点管理第一种:注册中心主动剔除有问题的服务提供者根据心跳机制决定哪些服务提供者有效,由注册中心主动撤除有问题的服务提供者的ip,更新服务消费者端的服务提供者ip列表第二种:服务消费者维护服务提供者列表服... 查看详情

微服务治理之dubbo基础认识(代码片段)

...上的。和传统的单体架构相比,分布式多了一个远程服务之间的通信,不管是soa还是微服务,他们本质上都是对于业务服务 查看详情

springcloud系列之服务治理eureka(代码片段)

什么是EurekaNetflixEureka是一款由Netflix开源的基于REST服务的注册中心,用于提供服务发现功能。SpringCloudEureka是SpringCloudNetflix微服务套件的一部分,基于NetflixEureka进行了二次封装,主要负责完成微服务架构中的服务治理功能。Spring... 查看详情

001服务治理(代码片段)

一.概述  在微服务之中,最为重要的就是服务治理.这个概念包括两个方面的内容  [1]服务的注册  [2]服务的发现  本次我们从开始使用Eureka来完成服务中心的搭建. 二.创建eureka服务中心[1]依赖的服务  <dependencies... 查看详情

有货微服务治理实践(代码片段)

服务依赖???????在分布式架构中,服务间的依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当会员服务不可用时,订单服务请求线程被阻塞,当有大批量请求调用会员服务时,最终可能导致整个会... 查看详情

[图像处理]14.分割算法比较otsu算法+自适应阈值算法+分水岭(代码片段)

...友的博客-CSDN博客_drawcontoursR329-opencv阈值分割算法——自适应阈值_ThirdImpact的博客-CSDN博客_opencv自适应阈值分割分水岭算法的python实现及解析_进不去的博客-CSDN博客_python分水岭 查看详情

配置nacos注册中心(代码片段)

Nacos内容一、什么是服务治理二、常见注册中心三、Nacos简介四、Nacos实战入门(掌握)1.搭建Nacos环境2.将商品服务注册到Nacos3.将订单服务注册到Nacos总结内容微服务配置请看我的上篇博客:微服务环境搭建一、什么是服务治理... 查看详情

matlab自适应滤波算法lms小白通俗易懂版(代码片段)

Matlab自适应滤波算法在学习自适应算法的过程中,入门阶段,学习了LMS算法、NLMS算法,并用Matlab对算法进行了复现。LMS最小均方(LMS)是一种搜索算法,它通过对目标函数进行适当修改,以便简化梯度向量的计... 查看详情

微服务架构&servicemesh(代码片段)

微服务架构,服务治理怎么做,采用什么技术?微服务架构实施服务治理的方法和技术:容器化:Docker+Kubernetes服务注册发现:ZooKeeper、Consul、Etcd负载均衡:HAProxy、Nginx、AmazonELBAPI网关:Kong、ShenYu,中大厂一般自研监控和日志:E... 查看详情

服务治理springcloudeureka——高可用注册中心(代码片段)

承接上一篇《第3章服务治理SpringCloudEureka(一)——单片搭建》2.5高可用注册中心2.5.1简介在微服务架构这样的分布式环境中,我们需要充分考虑发生故障的情况,所以在生产环境中必须对各个组件进行高可用部... 查看详情

服务治理springcloudeureka——高可用注册中心(代码片段)

承接上一篇《第3章服务治理SpringCloudEureka(一)——单片搭建》2.5高可用注册中心2.5.1简介在微服务架构这样的分布式环境中,我们需要充分考虑发生故障的情况,所以在生产环境中必须对各个组件进行高可用部... 查看详情

asp.netcore搭载envoy实现微服务的可视化监控(代码片段)

...起原理说明数据源与可视化自定义图表文本小结在构建微服务架构的过程中,我们会接触到服务划分、服务编写以及服务治理这一系列问题。其中,服务治理是工作量最密集的一个环节,无论是服务发现、配置中心、故障转移、... 查看详情

asp.netcore搭载envoy实现微服务的可视化监控(代码片段)

...起原理说明数据源与可视化自定义图表文本小结在构建微服务架构的过程中,我们会接触到服务划分、服务编写以及服务治理这一系列问题。其中,服务治理是工作量最密集的一个环节,无论是服务发现、配置中心、故障转移、... 查看详情