一、熔断降级限流
1.1 什么是熔断?
-
A 服务调用 B 服务的某个功能,由于网络不稳定问题,或者 B 服务卡机,导致功能耗时超长。如果出现这种情况的次数太多。我们就可以直接将 B 短路了(即 A 不再请求 B 接口),凡是调用 B 的直接返回降级数据,不必等待 B 的超长执行。这样 B 的故障问题,就不会级联影响到 A。
1.2 什么是降级?
-
整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级(停止服务,所有的调用直接返回降级数据)。以此缓解服务器资源的的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的响应。
1.3 什么是限流?
-
对打入服务的请求流量进行控制,使服务能够承担不超过自己能力的流量压力。
1.4 熔断与降级区别
-
相同点: -
为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我。 -
用户最终都是体验到某个功能不可用。 -
不同点(从以下三个角度): -
触发原因:服务熔断一般是某个服务(下游服务)故障引起;而服务降级一般是从整体负荷考虑。 -
管理目标的层次:熔断其实是一个框架级的处理,每个微服务都需要(无层级之分);而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)。 -
实现方式:服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。
二、Sentinel 简介
Sentinel
是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
2.1 基本概念
-
资源:资源是 Sentinel
的关键概念。它可以是Java
应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。只要通过 Sentinel API 定义的代码,就是资源,并能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL
,甚至服务名称作为资源名来标示资源。 -
规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
2.2 组成部分
-
核心库(
Java
客户端):不依赖任何框架/库,能够运行于所有Java
运行时环境,同时对Dubbo
/Spring Cloud
等框架也有较好的支持。 -
控制台(Dashboard):基于
Spring Boot
开发,打包后可以直接运行,不需要额外的Tomcat
等应用容器。
2.3 四大特征
-
丰富的应用场景: Sentinel
承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。 -
完备的实时监控: Sentinel
同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。 -
广泛的开源生态: Sentinel
提供开箱即用的与其它开源框架 / 库的整合模块,例如与Spring Cloud
、Dubbo
、gRPC
的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel
。 -
完善的 SPI 扩展点: Sentinel
提供简单易用、完善的SPI
扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
2.4 主要特性
2.5 相关文档
-
官方手册:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
三、与同类组件 Hystrix 的区别
Spring Cloud
中的Hystrix
是Netflix
开源的一款针对分布式系统延迟和容错的库,其目的是通过添加延迟容忍和容错逻辑,从而控制分布式服务之间的交互。
3.1 功能对比
功能 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 |
实时统计实现 | 滑动窗口(LeapArray ) |
滑动窗口(基于RxJava ) |
动态规则配置 | 支持多种数据源 | 支持多种数据源 |
拓展性 | 多个拓展点 | 插件形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于QPS ,支持基于调用关系的限流 |
有限的支持 |
流量整形 | 支持预热模式、匀速器模式、预热排队模式 | 不支持 |
系统自适应保护 | 支持 | 不支持 |
控制台 | 可配置规则、查看秒级监控、机器发现等 | 简单的监控查看 |
3.2 熔断降级设计理念
除了流量控制以外,降低调用链路中的不稳定资源也是
Sentinel
的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。
-
Sentinel
和Hystrix
的原则是一致的,当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。 -
Hystrix
通过 线程池隔离 的方式,来对依赖(对应Sentinel
中的资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。 -
而 Sentinel
采用了两种手段,一是通过并发线程数进行限制,二是通过响应时间对资源进行降级。 -
Sentinel
通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。 -
Sentinel
还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
3.3 熔断降级策略
-
慢调用比例 ( SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用RT
(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN
状态),若接下来的一个请求响应时间小于设置的慢调用RT
则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 -
异常比例 ( ERROR_RATIO
):当单位统计时长内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN
状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。 -
异常数 ( ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN
状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
3.4 系统自适应保护
-
Sentinel
在得知系统性能之后,可以在低峰期将流量都放进来,高峰期对流量进行限制。
四、Docker 下搭建 Sentinel 控制台
4.1 安装
# 拉取镜像
$ docker pull bladex/sentinel-dashboard:1.6.3
# 运行
$ docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard:1.6.3
# 设置开机自启动
$ docker update sentinel --restart=always
4.2 登录控制台
# 浏览器访问
http://yourIp:8858
# 用户名/密码
sentinel/sentinel
五、SpringBoot 整合 Sentinel
5.1 依赖引入
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--健康监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--远程调用-feign组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
5.2 配置文件
-
application.properties
# 指定控制台地址
spring.cloud.sentinel.transport.dashboard=yourIp:8858
# 与sentinel单独连接的端口(启动该服务,会在应用程序的相应服务器上启动HTTP Server,并且该服务器将与Sentinel dashboard进行交互)
spring.cloud.sentinel.transport.port=8719
# 如果sentinel装在虚拟机,则必须配这个
# spring.cloud.sentinel.transport.client-ip=本机的ip
# 暴露应用的所有信息(包括当前应用的所有规则信息、日志目录、当前实例的 IP,Sentinel Dashboard 地址,Block Page,应用与 Sentinel Dashboard 的心跳频率等等信息)
management.endpoints.web.exposure.include=*
5.3 流控配置
-
当请求进入微服务时,首先会访问 DispatcherServlet
,然后进入Controller
、Service
、Mapper
,这样的一个调用链就叫做簇点链路。簇点链路中被监控的每一个接口就是一个资源。 -
默认情况下 Sentinel
会监控SpringMVC
的每一个端点(Endpoint
,也就是Controller
中的方法),因此SpringMVC
的每一个端点(Endpoint
)就是调用链路中的一个资源。 -
注意:默认所有的流控设置都是保存在内存中,重启后失效。 -
Sentinel
官方推荐在生产架构中使用第三方数据源作为永久存储中心,比如nacos
、apollo
、zookeeper
。因为这几种数据源都是基于push
模式,也就是由规则中心统一推送,Sentinel Client
只需通过注册监听器的方式时刻监听变化,并负责获取配置中心推送的配置并更新到本地,这种方式能更好的保证实时性和一致性,并且各个微服务的限流、降级规则都可以永久存储。
5.4 自定义阻塞响应
当被触发流控规则后,
Sentinel
默认会提示Blocked by Sentinel (flow limiting)
。此时,我们可以优化为自定义的提示信息。
@Component
public class MySentinelExceptionHandler implements BlockExceptionHandler {
/**
* 自定义 sentinel 触发阻塞后的响应信息。
*
* @param request 请求
* @param response 响应
* @param e 阻塞类型
* @throws Exception 异常
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = null;
if (e instanceof FlowException) {
msg = "访问频繁,请稍候再试";
} else if (e instanceof DegradeException) {
msg = "系统降级";
} else if (e instanceof SystemBlockException) {
msg = "系统规则限流或降级";
} else if (e instanceof AuthorityException) {
msg = "授权规则不通过";
} else {
msg = "未知限流降级";
}
// 设置响应信息
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
// 反序列化为json进行输出
new ObjectMapper().writeValue(response.getWriter(), msg);
}
}
5.5 熔断配置
# 调用方feign启用sentinel,当调用出错后实现容错降级
feign.sentinel.enabled=true
-
官方示例Demo:
触发熔断后,调用
Fallback
方法。
@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
String echo(@PathVariable("str") String str);
}
class FeignConfiguration {
@Bean
public EchoServiceFallback echoServiceFallback() {
return new EchoServiceFallback();
}
}
class EchoServiceFallback implements EchoService {
@Override
public String echo(@PathVariable("str") String str) {
return "echo fallback";
}
}
六、Spring Cloud Sleuth
Spring Cloud Sleuth
为SpringCloud
实现了一个分布式链路追踪解决方案,大量借鉴了Dapper
,Zipkin
和HTrace
等链路追踪技术。对于大多数用户而言,
Sleuth
应该是不可见的,并且您与外部系统的所有交互都应自动进行检测。您可以简单地在日志中捕获数据,也可以将其发送到远程收集器服务。
6.1 为什么要链路追踪?
-
微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。由于服务单元数量众多,且一个请求可能需要调用很多个服务,,如果出现了错误和异常,很难去定位。 -
所以微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,方便快速地定位问题。 -
链路追踪组件有 Google
的Dapper
,Twitter
的Zipkin
,以及阿里的Eagleeye
(鹰眼)等,它们都是非常优秀的链路追踪开源组件。 -
我们这里使用 Spring Cloud Sleuth
中集成Zipkin
非常的简单,只需要引入相应的依赖和做相关的配置即可。
七、Zipkin
Zipkin
是Google Dapper
实现。
7.1 简介
-
可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的 REST API
接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源。 -
除了面向开发的 API
接口之外,它也提供了方便的UI
组件帮助我们直观地搜索跟踪信息和分析请求链路明细,比如:可以查询某段时间内各用户请求的处理时间等。 -
它相较于 SkyWalking
更加轻量级,安装也非常简单,是中小规模的微服务项目首选方案。
7.2 基本术语
-
Spring Cloud Sleuth
是对Zipkin
的一个封装,对于Span
、Trace
等信息的生成、接入HTTP Request
,以及向Zipkin Server
发送采集信息等全部自动完成。 -
Span(跨度):基本工作单元,发送一个远程调度任务 就会产生一个 Span,Span 是一个 64 位 ID 唯一标识的,Trace 是用另一个 64 位 ID 唯一标识的,Span 还有其他数据信息,比如摘要、时间戳事件、Span 的 ID、以及进度 ID。
-
Trace(跟踪):一系列 Span 组成的一个树状结构。请求一个微服务系统的 API 接口,这个 API 接口,需要调用多个微服务,调用每个微服务都会产生一个新的 Span,所有由这个请求产生的 Span 组成了这个 Trace。
-
Annotation(标注):用来及时记录一个事件的,一些核心注解用来定义一个请求的开始和结束 。这些注解包括以下:
-
cs
(Client Sent):客户端发送一个请求,这个注解描述了这个Span
的开始。 -
sr
(Server Received):服务端获得请求并准备开始处理它,用 sr 减去 cs 时间戳便可得到网络传输的时间。 -
ss
(Server Sent - 服务端发送响应):该注解表明请求处理的完成(当请求返回客户端),如果 ss 的时间戳减去 sr 时间戳,就可以得到服务器请求的时间。 -
cr
(Client Received - 客户端接收响应):此时 Span 的结束,如果 cr 的时间戳减去 cs 时间戳便可以得到整个请求所消耗的时间。 -
概念图:
7.3 服务搭建
-
docker
下 安装zipkin
$ docker run -d -p 9411:9411 openzipkin/zipkin
-
访问控制台
http://yourIp:9411/
7.4 项目整合
-
依赖引入
<!--zipkin 依赖也同时包含了 sleuth,可以省略 sleuth 的引用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
-
application.properties
# zipkin 服务器的地址
spring.zipkin.base-url=http://yourIp:9411/
# 关闭服务发现,否则 Spring Cloud 会把 zipkin 的 url 当做服务名称
spring.zipkin.discovery-client-enabled=false
# 设置使用 http 的方式传输数据
spring.zipkin.sender.type=web
# 设置抽样采集率为 100% ,默认为 0.1 ,即 10%
spring.sleuth.sampler.probability=1
-
链路追踪图:
文章评论