二、服务容错
1.基础知识
-
雪崩效应
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B
服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服
务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,
导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难
性的严重后果,这就是服务故障的“雪崩”效应。 -
雪崩的根本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
-
服务隔离
顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。
当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体
的系统服务。解决方案:通过线程池的方式实现服务隔离
-
熔断降级
熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压
力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这
种牺牲局部,保全整体的措施就叫做熔断。 -
服务限流
限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说
系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流
量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者者部分拒绝解决等等。
2.Hystrix
简介
Hystrix 是由Netflix开源的一个延迟和容错库
包裹请求:使用 HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用
了设计模式中的“命令模式”。
rest实现服务熔断(restTemplate)
-
配置依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
开启熔断
@EnableCircuitBreaker //开启熔断器 //@SpringBootApplication @SpringCloudApplication public class OrderApplication { //创建RestTemplate对象 @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
-
配置熔断降级业务逻辑
@DefaultProperties(defaultProperties=“全局降级方法名称”)
@HystrixCommand(fallbackMethod=“降级方法名称”)
@RestController @RequestMapping("/order") /** * @DefaultProperties : 指定此接口中公共的熔断设置 * 如果过在@DefaultProperties指定了公共的降级方法 * 在@HystrixCommand不需要单独指定了 */ //@DefaultProperties(defaultFallback = "defaultFallBack") public class OrderController { @Autowired private RestTemplate restTemplate; /** * 使用注解配置熔断保护 * fallbackmethod : 配置熔断之后的降级方法 */ @HystrixCommand(fallbackMethod = "orderFallBack") @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { if(id != 1) { throw new RuntimeException("服务器异常"); } return restTemplate.getForObject("http://service-product/product/1",Product.class); } /** * 指定统一的降级方法 * * 参数 : 没有参数 */ public Product defaultFallBack() { Product product = new Product(); product.setProductName("触发统一的降级方法"); return product; } /** * 降级方法 * 和需要收到保护的方法的返回值一致 * 方法参数一致 */ public Product orderFallBack(Long id) { Product product = new Product(); product.setProductName("触发降级方法"); return product; } }
注意事项:熔断的降级逻辑方法必须跟正常逻辑方法保证: 相同的参数列表和返回值声明
-
超时设置
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 2000
-
隔离方式设置
hystrix: command: default: execution: isolation: strategy: ExecutionIsolationStrategy.SEMAPHORE #信号量隔离 strategy: # ExecutionIsolationStrategy.THREAD 线程池隔离
-
熔断隔离参数配置
hystrix: command: default: execution: isolation: strategy: ExecutionIsolationStrategy.SEMAPHORE #信号量隔离 strategy: # ExecutionIsolationStrategy.THREAD 线程池隔离 thread: timeoutInMilliseconds: 3000 #默认的连接超时时间1秒,若1秒没有返回数据,自动的触发降级逻辑 circuitBreaker: requestVolumeThreshold: 5 #触发熔断的最小请求次数,默认20 /10秒 sleepWindowInMilliseconds: 10000 #熔断多少秒后去尝试请求 默认 5 打开状态的时间 errorThresholdPercentage: 50 #触发熔断的失败请求最小占比,默认50%
Feign 实现服务熔断
-
在Fegin中开启hystrix
在Feign中已经内置了hystrix,但是默认是关闭的需要在工程的 application.yml 中开启对hystrix的
支持feign: client: config: service-product: #需要调用的服务名称 loggerLevel: FULL #开启对hystrix的支持 hystrix: enabled: true
-
配置FeignClient接口的实现类,在接口上配置添加降级方法的实现类名称
@Component public class ProductFeignClientCallBack implements ProductFeignClient { /** * 熔断降级的方法 */ public Product findById(Long id) { Product product = new Product(); product.setProductName("feign调用触发熔断降级方法"); return product; } }
/** * 声明需要调用的微服务名称 * @FeignClient * * name : 服务提供者的名称 * * fallback : 配置熔断发生降级方法 * 实现类 * @FeignClient 注解中以fallback声明降级方法 */ @FeignClient(name="service-product",fallback = ProductFeignClientCallBack.class) public interface ProductFeignClient { /** * 配置需要调用的微服务接口 */ @RequestMapping(value="/product/{id}",method = RequestMethod.GET) public Product findById(@PathVariable("id") Long id); }
Hystrix DashBoard监控
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
-
添加EnableHystrixDashboard 注解
@EnableHystrixDashboard public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
-
暴露所有actuator监控的端点
management: endpoints: web: exposure: include: '*'
-
访问 localhost:端口号/hystrix
输入监控断点展示监控的详细数据http:/localhost:9001/actuator/hystrix.stream
断路器聚合监控Turbine
-
创建工程 shop_hystrix_turbine 引入相关坐标
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
-
配置多个微服务的hystrix监控
server: port: 8031 spring: application: name: microservice-hystrix-turbine eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true turbine: # 要监控的微服务列表,多个用,分隔 appConfig: shop-service-order clusterNameExpression: "'default'"
eureka 相关配置 : 指定注册中心地址
turbine 相关配置:指定需要监控的微服务列表
turbine会自动的从注册中心中获取需要监控的微服务,并聚合所有微服务中的 /hystrix.stream 数据 -
配置启动类
@SpringBootApplication @EnableTurbine @EnableHystrixDashboard public class TurbineServerApplication { public static void main(String[] args) { SpringApplication.run(TurbineServerApplication.class, args); } }
熔断器的三个状态
熔断器有三个状态 CLOSED 、 OPEN 、 HALF_OPEN 熔断器默认关闭状态,当触发熔断后状态变更为
OPEN ,在等待到指定的时间,Hystrix会放请求检测服务是否开启,这期间熔断器会变为 HALF_OPEN 半
开启状态,熔断探测服务可用则继续变更为 CLOSED 关闭熔断器。
Closed :关闭状态(断路器关闭),所有请求都正常访问。代理类维护了最近调用失败的次数,
如果某次调用失败,则使失败次数加1。如果最近失败次数超过了在给定时间内允许失败的阈值,
则代理类切换到断开(Open)状态。此时代理开启了一个超时时钟,当该时钟超过了该时间,则切
换到半断开(Half-Open)状态。该超时时间的设定是给了系统一次机会来修正导致调用失败的错
误。
Open :打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间
内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求
次数最少不低于20次。
Half Open :半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路
器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则
继续保持打开,再次进行5秒休眠计时。
-
修改熔断策略
hystrix: command: default: execution: isolation: strategy: ExecutionIsolationStrategy.SEMAPHORE #信号量隔离 strategy: # ExecutionIsolationStrategy.THREAD 线程池隔离 thread: timeoutInMilliseconds: 3000 #默认的连接超时时间1秒,若1秒没有返回数据,自动的触发降级逻辑 circuitBreaker: requestVolumeThreshold: 5 #触发熔断的最小请求次数,默认20 /10秒 sleepWindowInMilliseconds: 10000 #熔断多少秒后去尝试请求 默认 5 打开状态的时间 errorThresholdPercentage: 50 #触发熔断的失败请求最小占比,默认50%
requestVolumeThreshold :触发熔断的最小请求次数,默认20
errorThresholdPercentage :触发熔断的失败请求最小占比,默认50%
sleepWindowInMilliseconds :熔断多少秒后去尝试请求
隔离策略
-
线程池隔离策略: 使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超
时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资
源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处
理) -
信号量隔离策略: 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判
断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请
求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发
流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服
务)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TrKfaPYM-1602131821264)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1602074642196.png)]
3.Sentinel
获取 Sentinel 控制台
您可以从官方网站中 下载最新版本的控制台 jar 包,下载地址如下:
https://github.com/alibaba/Sentinel/releases/download/1.6.3/sentinel-dashboard-1.6.3.jar
- 使用如下命令启动控制台:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -
Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
- 引入JAR包
客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信。可以通过 pom.xml 引入 JAR 包:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
-
配置启动参数
spring: cloud: sentinel: transport: dashboard: localhost:8080
这里的 spring.cloud.sentinel.transport.dashboard 配置控制台的请求路径。
-
父工程引入 alibaba实现的SpringCloud
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency>
子工程中引入 sentinel
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
配置熔断降级方法
@GetMapping("/buy/{id}")
@SentinelResource(value="order",blockHandler = "orderblockHandler",fallback
= "orderfallback")
public Product order(@PathVariable Long id) {
return restTemplate.getForObject("http://shop-service-
product/product/1", Product.class);
}
//降级方法
public Product orderblockHandler(Long id) {
Product product = new Product();
product.setId(-1l);
product.setProductName("触发熔断降级方法");
return product;
}
//降级方法
public Product orderfallback(Long id) {
Product product = new Product();
product.setId(-1l);
product.setProductName("触发抛出异常方法");
return product;
}
在需要被保护的方法上使用 @SentinelResource注解进行熔断配置。与Hystrix不同的是,Sentinel对抛
出异常和熔断降级做了更加细致的区分,通过 blockHandler 指定熔断降级方法,通过 fallback 指定
触发异常执行的降级方法。
Rest实现熔断
/**
* sentinel支持对restTemplate的服务调用使用sentinel方法.在构造
* RestTemplate对象的时候,只需要加载@SentinelRestTemplate即可
*
* 资源名:
* httpmethod:schema://host:port/path :协议、主机、端口和路径
* httpmethod:schema://host:port :协议、主机和端口
*
* @SentinelRestTemplate:
* 异常降级
* fallback : 降级方法
* fallbackClass : 降级配置类
* 限流熔断
* blockHandler
* blockHandlerClass
*/
@LoadBalanced
@Bean
@SentinelRestTemplate(fallbackClass = ExceptionUtils.class,fallback = "handleFallback",
blockHandler = "handleBlock" ,blockHandlerClass = ExceptionUtils.class
)
public RestTemplate restTemplate() {
return new RestTemplate();
}
@SentinelRestTemplate 注解的属性支持限流( blockHandler , blockHandlerClass )和降级( fallback , fallbackClass )的处理。
其中 blockHandler 或 fallback 属性对应的方法必须是对应 blockHandlerClass 或
fallbackClass 属性中的静态方法。
该方法的参数跟返回值跟org.springframework.http.client.ClientHttpRequestInterceptor#interceptor 方法一
致,其中参数多出了一个 BlockException 参数用于获取 Sentinel 捕获的异常。
public class ExceptionUtils {
/**
* 静态方法
* 返回值: SentinelClientHttpResponse
* 参数 : request , byte[] , clientRquestExcetion , blockException
*/
//限流熔断业务逻辑
public static SentinelClientHttpResponse handleBlock(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
return new SentinelClientHttpResponse("abc");
}
//异常降级业务逻辑
public static SentinelClientHttpResponse handleFallback(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution, BlockException ex) {
return new SentinelClientHttpResponse("def");
}
}
Sentinel RestTemplate 限流的资源规则提供两种粒度:
httpmethod:schema://host:port/path :协议、主机、端口和路径
httpmethod:schema://host:port :协议、主机和端口
Feign实现熔断
-
引入依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
开启sentinel 支持
#激活sentinel的支持 feign: sentinel: enabled: true
-
配置FeignClient及熔断方法
两种粒度:
httpmethod:schema://host:port/path :协议、主机、端口和路径
httpmethod:schema://host:port :协议、主机和端口
Feign实现熔断
-
引入依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
开启sentinel 支持
#激活sentinel的支持 feign: sentinel: enabled: true
-
配置FeignClient及熔断方法
和使用Hystrix的方式基本一致,需要配置FeignClient接口以及通过 fallback 指定熔断降级方法