小米服务治理——客户端熔断器(Google SRE客户端熔断器)

news2025/2/5 0:56:28

目录

前言

一、什么是Google SRE熔断器

二、Google SRE 熔断器的工作流程:

三、客户端熔断器  (google SRE 熔断器) golang GRPC 实现

四、客户端熔断器 (google SRE 熔断器) golang GRPC单元测试


大家可以关注个人博客:xingxing – Web Developer from Somewhere   有关后端问题探讨

前言

当某个用户超过资源配额时,后端任务应该迅速拒绝该请求,返回一个“用户配额不足”类型的错误,该回复应该比真正处理该请求所消耗的资源少得多。然而,这种逻辑其实不适用于所有请求。例如,拒绝一个执行简单内存查询的请求可能跟实际执行该请求消耗内存差不多(因为这里主要的消耗是在应用层协议解析中,结果的产生部分很简单)。

就算在某些情况下,拒绝请求可以节省大量资源,发送这些拒绝回复仍然会消耗一定数量的资源。如果拒绝回复的数量也很多,这些资源消耗可能也十分可观。在这种情况下,有可能该后端在忙着不停地发送拒绝回复时一样会进人过载状态。

那么客户端截流机制就可以解决这个问题,也就是Google SRE

一、什么是Google SRE熔断器

是否可以做到在熔断器 Open 状态下(但是后端未 Shutdown)仍然可以放行少部分流量呢?Google SRE 熔断器提供了一种算法:客户端自适应限流(client-side throttling)。

解决的办法就是客户端自行限制请求速度,限制生成请求的数量,超过这个数量的请求直接在本地回复失败,而不会真正发送到服务端。

该算法统计的指标依赖如下两种,每个客户端记录过去两分钟内的以下信息(一般代码中以滑动窗口实现)。

  • requests:客户端请求总量

    • 注:The number of requests attempted by the application layer(at the client, on top of the adaptive throttling system)

  • accepts:成功的请求总量 - 被 accepted 的量

    • 注:The number of requests accepted by the backend

二、Google SRE 熔断器的工作流程:

  • 在通常情况下(无错误发生时) requests == accepts ;

  • 当后端出现异常情况时,accepts 的数量会逐渐小于 requests;

  • 当后端持续异常时,客户端可以继续发送请求直到 requests = K∗accepts,一旦超过这个值,客户端就启动自适应限流机制,新产生的请求在本地会被概率(以下称为p)丢弃;

  • 当客户端主动丢弃请求时,requests 值会一直增大,在某个时间点会超过 K∗accepts,使 p 计算出来的值大于 0,此时客户端会以此概率对请求做主动丢弃;

  • 当后端逐渐恢复时,accepts 增加,(同时 requests 值也会增加,但是由于 K 的关系,K*accepts的放大倍数更快),使得 (requests − K×accepts) / (requests + 1) 变为负数,从而 p == 0,客户端自适应限流结束。

客户端请求被拒绝的概率(Client request rejection probability,以下简称为 p)

p 基于如下公式计算(其中 K 为倍率 - multiplier,常用的值为 2)。

  • 当 requests − K∗accepts <= 0 时,p == 0,客户端不会主动丢弃请求;

  • 反之, p 会随着 accepts 值的变小而增加,即成功接受的请求数越少,本地丢弃请求的概率就越高。

客户端可以发送请求直到 requests = K∗accepts, 一旦超过限制, 按照 p 进行截流。

对于后端而言,调整 K 值可以使得自适应限流算法适配不同的服务场景

  • 降低 K 值会使自适应限流算法更加激进(允许客户端在算法启动时拒绝更多本地请求);

  • 增加 K 值会使自适应限流算法变得保守一些(允许服务端在算法启动时尝试接收更多的请求,与上面相反)。

熔断本质上是一种快速失败策略。旨在通过及时中断失败或超时的操作,防止资源过度消耗和请求堆积,从而避免服务因小问题而引发的雪崩效应。

三、客户端熔断器  (google SRE 熔断器) golang GRPC 实现

