1、为什么要加限流熔断降级?
对于这个问题,有两个考虑点:
第一个是流量是不稳定的,有时候高,有时候低,如果流量特别高,就会导致cpu、内存吃紧,gc,导致处理能力下降,导致所有的请求都会受到影响;第二个影响是也会导致雪崩问题,因为一个接口的流量暴增引起的cpu压力增大、gc次数增加会导致其他的接口的处理速度也会下降,甚至导致整个服务不可用,对于上游来说,因为当前服务响应慢,导致比如rpc请求的连接的使用时间增加,会导致其连接数也会增加、同理线程也会增加,如果达到阈值,那么上游服务就没有连接数和线程可用,导致上游也不可用,所以会导致雪崩。对于这种情况添加限流可以避免突发流量的控制,是程序更加稳定,当然对于这种情况也可以使用机器扩容来解决,比如美团又hulk平台,可以配置监控任务,当cpu或者内存或者gc次数上升的时候,可以动态扩容。
第二个,下游依赖是不稳定的,比如调用下游接口、数据库、redis等,或者说网络是不可用的。如果出现网络问题或者下游响应慢,那么就会有消耗更多的线程、连接,如果达到阈值了,就会导致没有线程、连接可用,此时程序就会阻塞,导致本服务的其他接口也没法使用,也会影响上游接口,导致雪崩。
所以需要给接口添加一个保护措施,避免因为单个接口的流量徒增或者单个接口的下游不稳定导致整个系统不可用,甚至引发雪崩。
保护措施可以有三种:
1、基于请求次数的,比如qps、比如令牌桶。
2、线程数,等下要完善。
3、异常
2、限流的方案都有什么
滑动窗口算法:通过计算qps,判断qps有没有超过预设的阈值,如果超过了就禁止请求,直接执行降级方法。滑动窗口有两个概念,一个是时间窗口,比如计算qps要计算过去多久的qps,一个是采样周期,比如时间窗口可以设置1分钟,采样周期可以设置1秒,可以用一个数组,大小是60,每个元素表示每秒的统计数据,这样就可以根据当前是第几秒直接定位到某个元素,更新他的请求次数,这样在计算qps的时候,可以求过去60个值的平均值来计算qps。
令牌桶:令牌是直接丢弃,不是还回去。实现方案:redis的队列。
漏桶算法:
对于redis更新的降级:
假如一个在redis中商品过期了,有多个请求同时来读取这个商品,如果没有措施,那么这n个请求都会去查询数据库然后都会写入到缓存,导致效率低下。正确的方式是:
1、加分布式锁,只有一个线程加锁成功,其余的线程可以等待或者直接降级,推荐降级,等待不太好,首先处理逻辑比较复杂,第二个万一获得锁的线程处理的比较慢,阻塞的线程就会很多,就有雪崩的风险。降级的话会安全很多。
2、写空数据,比如写个空字符串,这样其他线程就能拿到空数据,表示正在有线程处理这个key,然后可以直接降级。这个更好一些,相比于加锁,该方法避免了其他现成去加锁。