1、服务限流
限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统, 一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。
一般开发高并发系统常见的限流有:
限制总并发数(比如数据库连接池、线程池)、
限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、
限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率);
其他还有如限制远程接口调用速率、限制MQ的消费速率。
另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。
1)限流算法
常见的限流算法有:令牌桶、漏桶、计数器算法(固定时间窗口)。
令牌桶算法:
所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
根据限流大小,设置按照一定的速率往桶里添加令牌;
桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;
漏桶:
漏桶算法其实很简单,可以粗略的认为就是注水漏水过程,往桶中以一定速率流出水,以任意速率流入水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。
计数器算法:(启动一个计数器,每次服务请求会把计数器加一,同时到达指定的时间间隔后会把计数器清零)
计数器算法是在一定的时间间隔里,记录请求次数,当请求次数超过该时间限制时,就把计数器清零,然后重新计算。当请求次数超过间隔内的最大次数时,拒绝访问。
例如:一个接口每分钟允许访问100次。实现方式如下:
1)设置一个计数器 count ,接收一个请求就将计数器加一,同时记录当前时间。
2)判断当前时间和上次统计时间是否为同一分钟。
如果是,则判断 count 是否超过阈值,如果超过阈值,则返回限流拒绝。
如果不是,则把 count 重置为1,判断是否超过阈值。
实现:对单机限流来说,使用全局内存计数即可,但对分布式系统需要有一个公共存储计数,redis是最佳存储方案,且redis的incr能保障原子性操作。
适用场景:适用于做API限流,比如对外提供ip定位查询服务api,天气查询api等,可以根据ip做粒度控制,防止恶意刷接口造成异常,也适用于提供API查询服务做配额限制,一般限流后会对请求做丢弃处理。
方案改进:使用滑动窗口
局限:窗口算法对于流量限制是定速的,对细粒度时间控制突发流量控制能力就有限了。
注意:计数器算法 VS 漏桶算法 VS 令牌桶算法【优缺点】
计数器算法的优点是简单,能满足简单的限流需求,但缺点也是明显的:临界问题。
漏桶算法的出水速度是恒定的,那么意味着如果瞬时大流量的话,将有大部分请求被丢弃掉(也就是所谓的溢出)。
令牌桶算法生成令牌的速度是恒定的,而请求去拿令牌是没有速度限制的。这意味,面对瞬时大流量,该算法可以在短时间内请求拿到大量令牌,而且拿令牌的过程并不是消耗很大的事情。
所以:无论是对于令牌桶拿不到令牌被拒绝,还是漏桶的水满了溢出,都是为了保证大部分流量的正常使用,而牺牲掉了少部分流量,这是合理现象,如果因为极少部分流量需要保证的话,那就可能导致系统达到极限而挂掉,得不偿失。
2)应用级限流
对于一个应用系统来说一定会有极限并发/请求数,即总有一个TPS/QPS阀值,如果超了阀值则系统就会不响应用户请求或响应的非常慢,因此最好进行过载保护,防止大量请求涌入击垮系统。
MySQL(如max_connections)、Redis(如tcp-backlog)都会有类似的限制连接数的配置。
3)池化技术
如果有的资源是稀缺资源(如数据库连接、线程),而且可能有多个系统都会去使用它,那么需要限制应用;
可以使用池化技术来限制总资源数:连接池、线程池。
比如分配给每个应用的数据库连接是100,那么本应用最多可以使用100个资源,超出了可以等待或者抛异常。
4)分布式限流
分布式限流最关键的是要将限流服务做成原子化,
而解决方案可以使使用redis+lua或者nginx+lua技术进行实现,通过这两种技术可以实现的高并发和高性能。
2、服务降级
概念:服务降级是指在面对高并发请求的时候,根据实际业务使用情况以及流量,对一些服务和页面有策略的不处理或者用一种简单的方式进行处理,从而释放服务器资源的资源以保证核心业务的正常高效运行
问题产生的原因: 服务器的资源是有限的,而请求是无限的。在用户使用即并发高峰期,会影响整体服务的性能,严重的话会导致宕机,以至于某些重要服务不可用。所以为了保证核心功能的可用性,就需要对某些不重要的服务进行降级处理,舍小保大。
应用场景:比如说双十一的时候,为了保证付款的服务正常运行,就无法关闭,或者对退款的服务进行降级。
3、服务熔断
概念:应对微服务雪崩效应的一种链路保护机制。
原因:A->B->C ,如果某一时间段对服务C的调用响应时间过长或者服务C不可用,随着时间的增长,对服务C的调用也越来越多,然后服务C崩溃了,但是链路调用还在,对服务B的调用也在持续增多,然后服务B崩溃,随之A也崩溃,导致雪崩效应。服务熔断是应对雪崩效应的一种微服务链路保护机制。
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。
-应用场景:微服务架构中,多个微服务相互调用出使用
需要考虑问题:
如何所依赖的服务对象不稳定
失败之后如何快速恢复依赖对象,如何探知依赖对象是否恢复
3.服务降级和服务熔断区别
触发原因不一样,服务熔断由链路上某个服务引起的,服务降级是从整体的负载考虑
管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理
实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制
触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
一句话:
服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是更加宽泛的概念,主要是对系统整体资源的合理分配以应对压力。
服务熔断是服务降级的一种特殊情况,他是防止服务雪崩而采取的措施。系统发生异常或者延迟或者流量太大,都会触发该服务的服务熔断措施,链路熔断,返回兜底方法。这是对局部的一种保险措施。
服务降级是对系统整体资源的合理分配。区分核心服务和非核心服务。对某个服务的访问延迟时间、异常等情况做出预估并给出兜底方法。这是一种全局性的考量,对系统整体负荷进行管理。
限流:限制并发的请求访问量,超过阈值则拒绝;
降级:服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;
举例:当双11活动时,把无关交易的服务统统降级,如查看蚂蚁森林,查看历史订单
熔断:依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复。
总结:
限流、熔断和服务降级是系统容错的重要设计模式,从一定意义上讲限流和熔断也是一种服务降级的手段。
熔断和服务降级主要是针对非核心业务功能,而核心业务如果流程超过预估的峰值,就需要进行限流。
对于限流,选择合理的限流算法很重要,令牌桶算法优势很明显,也是使用最多的限流算法。
在系统设计的时候,这些模式需要配合业务量的预估、性能测试的数据进行相应阈值的配置,而这些阈值最好保存在配置中心,方便实时修改。