我们要考虑几个问题,第一个问题用哪种算法去做统计呢,我感觉用滑动窗口去统计比较合适,因为滑动窗口是统计一个周期内的请求以及响应.用户的响应也是随着周期性的变化的,这样就可以周期性的统计。

第二个问题是此算法在什么时候执行呢,就拿GRPC 来说,当然是拦截器呢,在发送后端服务请求的时候前就要去看是否要熔断,避免错误的请求发送到后端。

type googleSlide struct {
	sreSlide *list.List
	//滑动窗口大小
	interval int64
	mutex    sync.Mutex
	//客户端成功请求量的系数
	k float64
}

type slideVal struct {
	//客户端请求时间
	time int64
	//客户端的总请求量
	req float64
	//客户端成功请求量
	accept float64
}

type SlideValOptions func(val *slideVal)

func NewSlideval(options ...SlideValOptions) *slideVal {
	t := &slideVal{
		time: time.Now().UnixNano(),
	}
	for _, option := range options {
		option(t)
	}
	return t
}

func WithReqOption(req float64) SlideValOptions {
	return func(val *slideVal) {
		val.req = req
	}
}

func WithAcceptReqOption(accept float64) SlideValOptions {
	return func(val *slideVal) {
		val.accept = accept
	}
}

func NewGoogleSlide(interval time.Duration, k float64) *googleSlide {
	return &googleSlide{
		sreSlide: list.New(),
		interval: interval.Nanoseconds(),
		k:        k,
	}
}

func (g *googleSlide) Sre() grpc.UnaryClientInterceptor {
	return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
		g.mutex.Lock()
		now := time.Now().UnixNano()
		front := g.sreSlide.Front()
		//调整滑动窗口
		for front != nil && front.Value.(*slideVal).time+g.interval < now {
			g.sreSlide.Remove(front)
			front = g.sreSlide.Front()
		}
		var r, accept float64
		front = g.sreSlide.Front()
		//当前滑动窗口下的请求和成功请求量的统计
		for front != nil {
			t := front.Value.(*slideVal)
			r += t.req
			accept += t.accept
			front = front.Next()
		}
		//客户端请求被拒绝的概率((requests − K×accepts) / (requests + 1))
		tail := (r - g.k*accept) / (r + 1)
		if tail > 0 {
			g.mutex.Unlock()
			return errors.New("request is fail")
		}
		g.sreSlide.PushBack(NewSlideval(WithReqOption(1)))
		err := invoker(ctx, method, req, req, cc, opts...)
		if err == nil {
			g.sreSlide.PushBack(NewSlideval(WithAcceptReqOption(1)))
		}
		g.mutex.Unlock()
		return err
	}
}

四、客户端熔断器 (google SRE 熔断器) golang GRPC单元测试

模拟客户端请求,handler 是正常的请求,handler1是返回有问题的请求,2 客户端熔断器的参数. 此值越小越激进,对服务端错误的容忍越小.

测试用例我说明下:

network is fail 是模拟服务端返回的错误,是要调用服务端,此时并不会限制,随着服务恢复,整个请求逐渐正常。

 request is fail 是熔断器返回的,不会调用服务端的,直接返回错误。这就是熔断器的魅力所在。

func TestGoogleSre(t *testing.T) {
   slide := NewGoogleSlide(5*time.Second, 2)
   builder := slide.Sre()
   // 模拟服务端正常的请求
   handler := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
      return nil
   }
   //模拟服务端出问题
   handler1 := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
      return errors.New("network is fail")
   }
   err := builder(context.Background(), "/test/a", &gen.GetByIdReq{}, &gen.GetByIDResp{}, nil, handler)
   assert.NoError(t, err)
   err = builder(context.Background(), "/test/a", &gen.GetByIdReq{}, &gen.GetByIDResp{}, nil, handler1)
   assert.Equal(t, err, errors.New("network is fail"))
   err = builder(context.Background(), "/test/a", &gen.GetByIdReq{}, &gen.GetByIDResp{}, nil, handler1)
   assert.Equal(t, err, errors.New("network is fail"))
   err = builder(context.Background(), "/test/a", &gen.GetByIdReq{}, &gen.GetByIDResp{}, nil, handler1)
   assert.Equal(t, err, errors.New("request is fail"))
   err = builder(context.Background(), "/test/a", &gen.GetByIdReq{}, &gen.GetByIDResp{}, nil, handler1)
   assert.Equal(t, err, errors.New("request is fail"))
   time.Sleep(5 * time.Second)
   err = builder(context.Background(), "/test/a", &gen.GetByIdReq{}, &gen.GetByIDResp{}, nil, handler)
   assert.NoError(t, err)
}

