分布式系统架构:限流设计模式

1.为什么要限流?

任何一个系统的运算、存储、网络资源都不是无限的,当系统资源不足以支撑外部超过预期的突发流量时,就应该要有取舍,建立面对超额流量自我保护的机制,而这个机制就是微服务中常说的“限流”

2.四种限流设计模式

说到限流,大家直接的想法就是Sentinel,但是Sentinel限流的原理可能很多人没去深入理解,或者限流到底是怎么做的?具体如何进行限流,业界内也有一些常见设计模式。

2.1流量计数器模式

流量计数器是一种最简单的限流方式,通过记录固定时间窗口内的请求次数来判断是否达到限流阈值。如果请求次数超过限制值,则拒绝后续请求。

实现方式:

  • 将时间划分为固定的时间窗口(如 1 秒、1 分钟)。

  • 每个窗口维护一个计数器,记录当前时间窗口内的请求次数。

  • 如果计数器值超过限流阈值,直接拒绝请求;否则增加计数器。

固定窗口边界问题:

  • 在窗口边界的两端,可能存在短时间内超量请求的“临界问题

比如场景设定:一秒内的TPS大于80时,就限流。

存在问题:即使每一秒的统计流量都没有超过 80 TPS,也不能说明系统没有遇到过大于 80 TPS 的流量压力。比如说系统在连续2秒内都收到60TPS的请求,但是请求发生的时间分别在第1秒的后0.5秒,以及第2秒的前0.5秒。这样系统实际曾在1秒内发生超过80 TPS的请求。

  • 即使连续若干秒统计流量超过阈值,也不能说明流量压力一定超过系统承受能力

假设 10 秒的时间片段中,前 3 秒的 TPS 平均值到了 100,而后 7 秒的平均值是 30 左右,此时系统是否能够处理完这些请求而不产生超时失败?答案是可以的

存在缺陷:造成上面2个问题得原因是流量计数器模式是对时间点进行离散的统计

2.2滑动窗口模式

概念:时间轴上,一个固定大小的窗口随时间平滑滚动。任何时刻,静态地通过窗口内观察到的信息,都等价于一段长度与窗口大小相等的信息。主要是通过记录多个较小时间窗口(子窗口)的请求次数,实现更精细化的限流控制

假设:准备观察的时间片段为 10 秒,以 1 秒作为统计精度,那可以得到一个长度为 10 的数组。设定限流阈值是最近 10 秒内收到的请求不超过 500 个,那么就需要统计10个子数组的请求总数,是否超过阈值。

优点

  • 解决了固定窗口边界问题

缺点

  • 只适用于否决式限流,超过阈值的流量就必须失败

2.3漏桶模式

漏桶可以简单的理解:小学水池应用题,一个水池,每秒以 X 升速度注水,同时又以 Y 升速度出水,问水池啥时候装满。

概念:将请求视为流入漏桶的水,漏桶以固定速率“漏水”。当请求流量超过漏桶的处理能力时,多余的请求会被丢弃或排队。其核心思想是平滑请求流量

实现方式

  1. 维护一个队列(或计数器),用来模拟漏桶。

  2. 新请求到来时,将请求放入桶中。

  3. 按固定速率处理桶中的请求。

  4. 如果桶已满,则拒绝新请求。

缺点:

  • 比较难确定桶的大小和水流出的速度

2.4令牌桶算法

和漏桶一样是基于缓冲区的限流算法,简单理解就是去银行办事时在排队机号取号的场景。

概念:通过固定速率向桶中添加令牌,请求到来时需要先消耗令牌才能被处理。如果桶中没有足够的令牌,请求会被拒绝。与漏桶算法不同,令牌桶允许一定的突发流量

实现方式

  1. 维护一个桶,桶中存储令牌。

  2. 按固定速率(比如限流是1秒100次请求,那么间隔10ms时间放入令牌)向桶中添加令牌,直到桶满为止。

  3. 请求到来时从桶中取出令牌,如果没有令牌就马上失败或者进入降级逻辑。

实际开发的时候,不需要专门做放令牌到桶里这件事,只需要在获取令牌前,比较一下时间戳与当前时间,就能算出需要放入多少令牌,下面是示例代码:

