Spring Cloud入门实战(四) Hystrix--实现微服务的容错机制
雪崩效应微服务架构中,系统通常包括多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,服务间的依赖就难免会存在。而每个微服务都不能保证自己时时刻刻百分百可用。当一个微服务挂掉之后,其他微服务调用这个挂掉微服务的都不可用了。把这种基础服务故障导致级联故障的现象称为雪崩效应。简单点来说就是 C调用B ,B调用A ,然后A出了故障,导致B处于等待状态,然后C也调不到B。这样的级联故..
雪崩效应
微服务架构中,系统通常包括多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,服务间的依赖就难免会存在。而每个微服务都不能保证自己时时刻刻百分百可用。当一个微服务挂掉之后,其他微服务调用这个挂掉微服务的都不可用了。把这种基础服务故障导致级联故障的现象称为雪崩效应。
简单点来说就是 C调用B ,B调用A ,然后A出了故障,导致B处于等待状态,然后C也调不到B。这样的级联故障导致整个系统不可用。
解决方案(容错)
要想防止雪崩效应,就要一个强大的容错机制。容错机制一般需要实现以下两点。
- 为网络请求设置超时:为每个网络请求设置超时时间,超时不再等待,让资源尽快释放。
- 使用断路器模式:断路器就相当于家里的自动跳闸的。当请求错误失败数较多,或者有大量超时的请求,就会自动停止对该微服务的请求。
使用Hystrix 实现容错
Hystrix是一个实现了超时机制和断路器模式的工具类库。用户隔离访问远程系统、服务或者第三方库,防止连级失败,从而提高系统的可用性和容错性。它主要基于以下几点:
- 包裹请求:使用HystrixCommand注解包裹对依赖的调用逻辑,每个命令都在独立的线程中执行。
- 跳闸机制:当某服务的错误率超过了一定的阈值,Hystrix可以自动或者手动跳闸,停止对该服务的访问。
- 资源隔离:Hystrix为每个历来都维护了一个小型的线程池。如果该线程池已满,发往该依赖的请求就会被拒绝,而不是排队等候。从而加速失败的判定。
- 监控: Hystrix可以实时的监控运行指标和配置的变化。
- 回退机制: 当请求失败、超时、被拒绝时,或者当断路器被打开时执行回退的逻辑,回退的逻辑可以自己提供。
- 自我修复: 断路器打开一段时间后,会自动进入半开状态。
将上一节中的Feign项目添加Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
然后在启动类上添加@EnableHystrix
和@EnableCircuitBreaker
注解。为项目启用断路器支持。
然后修改UserController
在方法上添加@HystrixCommand
注解,表示如果这个接口调用失败之后,就执行fallbackMethod
指定的方法。
@RestController
public class UserController {
@Autowired
private UserFeignClient userFeignClient;
@RequestMapping("/user/{id}")
@HystrixCommand(fallbackMethod = "getUserInfoFallBack")
public String getUserInfo(@PathVariable Long id){
return userFeignClient.getUserInfo(id);
}
public String getUserInfoFallBack(Long id){
return "eureka-client 不可用啦!" + id;
}
}
然后不启动eureka-client。让FeignClient找不到服务地址。访问http://localhost:8004/user/1
发现容错已经实现了。
但是这时候并不代表断路器打开不会去请求相关服务了,这时候只是执行了回退方法,默认5秒内失败20次打开断路器。
证实:
在pom文件中引入actuator
的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在yml配置文件中添加配置,显示服务的健康状态。
management:
endpoint:
health:
show-details: always
然后启动服务。访问http://localhost:8004/actuator/health
。
可以看到状态还是UP,断路器处于关闭状态下,还是回去请求别的服务。然后试试连续请求20次以上(5s内)。
可以看到,状态已经是打开了。这样,断路器才是打开状态的。
@HystrixCommand
还可以添加一些属性来配置。例如:
@RequestMapping("/user/{id}")
@HystrixCommand(fallbackMethod = "getUserInfoFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "5000"),
},threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "1"),
@HystrixProperty(name = "maxQueueSize",value = "10")
})
public String getUserInfo(@PathVariable Long id){
return userFeignClient.getUserInfo(id);
}
这些属性的定义可以访问Hystrix官方属性值定义查看。
Hystrix的隔离策略
Hystrix的隔离策略有两种:
- THREAD(线程隔离):使用该方式,HystrixCommand 将会在单独的线程上执行,并发请求受线程池的线程数量影响。
- SEMAPHORE(信号量):这种方式。HystrixCommand将会在调用线程上执行,开销相对较少,并发请求受到信号量个数的闲置。
默认是使用 线程隔离 的,因为这种方式有一个除了网络超时以外的额外保护层。
一般来说,只有当负载非常高的时候,才会选用信号量,因为这种场景下,使用线程隔离的开销比较大。信号量一半仅适用于非网络调用的隔离。
可以使用
@HystrixProperty(name = "execution.isolation.straregy",value = "SEMAPHORE")
来指定隔离策略。
在Feign中使用Hystrix
如果想在FeignClient中实现Hystrix怎么做呢。
新建一个FeignClientFallback
类。
@Component
public class FeignClientFallback implements UserFeignClient {
@Override
public String getUserInfo(Long id) {
return "请求失败了!";
}
}
然后在 UserFeignClient
的@FeignClient
注解上添加fallback = FeignClientFallback.class
属性。
然后在yml配置文件中添加Feign 对 Hystrix的支持。
feign:
hystrix:
enabled: true
这样就成了。访问http://localhost:8004/user/1
检查回退的原因
很多场景下需要检查回退的原因。这就可以使用@FeignClient
的fallbackFactory
属性。
新建一个FeignClientFallbackFactory
类。
@Component
public class FeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
public static final Logger logger = LoggerFactory.getLogger(FeignClientFallbackFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
return new UserFeignClient() {
@Override
public String getUserInfo(Long id) {
logger.info("fallback !! because:",cause);
return "请求失败了。。。。。";
}
};
}
}
修改UserFeignClient
的@FeignClient
属性,去掉fallback
。加上fallbackFactory
。
请求http://localhost:8004/user/1
,发现是对的,走的fallbackFactory
里的方法。
查看控制台。
# Hystrix自带的监控 在启动类加一个方法。
/**
* 低版本(2.0以前)直接启动即可使用 http://ip:port/hystrix.stream 查看监控信息
* 高版本(2.0以后)需要添加本方法方可使用 http://ip:port/hystix.stream 查看监控信息
*
* @return
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
然后访问http://localhost:8004/hystrix.stream
会发现一直在ping。然后访问一下http://localhost:8004/user/1
。
可以看到效果了。
但是这样的数据太难看了。所以接下来说说可视化的监控数据。
Hystrix Dashboard 可视化监控数据
新建一个全新的项目。
pom文件中加入dashboard的依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
启动类上加上@EnableHystrixDashboard
注解。
配置一个端口。
server:
port: 8005
然后访问 http://localhost:8005/hystrix
,可以看到下面的画面。
在地址栏输入 Hystrix的服务端口和监控数据的地址。下面两个随便填。点击 Monitor Stream
就能看到监控的详情。
各项指标的属性如图。也可以访问dashboard wiki查看。
但是这样监控多个就不好了。需要来回切换。
# 使用Turbine 做聚合数据监控 Turbine是一个聚合监控数据的工具,他可以将所有相关的 /hystrix.stream 的数据聚合到一个 /turbine.stream 中。 新建一个项目:pom文件加入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-eureka-client</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yml配置文件如下:
server:
port: 8007
spring:
application:
name: hystrix-turbine
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://user:123456@server1:8000/eureka/,http://user:123456@server2:8002/eureka/
management:
endpoints:
web:
exposure:
include: '*'
turbine:
app-config: ribbon-consumer1,ribbon-consumer2
cluster-name-expression: "'default'"
instanceUrlSuffix: hystrix.stream
启动类上添加注解。@EnableTurbine
和@EnableDiscoveryClient
然后访问 dashboard项目也就是 http://localhost:8005/hystrix
。
输入 localhost:8007/turbine.stream
,点击
就能看到监控了多个了。
使用消息中间件收集数据
安装好一个消息中间件,我安装的是RabbitMQ。
装好之后启动、访问http://localhost:15672/
修改ribbon-consumer1
和ribbon-consumer2
两个服务。
pom文件中加入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
配置文件中加入:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
然后改一下 hystrix-turbine
,pom文件中加入。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
删除 netflix-turbine
依赖,不删除的话,启动会报错。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
yml文件中同样也添加rabbitmq配置。
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
删除掉下面的配置,不删除也会报错。
turbine:
app-config: ribbon-consumer1,ribbon-consumer2
cluster-name-expression: "'default'"
instanceUrlSuffix: hystrix.stream
启动上的@EnableTurbine
注解换成@EnableTurbineStream
。
然后启动两个ribbon项目,启动eureka-server。启动turbine。访问http://localhost:8007/
即turbine
的端口。
这时候会还是看不到数据。他会一直这样。
去mq控制台。
然后请求一遍原来的服务。再访问http://localhost:8007/
。就能看到数据了。
总结一下,坑挺多的。
更多推荐
所有评论(0)