1.1 背景
假设服务提供者响应非常缓慢,那么消费者对提供者的请求线程就会被等待,知道服务返回,在高并发高负载的场景下,如果不做任何处理,这种问题很有可能造成所有处理用户请求的线程的资源耗竭,而不能响应用户进一步请求。
如果服务消费者又是另外服务的提供者,那么有可能产生级联反应,导致其它的下一级服务消费者不可用,最终产生雪崩效应。
通过网络请求其他服务时,都设置超时。正常情况下,一个远程调用,即使毫秒就返回了。
当依赖的服务不可用的时候,或者因为网络问题,响应时间会变得很长(几十秒)。而通常情况下,一次远程调用对应了一个线程或者进程,如果响应太慢,那这个线程、进程就会得不到释放。而线程和进程是系统资源,如果大量线程、进程都不被释放,越积越多,服务资源就会被耗尽。所以我们必须设置超时请求。
类似于电短路的时候,跳闸。电流量过大,就会自动断开电路,避免电路升温,烧断电路或者电器。
当依赖的服务有大量超时的时候,再让新的请求去访问已经没有太大意义,只会无谓的消耗现有资源。譬如我们设置了超时时间为1秒,如果短时间有大量请求在1秒内得不到响应,往往意味着异常。此时就没有必要让更多的请求去访问这个依赖了,我们应该使用断路器避免资源浪费。
断路器可以实现快速失败,如果在一段时间内侦测到许多的类似的错误,就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断的尝试执行可能失败的操作,这样应用程序可以继续执行而不用等待修正错误。断路器模式也可以使得应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
断路器模式就类似于那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生几次错误,然后决定使用允许操作继续,否则直接返回错误。
如果你想将本地线程上下文传播到注解HystrixCommand中,默认的声明是不会起到这个作用的,因为它是在一个线程中启动的。你可以选择让Hystrix使用同一个线程,通过一些配置或直接写在注解上,通过使用isolation strategy属性。
比如:
@HystrixCommand(fallbackMethod ="stubMyService",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy",value="SEMAPHORE")
}
)
同样的方式适用于如果你用@SessionScope 或者 @RequestScope。你应该知道什么时候去做这件事因为有些运行时异常报找不到scoped上下文。
你也可以选择设置hystrix.shareSecurityContext 属性为true。这样做会自动配置一个Hystrix 并发策略插件钩子,然后会将SecurityContext从你当前主线程
传输到一个使用Hystrix Command注解的地方。
断路器的状态同样暴露在/health端点上。
{
"hystrix": {
"openCircuitBreakers": [
"StoreIntegration::getStoresByLocationLink"
],
"status": "CIRCUIT_OPEN"
},
"status": "UP"
}
使用Hystrix metricsstream需要引入依赖spring-boot-starter-actuator。这会暴露/hystrix.stream作为一个管理端点。
我们上面所说的熔断机制都是基于在方法上添加@HystrixCommand注解,然后通过属性fallbackMethod实现回退的。然而Feign是以接口形式工作的,他没有方法体,那么前面所述的方式是否依然适合Feign呢?
那么Feign要如何整合Hystrix呢?而且要如何实现Feign的回退呢?
Spring Cloud默认已经为Feign整合了Hystrix,只要Hystrix在项目的classpath中,Feign默认就会用断路器包裹的所有方法。
首先添加fallback属性为FeignClient,并且指定一个class,这个class实现了接口
@FeignClient(name="microservice-provider-user",configuration=Configuration1.class,
fallback=HystrixClientFallback.class)
public interface UserFeignClient {
@RequestLine("GET /user/{id}")
public User findById(@Param("id") Long id);
}
创建实现了Feign接口的类,并且实现fallback方法:
@Component
public class HystrixClientFallbackimplements UserFeignClient {
@Override
public UserfindById(Long id) {
User user = new User();
user.setId(-1L);
user.setName("默认用户");
return user;
}
}
@FeignClient(name="microservice-provider-user",configuration=Configuration1.class,
fallback=HystrixClientFallback.class)
public interface UserFeignClient {
@RequestLine("GET /user/{id}")
public User findById(@Param("id") Long id);
}
@Configuration
public class Configuration1 {
@Bean
@Scope("prototype")
public Feign.BuilderfeignBuilder() {
return Feign.builder();
}
}
在application.yml中添加feign.hystrix.enabled=false即可
如果有时候我们需要知道发生了什么异常,或者造成回退的原因是什么,我们应该怎么做呢?
我们可以在FeignClient注解上属性加上fallbackFactory属性
首先,创建一个类
public classHystrixClientFallbackFactory
implementsFallbackFactory
private static final Logger logger = LoggerFactory.getLogger
(HystrixClientFallbackFactory.class);
@Override
public UserFeignClientcreate(Throwable cause) {
/**
* ----日志最好放在各个fallback方法中,而不要直接放在create方法中
* ----否则在启动的时候,就会打印日志
*/
return new UserFeignClient(){
@Override
public UserfindById(Long id) {
HystrixClientFallbackFactory.logger.info
("fallback,causedby: "+cause);
User user = new User();
user.setId(-1L);
user.setName("默认用户");
return user;
}
};
}
}
然后:在Feign客户端的注解@FeignClient上加上属性
fallbackFactory
@FeignClient(name="microservice-provider-user",configuration=Configuration1.class,
fallbackFactory=HystrixClientFallbackFactory.class)
public interface UserFeignClient {
@RequestLine("GET /user/{id}")
public User findById(@Param("id") Long id);
}
Fallbackfactory属性还有其他用途, 我们可以让不同的异常返回不同的回退结果,从而使得Feign的回退更加灵活。例如:
public classHystrixClientFallbackFactory
implementsFallbackFactory
private static final Logger logger = LoggerFactory.getLogger
(HystrixClientFallbackFactory.class);
@Override
public UserFeignClientcreate(Throwable cause) {
/**
* ----日志最好放在各个fallback方法中,而不要直接放在create方法中
* ----否则在启动的时候,就会打印日志
*/
return new UserFeignClient(){
@Override
public UserfindById(Long id) {
HystrixClientFallbackFactory.logger.info
("fallback,causedby: "+cause);
User user = new User();
if (cause instanceof IllegalArgumentException) {
user.setId(-1L);
} else {
user.setId(-2L);
}
user.setName("默认用户");
return user;
}
};
}
}
处理实现容错外,Hystrix还提供了近乎实时的监控。HystrixCommand和HystrixObservableCommand在执行时,会生成执行结果和运行指标,比如每秒执行的请求数,成功数等,这些监控数据在对分析应用时很有用。
Spring-cloud-starter-hystrix已包含该模块,在此基础上,只需为项目添加spring-boot-starter-actuator,就可以使用/hystrix.stream端点获得Hystrix的监控信息了。
然后就可以访问了/hystrix.stream
对于Feign项目的Hystrix监控,我们需要在启动类上加上@Enable-
CircuitBreaker,这样就可以使用/hystrix.stream
前面的方式我们很难通过肉眼迅速看出当前系统运行状态,因为是以文字演示的,使用Hystrix 仪表盘,我们一眼就可以看出当前的运行状态,让监控数据可视化,图形化。
首先:添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
dependency>
其次: 在启动类上添加@EnableHystrixDashboard,我么修改端口为8030
@SpringBootApplication
@EnableHystrixDashboard
public classMovieServiceHystrixDashBoardRunner {
@Bean
@LoadBalanced
public RestTemplaterestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MovieServiceHystrixDashBoardRunner.class, args);
}
}
server:
port: 8030
最后:这样一个简单的Hystrix Board就完成了,我们知道,我们并没有把Hystrix Dashboard注册到Eureka Server上,访问localhost:8030/
Hystrix
前面我们已经知道,/hystrix.stream端点用于监控单个微服务实例,但是微服务架构体系中一般会包含若干个微服务,在生产环境中每一个微服务都可能会集群部署的,监控单个实例的话,就需要在Hystrix Dashboard上切换想要监控的地址,这显示是很不方便的,怎么办呢?
Turbine是一个聚合Hystrix监控数据的工具,他可以将相关/hystrix.stream端点的数据聚合到一个组合的/turbine.stream中,从而让集群监控的更加方便。
# 添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-turbineartifactId>
dependency>
# 启动类添加注解@EnableTurbine
@SpringBootApplication
@EnableTurbine
public classTurbineHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineHystrixApplication.class, args);
}
}
# 修改application.yml配置文件
turbine.aggregator.clusterConfig: 集群名字,多个逗号分割
turbine.appConfig: 微服务名称,多个逗号分割
# 访问
localhost:8031/turbine.stream?cluster=MICROSERVICE-CONSUMER-FEIGN-WITH-HYSTRIX
或者
localhost:8031/turbine.stream?cluster=MICROSERVICE-CONSUMER-RIBBON-WITH-HYSTRIX
如果启动了 Hystrix Dashboard,那么可以在Dashboard可视化展示数据