浅谈服务容错及其实现方案

1.什么是服务雪崩

在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
如下图所示 , A为服务提供者 , B为A的消费者 , C是B的消费者

在这里插入图片描述


当A服务不可用时 , 导致B服务的不可用 , 并将不可用逐渐蔓延到C , 就发生了微服务中的"雪崩"现象。

2.服务雪崩产生过程

正常情况 , 一个请求进入C , C会从线程池中申请一个线程处理 , 然后请求B , 同时线程等待 ; B服务收到请求同样申请线程然后请求A , A处理完成返程结果并归还释放A自身的线程 , 然后BC依次完成响应并归/释放还线程

A故障后 , B的线程请求A后 , 迟迟未得响应 , 线程阻塞等待

C继续接收大量请求并传给B , 导致B大量阻塞线程等待 , 直到线程资源耗尽 , 无法接收新的请求 . B服务故障

故障继续蔓延 , C的线程资源耗尽后 , 一个请求再也不能完成 . 整个微服务当机

3.服务雪崩产生原因

  1. 服务提供者不可用
    硬件故障:服务器主机死机 , 或网络硬件故障导致服务提供者无法及时处理和相应
    程序故障 : 缓存击穿 , 缓存应用重启或故障导致所有缓存被清空 , 或者大量缓存同时过期 , 导致大量请求直击后端(文件或数据库) , 造成服务提供者超负荷运行导致瘫痪 ; 高并发请求 , 在某些场景下 , 例如秒杀和大促销之前 , 如果没有做好应对措施 , 用户的大量请求也会导致服务故障
  2. 不合理的流量激增
    用户重试 : 用户忍不了界面上的一直加载或等待 , 频繁刷新页面或提交表单 , 这是在秒杀场景下的常规操作
    代码逻辑重试 : 在消费者服务中存在大量不合理的重试逻辑 , 比如各种异常的重试机制等
  3. 服务消费者不可用
    大量的等待线程占用系统资源 , 一旦资源被耗尽 , 消费者这边也会发生连锁反应 , 然后会导致故障向下蔓延

4.雪崩的应对策略

  • 超时:在调用方,一定时间内未得到响应则放弃请求;在异常时,确保资源的消耗有上限
  • 限流:在提供方,限制进入系统的流量,确保在系统的负载范围内,保证系统能够正常运转
  • 仓壁模式:资源隔离手段,在调用方使用线程池等方式,限制资源消耗在某个范围之内
  • 熔断器: 在调用方,在系统上检测调用,如果发现了某个底层服务故障,则自动熔断,不再调用该服务,隔一段时间再次检测是否恢复,如果恢复则加入集群
  • 自动熔断机制
    针对突发流量,除了扩容和流控外,还有一个能有效地保护系统整体可用性的手段就是熔断机制

    API 1 和 API 2一起关联部署,而且共同依赖多个依赖服务A,B,C。如果此时API 1依赖的服务B由于资源或者网络等原因造成接口变慢,就会导致和API 1一起关联部署的API 2也出现整体性能被拖累变慢的情况,继而导致依赖API 1和API 2的其他上层的服务也级联地性能变差,最终可能导致系统整体性能的雪崩。

    虽然服务间的调用能够通过超时控制来降低被影响的程序,但在狠多情况下,单纯依赖超时控制很难避免依赖服务性能恶化的问题。这种情况下,需要能快速熔断对这些性能出现问题的依赖调用。

    一种常见的方式是手动通过开关来进行依赖的降级,微博的很多场景和业务都有用到开关来实现业务或者资源依赖的降级。

    另一种更智能的方式是自动熔断机制。自动熔断机制主要是通过持续收集被依赖服务或者资源的访问数据和性能指标,当性能出现一定程度的恶化或者失败量达到某个阈值时,会自动触发熔断,让当前依赖快速失败,并降级到其他备用依赖,或者暂存到其他地方便于后续重试恢复。在熔断过程中,再通过不停探测被依赖服务或者资源是否恢复,来判断是否自动关闭熔断,恢复业务。

  • 流量控制
    计数器
    计数器是一种最简单的限流器.

    如果把 QPS 设置为100, 从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝。等到1s结束后,把计数恢复成0,重新开始计数. 这种计数器一般称为固定窗口计数器算法.

    可以看到计数器虽说有一定的缓冲空间, 但是需要一定的恢复空窗期, 在这个恢复时间内请求全部拒绝. 计数器还存在着另外一个问题, 特殊情况下会让请求的通过量为限制的两倍.

    考虑如下情况:

    限制 1 秒内最多通过 5 个请求,在第一个窗口的最后半秒内通过了 5 个请求,第二个窗口的前半秒内又通过了 5 个请求。这样看来就是在 1 秒内通过了 10 个请求

    综合来看, 计数器方式的限流是比较简单粗暴的, 我们需要更加优雅的限流方式

    漏桶算法:
    主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。它模拟的是一个漏水的桶,所有外部的水都先放进这个水桶,而这个桶以匀速往外均匀漏水,如果水桶满了,外部的水就不能再往桶里倒了。

    这里你可以把这些外部的水想象成原始的请求,桶里漏出的水就是被算法平滑过后的请求。从这里也可以看出来,漏桶算法可以比较好地控制流量的访问速度。

    由于漏斗桶有点类似队列, 先进去才能被消费掉, 如果漏斗桶溢出了, 后续的请求都直接丢弃了, 也就是说漏斗桶是无法短时间应对突发流量的. 对于互联网行业来说, 面对突发流量, 不能一刀切将突发流量全部干掉, 这样会给产品带来口碑上影响. 因此漏斗桶也不是完美的方案.

    不过漏斗桶能够限制数据的平均传输速率, 能够满足大部分的使用场景的. 如: 我们可以使用漏斗桶限制论坛发帖频率

    令牌桶算法
    控制的是一个时间窗口内通过的数据量。大概实现如下:

    1.每1/r秒往桶里放入一个令牌,r是用户配置的平均发送速率(也就是每秒会有r个令牌放入)

    2.桶里最多可以放入b个令牌,如果桶满了,新放入的令牌会被丢弃

    3.如果来了n个请求,会从桶里消耗n个令牌。

    4、如果桶里可用令牌数小于n,那么这n个请求会被丢弃或者等待新的令牌放入。

    算法按一定速度均匀往桶里放入令牌,原始请求进入后,根据请求量从令牌桶里取出需要的令牌数,如果令牌数不够,会直接抛弃掉超限的请求或者进行等待,能成功获取到令牌的请求才会进入到后端服务器。

    与漏桶算法精确控制速率不太一样的是,由于令牌桶的桶本身具备一定的容量,可以允许一次把桶里的令牌全都取出,因此,令牌桶算法在限制请求的平均速率的同时,还允许一定程度的突发流量。

  • 流控效果
  • 快速失败:直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式。该方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
  • warm up(预热):冷启动(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式。该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。
    比如设置的阈值为100,预热时间10s,冷加载因子是3,那最初的阈值时100/3,逐步加大阈值,10s后达到100。阈值大小决定了流量大小,也就是逐步释放请求流量进入系统,让系统“冷启动”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值