前言
随着现在微服务、分布式系统的发展,各个服务之间的相互调用越来越复杂。为了保证自身服务的稳定性与高可用,当面对超过自身服务能力的请求调用时,要做一定的限流措施。如同五一、国庆期间的旅游出行、景区爆满,游客限流。我们的服务面对诸如秒杀、大促、618、双十一以及可能的恶意攻击、爬虫等高并发、大流量的场景也需要做服务限流。
对超出服务处理能力之外的请求进行拦截,对访问服务的流量进行限制,这就是服务限流。接下来我们就好好谈谈服务限流这回事儿。
两种限流方式
常见的限流方式可以分为两类:基于请求限流和基于资源限流。
- 基于请求限流
基于请求限流指从外部访问的请求角度考虑限流,常见的方式有两种。
第一种是限制总量,也就是限制某个指标的累积上限,常见的是限制当前系统服务的用户总量,例如:某个直播间限制总用户数上限为100万,超过100万后新的用户无法进入;某个抢购活动商品数量只有100个,限制参与抢购的用户上限为1万个,1万以后的用户直接拒绝。
第二种是限制时间量,也就是限制一段时间内某个指标的上限,例如1分钟内只允许10000个用户访问;每秒请求峰值最高为10万。
优点:
- 实现简单
缺点:
- 实践中面临的主要问题是比较难以找到合适的阈值。例如系统设定了1分钟10000个用户,但实际上6000个用户的时候系统就扛不住了;或者达到1分钟10000用户后,其实系统压力还不大,但此时已经开始丢弃用户访问了。而且还要考虑硬件相关的因素,例如一台32核的机器和64核的机器处理能力差别很大,阈值是不同的。
应用:
- 适用于业务功能比较简单的系统,例如负载均衡系统、网关系统、抢购系统等。
- 基于资源限流
基于请求限流是从系统外部考虑的,而基于资源限流是从系统内部考虑的,也就是找到系统内部影响性能的关键资源,对其使用上限进行限制。常见的内部资源包括连接数、文件句柄、线程数和请求队列等。比如CPU的占用率超过80%的时候就开始拒绝新的请求。
优点:
- 有效地反映当前系统的压力,更好的进行限流
缺点:
- 难以确定关键资源
- 难以确定关键资源的阈值,需要在线上逐步调试,持续观察,直到找到合适的值。
应用:
- 适用于具体的某个服务,比如订单系统、商品系统等。
四种限流算法
常见的限流算法有4种,它们的实现原理和优缺点各不相同,在实际设计的时候需要根据业务场景来选择。
- 固定时间窗
固定时间窗算法的实现原理是,统计固定时间周期内的请求量或者资源消耗量,超过限额就会启动限流,如下图所示:
优点:
- 实现简单
缺点:
- 存在临界点问题。例如上图中的红蓝两点只间隔了短短10秒,期间的请求数却已经达到200,超过了算法规定的限额(1分钟内处理100)。但是因为这些请求分别来自两个统计窗口,从单个窗口来看还没有超出限额,所以并不会启动限流,结果可能导致系统因为压力过大而挂掉。
- 滑动时间窗
为了解决临界点问题,滑动时间窗算法应运而生,它的实现原理是,两个统计周期部分重叠,从而避免短时间内的两个统计点分属不同的时间窗的情况,如下图所示:
优点:
- 不存在临界点问题
缺点:
- 相对于固定窗口,复杂度有所提升
- 漏桶算法
漏桶算法的实现原理是,将请求放入“桶”(消息队列等),业务处理单元(线程、进程和应用等)从桶里拿请求处理,桶满则丢弃新的请求,如下图所示:
优点:
- 突发大量流量时丢弃的请求较少,因为漏桶本身有缓存请求的作用
缺点:
- 可以平滑流量,但是无法解决流量突增的问题。
- 桶大小动态调整比较困难,需要不断的尝试才能找到符合业务需求的最佳桶大小。
- 无法精确控制流出速度,也就是业务的处理速度。
漏桶算法主要适用于瞬时高并发流量的场景(例如刚才提到的0点签到、整点秒杀等)。在短短几分钟内涌入大量请求时,为了更好的业务效果和用户体验,即使处理慢一些,也要做到尽量不丢弃用户请求。
- 令牌桶算法
令牌桶算法和漏桶算法的不同之处在于,桶中放入的不是请求,而是“令牌”,这个令牌就是业务处理前需要拿到的“许可证”。也就是说,当系统收到一个请求时,先要到令牌桶里面拿“令牌”,拿到令牌才能进一步处理,拿不到就要丢弃请求。
它的实现原理是如下图所示:
优点:
- 通过控制放入令牌的速率,可以动态调整处理速率,实现更加灵活。
- 可以平滑限流,同时可以容忍突发流量,因为桶里面可以累积一定数量的令牌,当突发流量过来的时候,桶里面有累积的令牌,此时的业务处理速度会超过令牌放入的速度。
缺点:
- 突发大量流量的时候可能丢弃很多请求,因为令牌桶不能累积太多令牌。
- 实现相对复杂。
令牌桶算法主要适用于两种典型的场景,一种是需要控制访问第三方服务的速度,防止把下游压垮,例如支付宝需要控制访问银行接口的速率;另一种是需要控制自己的处理速度,防止过载,例如压测结果显示系统最大处理TPS是100,那么就可以用令牌桶来限制最大的处理速度。
五种限流策略
- 服务拒绝
当请求流量达到限流阈值时,对多余的请求直接拒绝。
可通过设计实现对指定域名、IP、客户端、应用、用户等不同来源的请求进行拒绝。
- 延时处理
通过将多余的请求加入缓存队列或延时队列,来应对短期的流量突增,高峰期过后开始将堆积的请求流量逐渐处理。
- 请求分级(优先级)
对不同来源的请求设置优先级,先处理优先级更高的请求。如VIP客户、重要的业务应用(如交易服务优先级高于日志服务)。
- 动态限流
可以监控系统相关指标、评估系统压力,通过注册中心、配置中心等动态调整限流阈值。
- 监控预警&动态扩容
如果有优秀的服务监控系统与自动部署、发布系统,可以通过监控系统自动监测系统运行情况,对短期内服务压力暴增、流量大幅写入的情况进行邮件、短信等方式进行预警。
在满足特定条件下,可自动部署、发布相关服务,起到动态扩容的效果。
三个限流位置
- 接入层限流
可以通过Nginx、API路由网关等对域名或IP进行限流,同时可以拦截非法请求。
- 应用限流
每个服务可以有自己的单机或集群限流措施,也可以调用第三方的限流服务,比如阿里的Sentinel限流框架。
- 基础服务限流
也可以对基础服务层进行限流。
- 数据库:限制数据库连接、限制读写速率
- 消息队列:限制消费速率(消费量、消费线程)
总结
本文从宏观角度总结了服务限流的两种方式,三个可以限流的位置,四种常见的限流算法,五种限流的策略。最后再补充几句,合理的限流配置,需要了解系统的吞吐量,所以,限流一般需要结合容量规划和压测来进行。当外部请求接近或者达到系统的最大阈值时,触发限流,采取其他的手段进行降级,保护系统不被压垮。
参考:http://www.studyofnet.com/555653372.html