【八股消消乐】构建微服务架构体系—限流算法优化

发布于:2025-06-13 ⋅ 阅读:(26) ⋅ 点赞:(0)

在这里插入图片描述

😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔本专栏《八股消消乐》旨在记录个人所背的八股文,包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点,期待与你一同探索、学习、进步,一起卷起来叭!

题目

💬技术栈:微服务架构

🔍简历内容:熟悉主流限流算法,基于Go语言实现BBR算法适应业务。为gRPC实现了各种限流算法,包括基于Redis实现的集群限流版本(开源)。

🚩面试问:针对 IP 限流是一个非常常见的限流方案,那么怎么获得用户的 IP 呢?尤其是在请求经过了网关的情况下,怎么避免自己拿到的是网关的 IP?


在这里插入图片描述

💡建议暂停思考10s,你有答案了嘛?如果你有不同题解,欢迎评论区留言、打卡。


答案

获取用户的真实IP,有应用层方法、传输层方法、网络层方法:

  1. 应用层方法:X-Forwarded-For,缺点:会被伪造、多个X-Forwarded-For头部、不能解决HTTP和SMTP之外的真实源IP获取的需求
  2. 传输层方法:利用 TCP Options 的字段来承载真实源 IP 信息、Proxy Protocol、NetScaler的 TCP IP header
  3. 网络层:隧道 +DSR 模式

限流

限流算法划分成静态算法和动态算法两类。

  • 静态算法包含令牌桶、漏桶、固定窗口和滑动窗口。这些算法就是要求研发人员提前设置好阈值。在算法运行期间它是不会管服务器的真实负载的。
  • 动态算法也叫做自适应限流算法,典型的是 BBR 算法。这一类算法利用一系列指标来判定是否应该减少流量或者放大流量。动态算法和 TCP 的拥塞控制是非常接近的,只不过 TCP 控制的是报文流量,而微服务控制的是请求流量

令牌桶

系统会以一个恒定的速率产生令牌,这些令牌会放到一个桶里面,每个请求只有拿到了令牌才会被执行。每当一个请求过来的时候,就需要尝试从桶里面拿一个令牌。如果拿到了令牌,那么请求就会被处理;如果没有拿到,那么这个请求就被限流了。

在这里插入图片描述

漏桶

漏桶是指当请求以不均匀的速度到达服务器之后,限流器会以固定的速率转交给业务逻辑。【其实就是令牌桶算法的一种特殊形态。将令牌桶中桶的容量设想为 0,令牌产生之后你就需要取走,没取走的话也不会积攒下来。因此 漏桶是绝对均匀的,而令牌桶不是绝对均匀的。】

在这里插入图片描述

固定窗口与滑动窗口

固定窗口、滑动窗口都是指一个固定时间段,只允许执行固定数量的请求。比如说在一秒钟之内只能执行 100 个请求。

区别就在于,滑动窗口是平滑地挪动窗口,而不像固定窗口那样突然地挪动窗口

在这里插入图片描述

限流对象,常见有

  • VIP 用户不限流而普通用户限流。
  • 针对 IP 限流。用户登录或者参与秒杀都可以使用这种限流,比方说设置一秒钟最多只能有 50 个请求,即便考虑到公共 IP 的问题,正常的用户手速也是没那么快的。
  • 针对业务 ID 限流,例如针对用户 ID 进行限流。

在这里插入图片描述
限流后的做法:

  • 同步阻塞等待一段时间:如果是偶发性地触发了限流,那么稍微阻塞等待一会儿,后面就有极大的概率能得到处理。但是要注意控制住超时,也就是说你不能让人无限期地等待下去。在这里插入图片描述
  • 同步转异步:如果一个请求没被限流,那就直接同步处理;而如果被限流了,那么这个请求就会被存储起来,等到业务低峰期的时候再处理
  • 调整负载均衡算法:如果某个请求被限流了,那么就相当于告诉负载均衡器,应该尽可能少给这个节点发送请求。调整节点的权重就能达成这种效果。在这里插入图片描述

固定窗口和滑动窗口会存在毛刺问题:

假如一个窗口大小是一分钟 1000 个请求,你预计这1000 个请求会均匀分散在这一分钟内。那么有没有可能第一秒钟就来了 1000 个请求?

在这里插入图片描述

所以固定窗口和滑动窗口的窗口时间不能太长。比如说以秒为单位是合适的,但是以分钟作为单位就是不合适的。

简历包装

推荐使用Go语言实现动态限流算法:BBR。或者为一些开源框架提供限流插件,例如grpc。
实践操作:Github开源仓库,为gRPC实现的各种限流算法,包括基于Redis实现的集群限流版本。【未来更新教程】

限流回答模板:
(1)限流算法:Github自行实现。例如B站开源的Kratos框架。
(2)限流对象:集群限流或者单机限流、针对具体业务限流、登录的时候针对IP进行限流、针对增值业务对于非付费用户限流等。

场景:IP限流
我在我们公司的登录接口里面就引入了限流机制。正常情况下,一个用户在一秒钟内最多点击一次登录,所以针对每一个 IP,我限制它最多只能在一秒内提交 50 次登录请求。这个 50 充分考虑到了公共 IP 的问题,正常用户是不可能触发这个阈值的。这个限流虽然很简单,但是能够有效防范一些攻击。不过限流再怎么防范,还是会出现系统撑不住流量的情况。
在这里插入图片描述

(3)突发流量:能否处理小规模的突发流量。