private long lastTime = System.currentTimeMillis();
private int tokens = 0; // 当前令牌数
private static final int LIMIT = 100; // 桶容量
private static final int REFILL_RATE = 10; // 令牌添加速率(令牌/秒)

public synchronized boolean tryAcquire() {
    long now = System.currentTimeMillis();
    // 添加令牌
    tokens = Math.min(LIMIT, tokens + (int) ((now - lastTime) / 1000) * REFILL_RATE);
    lastTime = now;

    if (tokens > 0) {
        tokens--;
        return true;
    }
    return false;
}

3.分布式限流

上面介绍的4种限流算法都只适用于单机限流,或者把系统当做整体来限流。实际应用中仍然需要精细的每个服务的限流。

概念:过将限流逻辑分散到多个节点,同时使用一致性算法保证全局限流的一致性。它结合了本地限流和集中式限流的优点。

实现方式

  • 基于 Redis + Lua 脚本使用 Redis 脚本实现分布式限流,在 Redis 中存储全局的请求计数器

  • 基于一致性算法使用分布式一致性算法(如 Raft、Paxos)维护全局流量状态

  • 分布式网关通过 API 网关(如 Kong、Nginx、Spring Cloud Gateway)实现流量的统一调度和限流。

缺点

  • 实现复杂度高,且网络通信和一致性操作带来额外延迟。当流量大时,限流本身会降低系统处理能力

总结

今天学习了4种限流设计模式:流量计数器模式、滑动窗口模式、漏桶模式、令牌桶模式,后面2种都是基于缓冲区的限流算法。简单了解了下分布式限流的概念。限流本身是有代价的,实际开发中需要权衡方案的代价和收益。后续有时间补充Sentinel的限流原理和其中用了哪些设计模式。

文章转载自:卷福同学

原文链接:https://www.cnblogs.com/dnboy/p/18621955

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年11月 蓝桥杯青少组 STEMA考试 Scratch真题

2024年11月 蓝桥杯青少组 STEMA考试 Scratch真题(选择题) 题目总数:5 总分数:50 选择题 第 1 题 单选题 Scratch运行以下程宇后,小兔子会( )。 A. 变小 B. 变大 C. 变色 D. …

Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类

Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类 CIFAR10数据集ParNet架构特点优势应用 ParNet结构代码详解结构代码代码详解SSEParNetBlock 类DownsamplingBlock 类FusionBlock 类ParNet 类 训练过程和测试结果代码汇总parnet.pytrain.pytest.py 前面文章我们构…

Docker核心技术和实现原理

目录 1. Docker镜像原理介绍1.1 操作系统基础1.2 Union FS(联合文件系统)1.3 再看 Docker 镜像是什么 2. 镜像的实现原理2.1 Docker 分层存储实现原理2.2 docker 镜像加载原理 3. 镜像分层存储实战3.1 基础知识3.2 实战过程 4. overlay 文件系统工作实战5. Docker卷原理介绍5.1…

AI的进阶之路:从机器学习到深度学习的演变(二)

AI的进阶之路:从机器学习到深度学习的演变(一) 三、机器学习(ML):AI的核心驱动力 3.1 机器学习的核心原理 机器学习(Machine Learning, ML)突破了传统编程的局限,它不再…

34.正则表达式

python正则表达式,使用re模块,模块中三个基础方法来做正则匹配。 match re.match(匹配规则, 被匹配的字符串) 从字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空。…

xpath插件安装与使用

1.背景 在使用python爬取页面数据时,经常会遇到解析页面数据,有一个非常好用的插件工具 是:xpath插件 2.安装与使用步骤 步骤1:准备xpath插件,并解压 步骤2:添加扩展程序 点击扩展程序后: 点击:加载已解压的扩展程序 安装成功后: 关闭浏览器,重新打开浏览器就可以使用了 步…

安徽医科大学卫生管理学院与和鲸科技签署“101 数智领航计划”,共拓“医学+AI”学科建设与人才培养

为进一步强化卫生健康人才培养关键方向,着力加强“医学AI”的复合型交叉人才培养,2024 年 12 月 13 日,安徽医科大学卫生管理学院与上海和今信息科技有限公司(以下简称“和鲸科技”)召开校企合作洽谈会,并正…

spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))