首先感谢《google SRE 》以及 腾讯微服务治理相关文章为我提供了深入的思考以及总结

代码或者测试用例如果有异议,请和我留言,大家一起探讨

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1427082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【动态规划】【C++算法】1340. 跳跃游戏 V

作者推荐 【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数 本文涉及知识点 动态规划汇总 LeetCode1340跳跃游戏 V 给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到&#xff1a; i x &#xff0c;其中 i x < arr.length 且 0 < x…

提及闭包函数,不得不说【python装饰器】

什么是装饰器 1. python装饰器是用于拓展原来函数功能的一种函数&#xff0c;目的是在不改变原函数的情况下&#xff0c;给函数增加功能。 2. 装饰器是通过闭包实现&#xff0c;所以讲装饰器首先得知道什么是闭包。 前言 什么是闭包 一、什么是闭包 1. 一个定义在函数内部的函…

[职场] 个人简历教育背景怎么填 #其他#笔记#媒体

个人简历教育背景怎么填 一个人的受教育程度很大部分决定了此人的专业素质&#xff0c;简历中的教育背景是HR衡量求职者的标准&#xff0c;因此教育背景部分在简历中比较靠前。 怎么写教育背景&#xff1f; 1、时间 首先&#xff0c;最重要的是你要注明好时间&#xff0c;时间都…

idea搭建spring5.3.x源码环境

1.写在前面的话 碰到了不少想阅读或者学习spring源码的同学&#xff0c;但是第一步搭建这个源码阅读环境就能难倒了一大批人。下面我就以spring5.3.x这个源码分支&#xff0c;来具体演示一下搭建过程。 2. 下载源码 下载源码这一步&#xff0c;说实话&#xff0c;由于某些原…

STM32控制DS18B20温度传感器获取温度

时间记录&#xff1a;2024/1/28 一、DS18B20温度传感器介绍 &#xff08;1&#xff09;测温范围-55℃~125℃&#xff0c;在-10℃到85℃范围内误差为0.4 &#xff08;2&#xff09;返回的温度数据为16位二进制数据 &#xff08;3&#xff09;STM32和DS18B20通信使用单总线协议…

算法模板 3.二分查找

二分查找 789. 数的范围 - AcWing题库 check函数&#xff08;lr&#xff08;这里要不要1&#xff09;&#xff09;>> 1 &#xff0c;要根据具体情况&#xff0c;如果是r mid的话lr就不用1&#xff0c;l mid的话就要1 #include <bits/stdc.h> using namespace s…

Android Retrofit添加header参数

前言 有些接口需要单独设置header参数 作为标记 如何灵活 或者统一设置请求header呢 注解Headers 顾名思义 Headers是指多个header 放在对象里 统一管理 //获取xxx Headers({"Content-Type: application/json","User-Agent: test"}) POST("xxxx&q…

小程序:类型三级分类

一、效果图片 二、代码 <template><view class"customPosition"><!-- header --><navBar :border"false" :hasBack"true" :title"titleName"></navBar><!-- 查询 --><view class"search…

JavaWeb之打包失败解决方案

情况描述&#xff1a; ---打包不成功 这时点击Maven中的蓝色小闪电&#xff1a; 小闪电的意思&#xff1a;是否跳过测试。点击选中就可以跳过&#xff0c;打包的时候就可以跳过测试代码。 再次打包后即可成功&#xff1a; 查看具体打包后的jar包&#xff1a; 打包后&#x…

openGaussdb5.0单点企业版部署_Centos7_x86

本文档环境&#xff1a;CentOS7.9 x86_64 4G1C40G python2.7.5 交互式初始化环境方式 介绍 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合华为在数据库领域多年的经验&#xff0c;结合企业级场景需求&#xff0c;持续…

