Random Thoughts
Recent content on Random Thoughts
马上订阅 Random Thoughts RSS 更新: https://blog.joway.io/index.xml
RPC 漫谈: 限流问题
微服务之间的 RPC 调用往往会使用到限流功能,但是很多时候我们都是用很简单的限流策略,亦或是工程师拍脑袋定一个限流值。
这篇文章主要讨论在 RPC 限流中,当前存在的问题和可能的解决思路。
为什么需要限流
避免连锁崩溃
一个服务即便进行过压测,但当真实运行到线上时,其收到的请求流量以及能够负载的流量是不固定的,如果服务自身没有一个自我保护机制,当流量超过预计的负载后,会将这部分负载传递给该服务的下游,造成连锁反应甚至雪崩。
提供可靠的响应时间
服务调用方一般都设有超时时间,如果一个服务由于拥塞,导致响应时间都处于超时状态,那么即便服务最终正确提供了响应,对于 Client 来说也完全没有意义。
一个服务对于调用方提供的承诺既包含了响应的结果,也包含了响应的时间。限流能够让服务自身通过主动丢弃负载能力外的流量,以达到在额定负载能力下,依然能够维持有效的响应效率。
传统方案
漏斗
优点:
- 能够强制限制出口流量速率
缺点:
- 无法适应突发性流量
令牌桶
优点:
- 在统计上维持一个特定的平均速度
- 在局部允许短暂突发性流量通过
存在的问题
在两类传统方案中,都需要去指定一个固定值用以标明服务所能够接受的负载,但在现代的微服务架构中,一个服务的负载能力往往是会不断变化的,有以下几个常见的原因:
- 随着新增代码性能变化而变化
- 随着服务依赖的下游性能变化而变化
- 随着服务部署的机器(CPU/磁盘)性能变化而变化
- 随着服务部署的节点数变化而变化
- 随着业务需求变化而变化
- 随着一天时间段变化而变化
通过人工声明一个服务允许的负载值,即便这个值是维护在配置中心可以动态变化,但依然是不可持续维护的,况且该值具体设置多少也极度依赖于人的个人经验和判断。甚至人自身的小心思也会被带入到这个值的选择中,例如 Server 会保守估计自己的能力,Client 会过多声明自己的需求,长期以往会导致最终的人为设定值脱离了实际情况。
什么是服务负载
当我们向一个服务发起请求时,我们关心的无外乎两点:
- 服务能够支撑的同时并发请求数
- 服务的响应时间
并发请求数
对于 Server 而言,有几个指标常常容易搞混:
- 当前连接数
- 当前接受的请求数
- 当前正在并发处理的请求数
- QPS
连接数和请求数是 1:N 的关系。在现代 Server 的实现中,连接本身消耗的服务器资源已经非常少了(例如 Java Netty 实现,Go Net 实现等),而且一般对内网的服务而言,多路复用时,请求数变多也并不一定会导致连接数变多。
有些 Server 出于流量整形角度的考虑,并不一定会在收到请求以后,立马交给 Server 响应函数处理,而是先加入到一个队列中,等 Server 有闲置 Worker 后再去执行。所以这里就存在两类请求:接受的请求与正在处理的请求。
而 QPS 是一个统计指标,仅仅只表示每秒通过了多少请求。