目录 一、单个字符串或对象引用的注入。 (1)简单案例演示。 1、项目的基本结构和类介绍。 2、接口"UserDao"代码。 3、实现类"UserDaoImpl"代码。 4、spring配置文件。 5、测试类(MainApp)。 6、查看执行结果。(对应成员变量成功注入…

三格电子——新品IE103转ModbusTCP网关

型号:SG-TCP-IEC103 产品概述 IE103转ModbusTCP网关型号SG-TCP-IEC103,是三格电子推出的工业级网关(以下简称网关),主要用于IEC103数据采集、DLT645-1997/2007数据采集,IEC103支持遥测和遥信,可…

leetcode-283.移动零-day13

方法一:双指针遇 0 交换 1. 基本思路回顾 该方法使用了两个指针m和i,m用于标记当前已经处理好的非零元素应该放置的位置,i用于遍历整个数组。当遇到nums[m]为0时,会通过内层while循环找到下一个非零元素(如果存在的话…

基于LabVIEW的USRP信道测量开发

随着无线通信技术的不断发展,基于软件无线电的设备(如USRP)在信道测量、无线通信测试等领域扮演着重要角色。通过LabVIEW与USRP的结合,开发者可以实现信号生成、接收及信道估计等功能。尽管LabVIEW提供了丰富的信号处理工具和图形…

Go怎么做性能优化工具篇之基准测试

一、什么是基准测试(Benchmark) 在 Go 中,基准测试是通过创建以 Benchmark 开头的函数,并接收一个 *testing.B 类型的参数来实现的。testing.B 提供了控制基准测试执行的接口,比如设置测试执行的次数、记录每次执行的…

Windows下使用git配置gitee远程仓库

目录 使用git配置(传统方法) 1、在桌面新建一个文件夹 2、git clone [ur1] 3、git branch查看分支 4、git branch新建分支(重要) 5、git push推送新分支 简单版(使用git小乌龟) 官网下载&#xff1…

DotNetBrowser 3.0.0 正式发布!

🛠️ 重要消息:DotNetBrowser 3.0.0 正式发布! 我们很高兴向您介绍全新的 DotNetBrowser 3.0.0 版本。此次更新带来了多项重要功能与优化,进一步提升了 Web 开发的效率和体验。 📢 DotNetBrowser 3.0.0 包含哪些新功…

【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题

目录 为什么要结合项目与算法? 1. 蓝桥杯与《苍穹外卖》项目的结合 实例:基于蓝桥杯算法思想的订单配送路径规划 问题描述: 代码实现:使用动态规划解决旅行商问题 代码解析: 为什么这个题目与蓝桥杯相关&#x…

严格推导质点曲线运动的运动学方程

前言 相当一部分物理学书籍在推导质点曲线运动的运动学方程时,采用的都是先建立位移的微元 Δ r ⃗ \Delta \vec{r} Δr ,然后几何近似求极限的方法。这种方法虽然能得到正确的结论,但数学上的严格性略有欠缺,且过程繁琐。考虑到…

【gym】理解gym并测试gym小游戏CartPole (一)

一、gym与文件位置的联合理解 import gym import inspect# 加载 CliffWalking 环境 env gym.make(CliffWalking-v0)# 获取环境的类 env_class type(env)# 获取环境类所在的文件路径 file_path inspect.getfile(env_class)print(f"The source code for CliffWalking-v0…

Mac升级macOS 15 Sequoia后,无法ssh连接本地虚拟机

现象 macOS 15后,无法ssh连接本地启动的虚拟机,提示错误: No route to host,也ping不通。包括UTM、Parallels Desktop这两个虚拟机软件。之前都是没问题的,通过一些简单排查,目前没发现什么问题。 在虚拟…

vue3 setup模式使用事件总线Event bus用mitt,app.config.globalProperties.$bus

环境介绍package.json中的内容如下 需要 npm install mitt: {"name": "event_bus_test","version": "0.0.0","private": true,"type": "module","scripts": {"dev": &…

【java基础系列】实现一个简单的猜数字小游戏

主要是用的java中的键盘录入和随机数两个api,实现这种人机交互的小游戏,可以用来锻炼基础算法思维 实现效果 实现代码 package com.gaofeng.day10;import java.util.Random; import java.util.Scanner;/*** author gaofeng* date 2024-12-22 - 9:21*/ …