概述
众所周知,微服务架构具有许多优点。包含松散耦合,自治服务,分散治理,更容易连续交付等。但与此同时,它使架构变得脆弱,因为每个用户的操作结果都会调用多个服务。它通过网络上的远程调用替换了单体系结构中的内存调用。但是当一个或多个服务不可用或表现出高延迟时,会导致整个系统出现级联故障。服务客户端的重试逻辑只会使情况更糟糕,并且可能导致系统彻底的崩溃。
断路器模式有助于防止跨多个系统的这种灾难性级联故障。断路器模式允许我们构建容错和弹性的系统,当关键服务不可用或具有高延迟时,系统仍然可以正常运行。
容错模式
服务熔断
当调用目标服务的请求和调用大量超时或失败,服务调用方为避免造成长时间的阻塞造成影响其他服务,后续对该服务接口的调用不再经过进行请求,直接执行本地的默认方法。
服务降级
为了保证核心业务在大量请求下能正常运行,根据实际业务情况及流量,对部分服务降低优先级,有策略的不处理或用简单的方式处理。
服务限流
当系统资源不够,不足以应对大量请求,对系统按照预设的规则进行流量限制或功能限制。
回退
在熔断或者限流发生的时候,应用程序的后续处理逻辑是什么?回退是系统的弹性恢复能力,常见的处理策略有,直接抛出异常,也称快速失败(Fail Fast),也可以返回空值或缺省值,还可以返回备份数据,如果主服务熔断了,可以从备份服务获取数据。
容错理念
- 凡是依赖都可能会失败
- 凡是资源(CPU/Memory/Threads/Queue)都有限制
- 网络并不可靠
- 延迟是应用稳定性杀手
断路器模式
断路器模式源于 Martin Fowler 的 Circuit Breaker 一文。“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
状态机模式
断路器的实现采用状态机模式,它有 3 种不同的状态:关闭、打开和半打开。
- 关闭:当一切正常时,断路器保持闭合状态,所有调用都能访问到服务。当故障数超过预定阈值时,断路器跳闸,并进入打开状态。
- 打开:断路器在不执行该服务的情况下为调用返回错误。
- 半开:超时后,断路器切换到半开状态,以测试问题是否仍然存在。如果在这种半开状态下单个调用失败,则断路器再次打开。如果成功,则断路器重置回正常关闭状态。
舱壁隔离模式
舱壁隔离模式,顾名思义,该模式像舱壁一样对资源或失败单元进行隔离,如果一个船舱破了进水,只损失一个船舱,其它船舱可以不受影响 。线程隔离就是舱壁隔离模式的一个例子,假定一个应用程序 A 调用了 Svc1/Svc2/Svc3 三个服务,且部署 A 的容器一共有 120 个工作线程,采用线程隔离机制,可以给对 Svc1/Svc2/Svc3 的调用各分配 40 个线程,当 Svc2 慢了,给 Svc2 分配的 40 个线程因慢而阻塞并最终耗尽,线程隔离可以保证给 Svc1/Svc3 分配的 80 个线程可以不受影响,如果没有这种隔离机制,当 Svc2 慢的时候,120 个工作线程会很快全部被对 Svc2 的调用吃光,整个应用程序会全部慢下来。
线程池隔离与信号量隔离
概念
- 线程池隔离:每次都开启一个单独线程运行。它的隔离是通过线程池,即每个隔离粒度都是个线程池,互相不干扰。
- 信号量隔离:每次调用线程,当前请求通过计数信号量进行限制,当信号大于了最大请求数(maxConcurrentRequests)时,进行限制。
对比
特性对比:
线程池隔离 | 信号量隔离 | |
---|---|---|
隔离原理 | 每个服务单独用线程池 | 通过信号量的计数器 |
是否支持熔断 | 支持,当线程池到达MaxSize后,再请求会触发fallback接口进行熔断 | 支持,当信号量达到maxConcurrentRequest后,再请求会触发fallback |
是否支持超时 | 支持,可直接返回 | 不支持,如果阻塞,只能通过调用协议 |
是否支持异步调用 | 可以是异步,也可以是同步。看调用的方法 | 同步调用,不支持异步 |
资源消耗 | 大,大量线程的上下文切换,容易造成机器负载高 | 小,只是个计数器 |
优缺点对比:
优点 | 不足 | 适用 | |
---|---|---|---|
线程池隔离 | 支持任务排队和超时 支持异步调用 |
线程调用会产生额外的开销 | 不受信客户 有限扇出 |
信号量隔离 | 轻量,无额外开销 | 不支持任务排队和主动超时 不支持异步调用 |
受信客户 高扇出(网关) 高频高速调用(cache) |
案例
线程池隔离案例:
主流开源断路器概览
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 | 基于异常比率、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于 RxJava) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
流量整形 | 支持预热模式、匀速器模式、预热排队模式 | 不支持 | 简单的 Rate Limiter 模式 |
系统自适应保护 | 支持 | 不支持 | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、查看秒级监控、机器发现等 | 简单的监控查看 | 不提供控制台,可对接其它监控系统 |