Ubuntu远程连接登录信息解读(ubuntu登录信息、远程登录信息)

文章目录 1. Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-100-generic aarch64)2. 三个链接是官方提供的文档、管理工具和技术支持3. System information as of Thu 01 Feb 2024 03:30:45 PM HKT4. System load: 1.16&#xff1a;系统负载指数5. Processes: 1096系统正在运…

虹科技术|一文详解IO-Link Wireless技术如何影响工业无线自动化

导读&#xff1a;在工业无线自动化的飞速发展进程中&#xff0c;IO-Link Wireless技术成为了一项具有颠覆性的创新。它将IO-Link协议与无线连接完美结合&#xff0c;解决了传统通信技术在工业应用中的痛点。本文将深入解析IO-Link Wireless技术的原理、应用领域、优势以及实际案…

vue3集成video.js

就长这个样子&#xff0c;该有的功能都有了 首先安装 npm install video.js 然后创建一个vue文件 <template><div class"full" style"padding: 10px;overflow: auto;"><video :id"videoId" class"video-js"><…

LeetCode.1686. 石子游戏 VI

题目 题目链接 分析 本题采取贪心的策略 我们先假设只有两个石头a,b&#xff0c; 对于 Alice 价值分别为 a1,a2&#xff0c; 对于 Bob 价值而言价值分别是 b1,b2 第一种方案是 Alice取第一个&#xff0c;Bob 取第二个&#xff0c;Alice与Bob的价值差是 c1 a1 - b1&#xf…

Django模型(八)

一、修改数据 先获取对象,通过对象属性更新数据,再保存 (更新单一数据)通过QuerySet的update函数更新数据 (更新多条数据) #单条记录修改 save c = Cook.objects.get(pk=1) c.name = 安妮 c.save()# 更新多个值 update Cook.objects.filter(sect=粤菜).update(level=5)1.1、…

游戏APP开发:从创意到实现的全过程

随着智能手机的普及和移动互联网的发展&#xff0c;游戏APP市场日益繁荣。游戏APP开发已经成为一个热门行业&#xff0c;吸引了众多开发者和创业者的关注。本文将介绍游戏APP开发的全过程&#xff0c;包括创意、策划、设计、开发、测试和发布等环节。 一、创意 游戏APP开发的…

测试access和trunk口的区别(华为)

思科设备参考&#xff1a;测试access和trunk口的区别&#xff08;思科&#xff09; 一&#xff0c;实验目的 实现同一 Vlan 内的主机互通&#xff0c;不同 Vlan 间的主机隔离。 二&#xff0c;配置前测试 PC1分别ping PC2、PC3、PC4都能通&#xff0c;因为四台PC默认同处于v…

2024年美赛F题Problem F Reducing Illegal Wildlife Trade减少非法野生动物贸易的完整思路代码分享

非法的野生动物贸易会对我们的环境产生负面影响&#xff0c;并威胁到全球的生物多样性。据估计&#xff0c;它每年涉及高达265亿美元&#xff0c;被认为是全球第四大非法交易。[1]你将开发一个由数据驱动的5年项目&#xff0c;旨在显著减少非法野生动物贸易。你的目标是说服一个…

2024 美国大学生数学建模竞赛 美赛(D题)五大湖水资源调配问题 国际大学生数学建模竞赛| 建模秘籍文章代码思路大全

铛铛&#xff01;小秘籍来咯&#xff01; 小秘籍希望大家都能轻松建模呀&#xff0c;华数杯也会持续给大家放送思路滴~ 抓紧小秘籍&#xff0c;我们出发吧~ 完整内容可以在文章末尾领取&#xff01; 问题一&#xff1a;建立一个包括五大湖和连接从苏必利尔湖到大西洋的河流的…

软件工程(最简式总结)

目录 第一章:概述 1.软件危机的表现原因 2.常见的软件开发方法包括&#xff1a; 3.软件工程基本原则 4.软件工程三要素 5.设计模式的分类 6.针对变换型数据流设计步骤 7.针对事务型数据流设计步骤 第二章&#xff1a;软件过程 1.软件生命周期 2.软件过程模型 &…