漏桶算法非常均匀,但是令牌桶相比之下就没那么均匀。
令牌桶本身允许积攒一部分令牌,所以如果有偶发的突发流量,那么这一部分请求也能得到正常处理。但是要小心令牌桶的容量,不能设置太大。不然积攒的令牌太多的话就起不到限流效果了。
例如容量设置为 1000,那么要是积攒了 1000 个令牌之后真的突然来了 1000 个请求,它们都能拿到令牌,那么系统可能撑不住这突如其来的 1000 个请求。

(4)请求大小:有时候使用了限流,系统还是有可能崩溃。

场景:限流是针对请求个数进行的,那么显然,如果有两台实例,一台实例处理的都是小请求,另一台实例处理的都是大请求,那么都限流在每秒 100 个请求。可能第一台实例什么问题都没有,而第二台实例就崩溃了。

(5)计算阈值:看服务的观测数据、压测、借鉴、手动计算。

看服务的观测数据:看业务高峰期的 QPS 来确定整个集群的阈值。如果要确定单机的阈值,那就再除以实例个数。【业务性能数据】

场景:我们公司有完善的监控,所以我可以通过观测到的性能数据来确定阈值。比如说观察线上的数据,如果在业务高峰期整个集群的 QPS 都没超过 1000,那么就可以考虑将阈值设定在 1200,多出来的 200 就是余量。

问题:

  • 服务必须先上线,有了线上的观测数据才能确定阈值
  • 整个阈值很有可能是偏低的。因为业务巅峰并不意味着是集群性能的瓶颈。如果集群本身可以承受每秒3000个请求,但是因为业务量不够,每秒只有 1000 个请求,那么我这里预估出来的阈值是显著低于集群真实瓶颈 QPS 的。【解决方案:压测,最好是全链路压测】

压测结果:

在这里插入图片描述
口诀:性能A、并发B、吞吐量C。

A点:性能苛刻的服务。A 之前 QPS 虽然在上升,但是响应时间稳定不变。在这个时候资源利用率也在提升。
B点:追求最高并发的服务。选择这个点意味着能撑住更高的并发,但是性能不是最好的,吞吐量也不是最高的。但是这会响应时间已经很长了。
C点:追求吞吐量的服务。

面试官多半会杠你,压力测试特别难,或者有些服务根本测不了,那你怎么办。

回答术语:一般我会认为一家公司应该把压测作为提高系统性能和可用性的一个关键措施,毕竟没有压测数据,性能优化和可用性改进也不知道怎么下手。所以我还是比较建议尽可能把压测搞起来,反正压测这个东西是迟早要有的


但如果真的做不了、来不及、没资源做压测,确定阈值需要通过借鉴

例如:

  • 如果A、B服务是紧密相关的,也就是通常调用了A服务就会调用B服务,那么可以用 A 已经确定的阈值作为 B 的阈值
  • A 服务到 B 服务之间有一个转化关系。比如说创建订单到支付,会有一个转化率,假如说是 90%,如果创建订单的接口阈值是 100,那么支付的接口就可以设置为 90。
    在这里插入图片描述

如果是一个全新的业务,都没得借鉴,那就只能手动计算了。沿着整条调用链路统计出现了多少次数据库查询、多少次微服务调用、多少次第三方中间件访问,如Redis,Kafka等。

假如说一个非常简单的服务,整个链路只有一次数据库查询,这是一个会回表的数据库查询,根据公司的平均数据这一次查询会耗时 10ms,那么再增加 10 ms 作为 CPU 计算耗时。也就是说这一个接口预期的响应时间是 20ms。如果一个实例是 4 核,那么就可以简单用 1000 m s ÷ 20 m s ∗ 4 t i m e s = 200 1000ms ÷ 20ms * 4times = 200 1000ms÷20ms4times=200得到阈值。

手动计算准确率很差,比如说垃圾回收类型语言,还要刨除垃圾回收的开销,相当于 200 打个折扣。折扣多大又取决于你的垃圾回收频率和消耗。


往期精彩专栏内容,欢迎订阅:

🔗【八股消消乐】20250611:构建微服务架构体系—降级策略全总结
🔗【八股消消乐】20250610:构建微服务架构体系—熔断恢复抖动优化
🔗【八股消消乐】20250609:构建微服务架构体系—负载均衡算法如何优化
🔗【八股消消乐】20250608:构建微服务架构体系—服务注册与发现
🔗【八股消消乐】20250607:MySQL存储引擎InnoDB知识点汇总
🔗【八股消消乐】20250606:MySQL参数优化大汇总
🔗【八股消消乐】20250605:端午节产生的消费数据,如何分表分库?
🔗【八股消消乐】20250604:如何解决SQL线上死锁事故
🔗【八股消消乐】20250603:索引失效与优化方法总结
🔗【八股消消乐】20250512:慢SQL优化手段总结
🔗【八股消消乐】20250511:项目中如何排查内存持续上升问题
🔗【八股消消乐】20250510:项目中如何优化JVM内存分配?
🔗【八股消消乐】20250509:你在项目中如何优化垃圾回收机制?
🔗【八股消消乐】20250508:Java编译优化技术在项目中的应用
🔗【八股消消乐】20250507:你了解JVM内存模型吗?
🔗【八股消消乐】20250506:你是如何设置线程池大小?
🔗【八股消消乐】20250430:十分钟带背Duubo中大厂经典面试题
🔗【八股消消乐】20250429:你是如何在项目场景中选取最优并发容器?
🔗【八股消消乐】20250428:你是项目中如何优化多线程上下文切换?
🔗【八股消消乐】20250427:发送请求有遇到服务不可用吗?如何解决?

📌 [ 笔者 ]   文艺倾年
📃 [ 更新 ]   2025.6.12
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

在这里插入图片描述