hystrix是什么
Hystrix是一个用于处理分布式系统的延迟
和容错
的开源库,在分布式系统中,许多依赖不可以避免的会调用失败,比如超时,异常等。hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常
,这样就保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
我们常见的异常:超时,运行异常,宕机
接下来,我们尝试初步的使用hystrix.
hystrix服务降级(Fallback)
应对超时,运行异常
Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.
服务降级一般作用于客户端,当然也可以在服务端使用,这里我们以客户端的降级为例.
创建 springcloud-consumer-user-feign-hystrix-80模块
**注:这里是和openFegin一起使用,有兴趣的可以看上一篇文章,这里不赘述
**
一、引入依赖
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--自己的工具包,里面就一个实体类-->
<dependency>
<groupId>com.huang</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
二、application.yml
server:
port: 80
eureka:
client:
register-with-eureka: false #不向eureka注册自己
service-url:
#eureka集群
defaultZone: http://localhost:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
#这个必须开启,hystrix才有用
feign:
hystrix:
enabled: true
三、主程序添加**@EnableHystrix**注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix //启动Hystrix断路器
public class UserConsumerFeignHystrix_80 {
public static void main(String[] args) {
SpringApplication.run(UserConsumerFeignHystrix_80.class,args);
}
}
四、FeiginHystrixClientService接口调用服务接口,SPRINGCLOUD-PROVIDER-USER-HYSTRIX是调用服务的名称
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-USER-HYSTRIX")
public interface FeiginHystrixClientService{
@GetMapping(value = "/user/get/{id}")
User get(@PathVariable("id")Long id);
}
五、在需要回调 的方法上增添 @HystrixCommand注解,其中
fallbackMethod是指定回调的方法名称。
@RestController
public class HystrixController {
@Autowired
private FeiginHystrixClientService feignClientService;
@GetMapping("/consumer/user/get/{id}")
@HystrixCommand(fallbackMethod ="fallbackMethod")
public String get(@PathVariable("id")Long id){
//int age=10/0; //故意制造错误
User user= this.feignClientService.get(id);
if (user==null){
throw new RuntimeException("id=>"+id+"不存在");
}
return "方法返回正确"+user.toString();
}
//备选方案
public String fallbackMethod(@PathVariable("id")Long id){
return "系统繁忙,请稍后再试";
}
}
这样一个简单的服务降级就完成了。我们打开int age=10/0;
这段代码,故意制造一个错误,进行测试。可以看到,后台出现报错,但是服务启用了降级的方法!!
服务降级优化-全局和通配降级
– 全局服务降级
应对超时,运行等异常
我们在使用过程中就会发现,按照上述使用,我们如果代码量大,就必须每个方法都编写一个降级的方法,这样的话就大大的提高的代码量,使得代码更加冗余繁重,这样就不是我们想要的。所以,有没有一种方法能过实现全局的配置,让重复的代码只要出现服务调用失败等错误就会统一降级到指定的方法呢!
由此我们采用通用和独享各自分开的方法,避免代码膨胀,合理减少代码量
接下来我们来进行统一的全局配置
,简化代码:
其实也很简单,我们只要在类上添加如下注解@DefaultProperties(defaultFallback = “globalFallbackMethod”)
,globalFallbackMethod就是你配置的全局降级的方法名,同时在需要用全局降级的方法上添加 @HystrixCommand ,如@HystrixCommand加了fallbackMethod就会找属于自己的降级方法!
@RestController
@DefaultProperties(defaultFallback = "globalFallbackMethod")
public class HystrixController {
@Autowired
private FeiginHystrixClientService feignClientService;
@GetMapping("/consumer/user/get/{id}")
@HystrixCommand(fallbackMethod ="fallbackMethod"
,commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String get(@PathVariable("id")Long id){
int age=10/0;//故意制造错误
User user= this.feignClientService.get(id);
if (user==null){
throw new RuntimeException("id=>"+id+"不存在");
}
return "方法返回正确(指定配置)"+user.toString();
}
//测试全局配置
public String list(){
int age=10/0;//故意制造错误
List<User> list=feignClientService.list();
return "方法返回正确(全局配置)"+list.toArray();
}
//备选方案
public String fallbackMethod(@PathVariable("id")Long id){
return "系统繁忙,请稍后再试";
}
//下面是全局fallBack
public String globalFallbackMethod(){
return "global异常信息,请稍后再试!!";
}
}
我们来测试下:
从上图可以看到,我们调用服务分别返回了各自通用和独享分开的方法!
– 通配服务降级
应对服务器宕机或者关闭
如上的全局还有有点耦合度过高,假设我们在调用服务的时候,碰上服务器宕机或者关闭,这时候之前的全局或者局部配置对降级的意义就不大了。所以我们想,如果在客户端调用服务端接口的时候就采用服务降级,当出现上述情况时,是不是可以做到异常转移的效果。
注:本案列服务降级处理时在客户端80完成的,与服务端没有关系(虽然都可以加),只需要为Fegin客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
一。在 Feign 的客户端类上的 @FeignClient 注解中指定 fallback 进行回退(代码如下所示),创建一个 Feign 的客户端类 FeiginHystrixClientService ,为其配置 fallback = FallbackService.class。
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-USER-HYSTRIX",fallback = FallbackService.class)
public interface FeiginHystrixClientService {
@GetMapping(value = "/user/get/{id}")
User get(@PathVariable("id")Long id);
@GetMapping(value = "/user/list")
List<User> list();
}
二、FallbackService 类需要实现 FeiginHystrixClientService 类中所有的方法,返回回退时的内容,代码如下所示。
/*
* 通配降级处理
* */
@Component
public class FallbackService implements FeiginHystrixClientService {
@Override
public User get(Long id) {
return new User().setId(id).setDb_source("没有数据库").setName("存在的名称");
}
@Override
public List<User> list() {
List<User> list=new ArrayList<>();
User user=new User().setName("不存在名称").setDb_source("不存在的数据库");
list.add(user);
return list;
}
}
yml必须开启以下 Hystrix 断路器
feign:
hystrix:
enabled: true
三、停掉所有 服务端,然后访问 http://localhost/consumer/user/list 接口,这个时候服务端 服务是不可用的,必然会触发回退,返回的内容是降级的内容,这证明回退生效了
服务熔断
熔断机制概述:
熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务出错不可以用或者响应时间太长,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路
。
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法返回友好提示。
出现情况:
( 1)服务提供者不可用
(a)硬件故障:硬件损坏造成的服务器主机宕机, 网络硬件故障造成的服务提供者的不可访问
(b)程序Bug:
( c ) 缓存击穿:缓存击穿一般发生在缓存应用重启, 所有缓存被清空时,以及短时间内大量缓存失效时. 大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用
(d)用户大量请求:在秒杀和大促开始前,如果准备不充分,用户发起大量请求也会造成服务提供者的不可用
(2)重试加大流量
(a)用户重试:在服务提供者不可用后, 用户由于忍受不了界面上长时间的等待,而不断刷新页面甚至提交表单
( b)代码逻辑重试: 服务调用端的会存在大量服务异常后的重试逻辑
(3)服务调用者不可用
(a)同步等待造成的资源耗尽:当服务调用者使用同步调用 时, 会产生大量的等待线程占用系统资源. 一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态, 于是服务雪崩效应产生了。
使用:
一般在client客户端实现熔断
springcloud-provider-user-hystrix-8001 服务提供者暴露的接口:
@GetMapping("/user/breaker/{id}")
public String breaker(@PathVariable("id")Integer id){
return id+"------链路正常:";
第一步: 编写接口
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-USER-HYSTRIX",fallback = FallbackService.class)
public interface FeiginHystrixClientService {
/**
* 断路器接口
* @param id id
* @return
*/
@GetMapping("/user/breaker/{id}")
String breaker(@PathVariable("id")Integer id);
}
第二步: contoller层编写熔断方法如下,
@RestController
@DefaultProperties(defaultFallback = "globalFallbackMethod")
public class HystrixController {
@Autowired
private FeiginHystrixClientService feignClientService;
//============服务熔断
@HystrixCommand(fallbackMethod = "circuitBreakerFallback",commandProperties = {
//是否开启断路器
@HystrixProperty(name ="circuitBreaker.enabled" ,value = "true"),
//请求次数
@HystrixProperty(name ="circuitBreaker.requestVolumeThreshold" ,value = "10"),
//时间窗口期
@HystrixProperty(name ="circuitBreaker.sleepWindowInMilliseconds" ,value = "10000"),
//失败率达到多少后跳闸
@HystrixProperty(name ="circuitBreaker.errorThresholdPercentage" ,value = "60"),
})
@GetMapping("/consumer/user/breaker/{id}")
public String circuitBreaker(@PathVariable("id")Integer id){
if (id<0){
throw new RuntimeException("******id不能为负数");
}
return feignClientService.breaker(id)+Thread.currentThread().getName();
}
//熔断降级方法
public String circuitBreakerFallback(@PathVariable("id")Integer id){
return id+"id 不能是负数,请稍后再试,/(ㄒoㄒ)/~~";
}
}
根据代码所示,我们需要在熔断的方法circuitBreaker()上加上注解@HystrixCommand ,注解中的fallbackMethod表示熔断后降级的方法,commandProperties表示命令行为,这里表示在10秒钟之内,请求10次失败率达到60%就会开启断路器的熔断
断路器的三个重要参数:快照时间窗、请求总数阀值、错误百分比阀值。
1:快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗
默认为最近的10秒。
2∶请求总数阀值 : 在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
3∶错误百分比阀值: 当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开。
Command属性主要用来控制HystrixCommand命令的行为,它主要分下面的类别
1 Execution:用来控制HystrixCommand.run()的执行
execution.isolation.strategy:该属性用来设置HystrixCommand.run()执行的隔离策略。默认为THREAD。
execution.isolation.thread.timeoutInMilliseconds:该属性用来配置HystrixCommand执行的超时时间,单位为毫秒。
execution.timeout.enabled:该属性用来配置HystrixCommand.run()的执行是否启用超时时间。默认为true。
execution.isolation.thread.interruptOnTimeout:该属性用来配置当HystrixCommand.run()执行超时的时候是否要它中断。
execution.isolation.thread.interruptOnCancel:该属性用来配置当HystrixCommand.run()执行取消时是否要它中断。
execution.isolation.semaphore.maxConcurrentRequests:当HystrixCommand命令的隔离策略使用信号量时,该属性用来配置信号量的大小。当最大并发请求达到该设置值时,后续的请求将被拒绝。
2 Fallback:用来控制HystrixCommand.getFallback()的执行
fallback.isolation.semaphore.maxConcurrentRequests:该属性用来设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数。当达到最大并发请求时,后续的请求将会被拒绝并抛出异常。
fallback.enabled:该属性用来设置服务降级策略是否启用,默认是true。如果设置为false,当请求失败或者拒绝发生时,将不会调用HystrixCommand.getFallback()来执行服务降级逻辑。
3 Circuit Breaker:用来控制HystrixCircuitBreaker的行为。
circuitBreaker.enabled:确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求。默认为true。
circuitBreaker.requestVolumeThreshold:用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为20的时候,如果滚动时间窗(默认10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
circuitBreaker.sleepWindowInMilliseconds:用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,会将断路器设置为“半开”状态,尝试熔断的请求命令,如果依然时候就将断路器继续设置为“打开”状态,如果成功,就设置为“关闭”状态。
circuitBreaker.errorThresholdPercentage:该属性用来设置断路器打开的错误百分比条件。默认值为50,表示在滚动时间窗中,在请求值超过requestVolumeThreshold阈值的前提下,如果错误请求数百分比超过50,就把断路器设置为“打开”状态,否则就设置为“关闭”状态。
circuitBreaker.forceOpen:该属性默认为false。如果该属性设置为true,断路器将强制进入“打开”状态,它会拒绝所有请求。该属性优于forceClosed属性。
circuitBreaker.forceClosed:该属性默认为false。如果该属性设置为true,断路器强制进入“关闭”状态,它会接收所有请求。如果forceOpen属性为true,该属性不生效。
4 Metrics:该属性与HystrixCommand和HystrixObservableCommand执行种捕获的指标相关。
metrics.rollingStats.timeInMilliseconds:该属性用来设置滚动时间窗的长度,单位为毫秒。该时间用于断路器判断健康度时需要收集信息的持续时间。断路器在收集指标信息时会根据设置的时间窗长度拆分成多个桶来累计各度量值,每个桶记录了一段时间的采集指标。例如,当为默认值10000毫秒时,断路器默认将其分成10个桶,每个桶记录1000毫秒内的指标信息。
metrics.rollingStats.numBuckets:用来设置滚动时间窗统计指标信息时划分“桶”的数量。默认值为10。
metrics.rollingPercentile.enabled:用来设置对命令执行延迟是否使用百分位数来跟踪和计算。默认为true,如果设置为false,那么所有的概要统计都将返回-1。
metrics.rollingPercentile.timeInMilliseconds:用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
metrics.rollingPercentile.numBuckets:用来设置百分位统计滚动窗口中使用桶的数量。
metrics.rollingPercentile.bucketSize:用来设置每个“桶”中保留的最大执行数。
metrics.healthSnapshot.intervalInMilliseconds:用来设置采集影响断路器状态的健康快照的间隔等待时间。
5 Request Context:涉及HystrixCommand使用HystrixRequestContext的设置。
requestCache.enabled:用来配置是否开启请求缓存。
requestLog.enabled:用来设置HystrixCommand的执行和事件是否打印到日志的HystrixRequestLog中。
第三步:启动注册中心springcloud-eureka-7001,服务提供者springcloud-provider-user-hystrix-8001 和客户端springcloud-consumer-user-feign-hystrix-80,启动链接正常后,我们访问http://localhost/consumer/user/breaker/1(也就是circuitBreaker()方法)
为了验证我们的熔断器,进行如下简单的操作:
1.输入正确地址,多次访问,出现如下数据,访问正常
2.之后我们输入错误的地址,熔断的也是先降级–》熔断–》慢慢恢复,初次输入错误的地址,我们看到hystrix进入服务降级。
为了满足熔断条件—》(在10秒钟之内,请求10次失败率达到60%就会开启断路器的熔断),我们疯狂的进行多次错误请求。
3.尝试多次错误请求后,达到熔断的条件,接下来我们访问正确的请求,发现依然是降级的返回值。说明我们的熔断器启动成功了!当我们多次尝试正确的访问,返回的数据慢慢恢复正常了
理解:
如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,而是直接返回fallback,快速释放资源。如果目标服务情况好转则慢慢恢复调用主逻辑。
结论:
Closed:熔断器关闭状态,调用失败次数积累,到了阈值(或一定比例)则启动熔断机制;
Open:熔断器打开状态,此时对下游的调用都内部直接返回错误,不走网络,但设计了一个时钟选项,默认的时钟达到了一定时间(这个时间一般设置成平均故障处理时间,也就是MTTR),到了这个时间,进入半熔断状态;
Half-Open:半熔断状态,允许定量的服务请求,如果调用都成功(或一定比例)则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态;
服务监控
我们已经知道 Hystrix 提供了监控的功能,可以通过hystrix.stream 端点来获取监控数据,但是这些数据是以字符串的形式展现的实际使用中不方便查看。我们可以借助 Hystrix Dashboard 对监控进行图形化展示。
Hystrix Dashboard 是一款针对 Hystrix 进行实时监控的工具,通过 Hystrix Dashboard 可以直观地看到各 Hystrix Command 的请求响应时间,请求成功率等数据。
下面我们单独创建一个项目来集成 dashboard。
创建一个 Maven 项目springcloud-consumer-hystrix-dashboard-9001,在 pom.xml 中添加 dashboard 的依赖,代码如下所示。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!--这个依赖是必须的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
创建启动类,在启动类上添加 @EnableHystrixDashboard 注解,代码如下所示。
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
在属性配置文件中只需要配置服务名称和服务端口:
server:
port: 9001
spring:
application:
name: hystrix-dashboard
然后启动服务,访问 http://localhost:9001/hystrix 就可以看到 dashboard 的主页,如图 所示。
接下来,我们尝试对客户端springcloud-consumer-user-feign-hystrix-80 进行监控。所有的代码如上述服务熔断和降级所示,未做改变。
切记必须加以下依赖:
<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>
除此之外还需要一段配置:在你需要监控的主程序下,加入以下的配置
SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class UserConsumerFeignHystrix_80 {
public static void main(String[] args) {
SpringApplication.run(UserConsumerFeignHystrix_80.class,args);
}
/*
* 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
* ServletRegistrationBean因为springcloud的默认路径不是"/hystrix.stream"
* 只要在自己的项目里配置下面的servlet就可以了
* @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;
}
}
之后启动项目,注册中心,服务提供者,客户端,以及刚创建的springcloud-consumer-hystrix-dashboard-9001。
在主页中有 3 个地方需要我们填写,第一行是监控的 stream 地址,也就是将之前文字监控信息的地址输入到第一个文本框中。第二行的 Delay 是时间,表示用多少毫秒同步一次监控信息,Title 是标题,这个可以随便填写,如图 所示。
输入完成后就可以点击 Monitor Stream 按钮以图形化的方式查看监控的数据了,如图所示。
这里我们可以访问下之前完成的服务熔断的方法(不了解的可以往回看):http://localhost/consumer/user/breaker/1 和 http://localhost/consumer/user/breaker/-1 ,每个重复点击多次,就可以看到监控页面的变化了。
图形页面说明:
实心圆共两种含义。他通过颜色的变化代表实例的健康程度。它的健康度从绿色<黄色<橙色<红色递减
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大,所以通过该实心圆的展示可以在大量的实例钟快速的发现故障实例和高压力实例
曲线::用来记录2分钟内流量的相对变化,可以通过它来观察流量的上升和下降趋势。
七色
左边的七种颜色数字对应右上角文字说明
如果你感兴趣可以继续学习:
springcloud(六) --GateWay服务网关介绍和使用
本文部分参考:http://c.biancheng.net/view/5365.html
https://www.cnblogs.com/yawen/p/6655352.html