(五)springcloud 断路器-Spring Cloud Netflix Hystrix
较低级别的服务中的服务故障可能导致级联故障一直到用户。 当对特定服务的调用超过circuitBreaker.requestVolumeThreshold(默认值:20个请求)且失败百分比大于circuit.rolllingStats.timeInMilliseconds定义的滚动窗口中的circuitBreaker.errorThresholdPercentage(默认值:> 50%)时(默认值:10秒)
设计原则:
- 防止单个服务的故障,耗尽整个系统服务的容器(比如tomcat)的线程资源,避免分布式环境里大量级联失败。通过第三方客户端访问(通常是通过网络)依赖服务出现失败、拒绝、超时或短路时执行回退逻辑
- 用快速失败代替排队(每个依赖服务维护一个小的线程池或信号量,当线程池满或信号量满,会立即拒绝服务而不会排队等待)和优雅的服务降级;当依赖服务失效后又恢复正常,快速恢复
- 提供接近实时的监控和警报,从而能够快速发现故障和修复。监控信息包括请求成功,失败(客户端抛出的异常),超时和线程拒绝。如果访问依赖服务的错误百分比超过阈值,断路器会跳闸,此时服务会在一段时间内停止对特定服务的所有请求
- 将所有请求外部系统(或请求依赖服务)封装到HystrixCommand或HystrixObservableCommand对象中,然后这些请求在一个独立的线程中执行。使用隔离技术来限制任何一个依赖的失败对系统的影响。每个依赖服务维护一个小的线程池(或信号量),当线程池满或信号量满,会立即拒绝服务而不会排队等待
功能特性:
- 请求熔断: 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
- 服务降级:Fallback相当于是降级操作. 对于查询操作, 可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存
- 依赖隔离(采用舱壁模式,Docker就是舱壁模式的一种):在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池
- 请求缓存
- 请求合并
Ribbon集成断路器
1、基于之前的服务,引入Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2、开启断路器功能
@EnableCircuitBreaker:加载断路器配置
@EnableCircuitBreaker
也可以使用@SpringCloudApplication注解:三合一
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
3、创建fallback方法,指定为某个接口的降级
@SpringCloudApplication
public class ConsumerRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerRibbonApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@RestController
@RequestMapping("/api/v1/consumer")
class ProviderController {
private static final String SERVICE_NAME = "provider-server";
private static final String GET_PORT = "/api/v1/provider/port";
@Resource
private RestTemplate restTemplate;
@GetMapping
@HystrixCommand(fallbackMethod = "consumerFallback")
public String consumer() {
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://" + SERVICE_NAME + GET_PORT, String.class, (Object) null);
return forEntity.getBody();
}
public String consumerFallback() {
return "provider error";
}
}
}
hystrix:配置
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 60000
shareSecurityContext: true
验证:
访问消费者:http://localhost:3000/api/v1/consumer
获得响应:2000
Feign集成断路器
引入依赖和开启配置和Ribbon一样
1、在@FeignClient上声明
@FeignClient(name = "provider-server", fallback = ProviderClientFallback.class)
@RequestMapping("/api/v1/provider")
public interface ProviderClient {
@GetMapping("/port")
String port();
}
2、开启配置feign.hystrix.enabled=true
server:
port: 3001
spring:
application:
name: consumer-server-feign
profiles:
active: dev
cloud:
config:
label: master
profile: ${spring.profiles.active}
discovery:
service-id: config-server
enabled: true
feign:
hystrix:
enabled: true
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/
3、编写fallback类
public class ProviderClientFallback implements ProviderClient {
@Override
public String port() {
return "provider error";
}
}
4、在声明类@FeignClient上指定fallback属性
@FeignClient(name = "provider-server", fallback = ProviderClientFallback.class)
@RequestMapping("/api/v1/provider")
public interface ProviderClient {
@GetMapping("/port")
String port();
}
验证:
访问消费者:http://localhost:3000/api/v1/consumer
获得响应:2000
关闭生产者服务:http://localhost:3000/api/v1/consumer
获得响应:provider error
5、换成指定FallbackFactory,可以获取到异常对象
@FeignClient(name = "provider-server",
fallbackFactory = HystrixClient.HystrixClientFallback.class,
path = "/api/v1/provider")
public interface HystrixClient {
@GetMapping("/port")
String port();
@Component
class HystrixClientFallback implements FallbackFactory<HystrixClient> {
@Override
public HystrixClient create(Throwable throwable) {
return new HystrixClient() {
@Override
public String port() {
return "provider error: " + throwable;
}
};
}
}
}
验证:
访问消费者:http://localhost:3000/api/v1/consumer
获得响应:2000
关闭生产者服务:http://localhost:3000/api/v1/consumer
获得响应:provider error: java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: provider-server
Hystrix Metrics Stream
包含依赖关系spring-boot-starter-actuator
,设置 management.endpoints.web.exposure.include: hystrix.stream
。这样做会将/actuator/hystrix.stream
管理端点公开
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置:
# 全部开放监控端点
management:
endpoints:
web:
exposure:
include: "*"
验证:
GET /actuator/metrics
GET /actuator/metrics/{requiredMetricName}
{[/actuator/metrics/{requiredMetricName}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
{[/actuator/metrics],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
断路器:Hystrix仪表板
1、添加依赖项:spring-cloud-starter-netflix-hystrix-dashboard
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
2、使用@EnableHystrixDashboard注解开启仪表盘
@EnableFeignClients
@SpringCloudApplication
@EnableHystrixDashboard
public class ConsumerFeignApplication {
}
3、访问/hystrix
并将仪表板指向/hystrix.stream
Hystrix客户端应用程序中的单个实例的端点。