一 超时机制、断路器模式简介
1.1 背景
假设服务提供者响应非常缓慢,那么消费者对提供者的请求线程就会被等待,知道服务返回,在高并发高负载的场景下,如果不做任何处理,这种问题很有可能造成所有处理用户请求的线程的资源耗竭,而不能响应用户进一步请求。
如果服务消费者又是另外服务的提供者,那么有可能产生级联反应,导致其它的下一级服务消费者不可用,最终产生雪崩效应。
1.2 解决方案
1.2.1 超时机制
通过网络请求其他服务时,都设置超时。正常情况下,一个远程调用,即使毫秒就返回了。
当依赖的服务不可用的时候,或者因为网络问题,响应时间会变得很长(几十秒)。而通常情况下,一次远程调用对应了一个线程或者进程,如果响应太慢,那这个线程、进程就会得不到释放。而线程和进程是系统资源,如果大量线程、进程都不被释放,越积越多,服务资源就会被耗尽。所以我们必须设置超时请求。
1.2.2 断路器模式
类似于电短路的时候,跳闸。电流量过大,就会自动断开电路,避免电路升温,烧断电路或者电器。
当依赖的服务有大量超时的时候,再让新的请求去访问已经没有太大意义,只会无谓的消耗现有资源。譬如我们设置了超时时间为1秒,如果短时间有大量请求在1秒内得不到响应,往往意味着异常。此时就没有必要让更多的请求去访问这个依赖了,我们应该使用断路器避免资源浪费。
断路器可以实现快速失败,如果在一段时间内侦测到许多的类似的错误,就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断的尝试执行可能失败的操作,这样应用程序可以继续执行而不用等待修正错误。断路器模式也可以使得应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
断路器模式就类似于那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生几次错误,然后决定使用允许操作继续,否则直接返回错误。
二 Hystrix传播Security Context或者SpringScope
如果你想将本地线程上下文传播到注解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注解的地方。
三 Hysteria Health Indicator 及Metric System
3.1health indicator 健康指标
断路器的状态同样暴露在/health端点上。
{
“hystrix”: {
“openCircuitBreakers”: [
“StoreIntegration::getStoresByLocationLink”
],
“status”: “CIRCUIT_OPEN”
},
“status”: “UP”
}
3.2Hystrix Metrics Stream
使用Hystrix metricsstream需要引入依赖spring-boot-starter-actuator。这会暴露/hystrix.stream作为一个管理端点。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
四 Feign对Hystrix的支持
我们上面所说的熔断机制都是基于在方法上添加@HystrixCommand注解,然后通过属性fallbackMethod实现回退的。然而Feign是以接口形式工作的,他没有方法体,那么前面所述的方式是否依然适合Feign呢?
那么Feign要如何整合Hystrix呢?而且要如何实现Feign的回退呢?
Spring Cloud默认已经为Feign整合了Hystrix,只要Hystrix在项目的classpath中,Feign默认就会用断路器包裹的所有方法。
4.1 为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
HystrixClientFallback
implements
UserFeignClient {
@Override
public
UserfindById(Long
id
) {
User
user
=
new
User();
user
.setId(-1L);
user
.setName(
”
默认用户
”
);
return
user
;
}
}
4.2 禁用单个FeignClient对Hystrix的支持
4.2.1 如果是某些Feign客户端想禁用Hystrix
@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
();
}
}
4.2.2 如果是希望全局禁用
在application.yml中添加feign.hystrix.enabled=false即可
4.3 Feign使用fallbackFactory的属性打印fallback异常
如果有时候我们需要知道发生了什么异常,或者造成回退的原因是什么,我们应该怎么做呢?
我们可以在FeignClient注解上属性加上fallbackFactory属性
首先,创建一个类
public
class
HystrixClientFallbackFactory
implements
FallbackFactory<UserFeignClient>{
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
class
HystrixClientFallbackFactory
implements
FallbackFactory<UserFeignClient>{
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的监控
5.1 指标化监控
处理实现容错外,Hystrix还提供了近乎实时的监控。HystrixCommand和HystrixObservableCommand在执行时,会生成执行结果和运行指标,比如每秒执行的请求数,成功数等,这些监控数据在对分析应用时很有用。
Spring-cloud-starter-hystrix已包含该模块,在此基础上,只需为项目添加spring-boot-starter-actuator,就可以使用/hystrix.stream端点获得Hystrix的监控信息了。
然后就可以访问了/hystrix.stream
对于Feign项目的Hystrix监控,我们需要在启动类上加上@Enable-
CircuitBreaker,这样就可以使用/hystrix.stream
5.2 可视化监控(HystrixDashboard)
前面的方式我们很难通过肉眼迅速看出当前系统运行状态,因为是以文字演示的,使用Hystrix 仪表盘,我们一眼就可以看出当前的运行状态,让监控数据可视化,图形化。
首先:添加依赖
<
dependency
>
<
groupId
>
org.springframework.cloud
</
groupId
>
<
artifactId
>
spring-cloud-starter-
hystrix
–
dashboard
</
artifactId
>
</
dependency
>
其次: 在启动类上添加@EnableHystrixDashboard,我么修改端口为8030
@SpringBootApplication
@EnableHystrixDashboard
public
class
MovieServiceHystrixDashBoardRunner {
@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
六 Turbine
前面我们已经知道,/hystrix.stream端点用于监控单个微服务实例,但是微服务架构体系中一般会包含若干个微服务,在生产环境中每一个微服务都可能会集群部署的,监控单个实例的话,就需要在Hystrix Dashboard上切换想要监控的地址,这显示是很不方便的,怎么办呢?
6.1Turbine简介
Turbine是一个聚合Hystrix监控数据的工具,他可以将相关/hystrix.stream端点的数据聚合到一个组合的/turbine.stream中,从而让集群监控的更加方便。
6.2 使用turbine监控多个微服务
# 添加依赖
<
dependency
>
<
groupId
>
org.springframework.cloud
</
groupId
>
<
artifactId
>
spring-cloud-starter-
hystrix
</
artifactId
>
</
dependency
>
<
dependency
>
<
groupId
>
org.springframework.cloud
</
groupId
>
<
artifactId
>
spring-cloud-starter-
turbine
</
artifactId
>
</
dependency
>
# 启动类添加注解@EnableTurbine
@SpringBootApplication
@EnableTurbine
public
class
TurbineHystrixApplication {
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可视化展示数据