当前位置: 首页 > news >正文

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

目录

前言

一、什么是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 int64mutex    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 float64front = g.sreSlide.Front()//当前滑动窗口下的请求和成功请求量的统计for front != nil {t := front.Value.(*slideVal)r += t.reqaccept += t.acceptfront = 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 》以及 腾讯微服务治理相关文章为我提供了深入的思考以及总结

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

相关文章:

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

目录 前言 一、什么是Google SRE熔断器 二、Google SRE 熔断器的工作流程&#xff1a; 三、客户端熔断器 (google SRE 熔断器) golang GRPC 实现 四、客户端熔断器 (google SRE 熔断器) golang GRPC单元测试 大家可以关注个人博客&#xff1a;xingxing – Web Developer …...

Springboot 校验工具类

校验工具类 这个实现逻辑很简单,就是调用string的正则表达式 我这里的代码要导入糊涂工具包 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.17</version> </dependency>import…...

编程笔记 html5cssjs 069 JavaScrip Undefined数据类型

编程笔记 html5&css&js 069 JavaScrip Undefined数据类型 一、undefined数据类型二、类型运算小结 在JavaScript中&#xff0c;undefined 是一种基本数据类型&#xff0c;它表示一个变量已经声明但未定义&#xff08;即没有赋值&#xff09;或者一个对象属性不存在。 一…...

MySQL 处理JSON字符串

目录 前言 JSON值的部分更新 创建JSON值 JSON 值的规范化、合并和自动包装 合并JSON值 搜索和修改JSON值 JSON路径 JSON值的比较和排序 JSON值的聚合 前言 现在很多数据会以json格式存储&#xff0c;如果你还在用like查询json字符串&#xff0c;那你就OUT了&#xff0…...

python爬虫-多线程-数据库——WB用户

数据库database的包&#xff1a; Python操作Mysql数据库-CSDN博客 效果&#xff1a; 控制台输出&#xff1a; 数据库记录&#xff1a; 全部代码&#xff1a; import json import os import threading import tracebackimport requests import urllib.request from utils im…...

有向图查询所有环,非递归

图&#xff1a; 有向图查询所有环&#xff0c;非递归&#xff1a; import java.util.*;public class CycleTest {private final int V; // 顶点数private final List<List<Integer>> adjList; // 邻接表public CycleTest(int vertices) {this.V vertices;this.…...

SpringBoot 使用WebSocket功能

实现步骤&#xff1a; 1.导入WebSocket坐标。 在pom.xml中增加依赖项&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency>2.编写WebSocket配…...

HTML5的新特性

目录 一&#xff0c;新增语义化标签 二&#xff0c;新增的多媒体标签 三&#xff0c;新增input表单 四&#xff0c;新增的表单属性 一&#xff0c;新增语义化标签 二&#xff0c;新增的多媒体标签 1&#xff0c;音频&#xff1a;<audio>.。。用MP3 <audio src…...

Filter过滤器学习使用

验证token 对外API过滤器 public class APIFilter implements Filter {private static Logger logger LogManager.getLogger(APIFilter.class);private ICachedTokenService tokenService;public APIFilter(ICachedTokenService tokenService) {super();this.tokenService …...

关于修改数据库服务器时间导致达梦数据库集群裂开

故障原因&#xff1a; 因为每天数据库服务器时间都不一致&#xff0c;想要给数据库服务器配置个NTP服务器。结果导致达梦数据库裂库。后面查看了达梦系统管理员手册了解了达梦集群的机制&#xff0c;知道数据库服务器时间需要先关闭数据库服务之后才可以修改数据库服务器时间。…...

自定义包的设计与实现

这是一个 CPacket 类&#xff0c;用于解析包含固定格式的数据。该类的成员变量包括固定包头 sHead、包长度 nLength、控制命令 sCmd、包数据 strData 和和校验 sSum。 构造函数&#xff1a; CPacket()&#xff1a;默认构造函数&#xff0c;初始化成员变量。 CPacket(const B…...

时机成熟了

时机成熟了。 有一个老乡群&#xff0c;一到年底就各种人找车、车找人的消息。这些消息如果能直接爬取到一个小的网页里面去&#xff0c;则可以极大地便利大家做检索。如何把非结构化的内容转成结构化的 json&#xff0c;在以前是一个难题&#xff0c;但是有了 ChatGPT&#x…...

Linux 驱动开发基础知识——总线设备驱动模型(八)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…...

SpringBoot+BCrypt算法加密

BCrypt是一种密码哈希函数&#xff0c;BCrypt算法使用“盐”来加密密码&#xff0c;这是一种随机生成的字符串&#xff0c;可以在密码加密过程中使用&#xff0c;以确保每次加密结果都不同。盐的使用增强了安全性&#xff0c;因为攻击者需要花费更多的时间来破解密码。 下图为…...

更新至2023年,2002-2023年3月中国国债发行数据

更新至2023年&#xff0c;2002-2023年3月中国国债发行数据 1、时间&#xff1a;2002-2023年3月 2、指标&#xff1a;序号、代码、发行日期、发行总额(亿元)、期限(年)、发行价格、票面利率(发行参考利率)(%)、票面利率说明、息票品种、附息利率类型、付息频率、起息日期、付息…...

2024最新版TypeScript安装使用指南

2024最新版TypeScript安装使用指南 Installation and Development Guide to the Latest TypeScript in 2024 By JacksonML 1. 什么是TypeScript? TypeScript is JavaScript with syntax for types. – typescriptlang.org TypeScript 是 JavaScript 的一个超集&#xff0c;…...

国外知名的农业机器人公司

从高科技温室到云播种&#xff0c;农业机器人如何帮助农民填补劳动力短缺以及超市货架的短缺。 概要 “高科技农业”并不矛盾。当代农业经营更像是硅谷&#xff0c;而不是美国哥特式&#xff0c;拥有控制灌溉的应用程序、驾驶拖拉机的 GPS 系统和监控牲畜的带有 RFID 芯片的耳…...

【EI会议征稿中|ACM出版】#先投稿,先送审#第三届网络安全、人工智能与数字经济国际学术会议(CSAIDE 2024)​

#先投稿&#xff0c;先送审#ACM出版#第三届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2024&#xff09; 2024 3rd International Conference on Cyber Security, Artificial Intelligence and Digital Economy 2024年3月8日-10日 | 中国济南 会议官网&…...

【笔试常见易错选择题01】else、表达式、二维数组、%m.ns、%m.nf、常量指针和指针常量、宏定义、传参、数组越界、位段

1. 下列main()函数执行后的结果为&#xff08;&#xff09; int func(){ int i, j, k 0; for(i 0, j -1;j 0;i, j){ k; } return k; } int main(){cout << (func());return 0; }A. -1 B. 0 C. 1 D. 2 判断为赋值语句&#xff0c;j等于0 0为假不进循环 选B. 2. 下面程…...

Unity中常见的单词

前言 unity中常见的单词学习积累 一.常用的基础词。 new:新建; as:像。。一样; null:对象空值; void:函数返回空值; switch:开关; abstract:抽象的; event:事件&#xff1b; return:返回; class:类; …...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

Java中HashMap底层原理深度解析:从数据结构到红黑树优化

一、HashMap概述与核心特性 HashMap作为Java集合框架中最常用的数据结构之一&#xff0c;是基于哈希表的Map接口非同步实现。它允许使用null键和null值&#xff08;但只能有一个null键&#xff09;&#xff0c;并且不保证映射顺序的恒久不变。与Hashtable相比&#xff0c;Hash…...