Hystrix是什么
通过第三方客户端的库来为访问依赖服务时的潜在故障提供保护和控制;
防止在复杂分布式系统中出现级联故障;
快速失败和迅速恢复;
在允许的情况下,提供退路对服务进行优雅降级;
提供近实时的监控、报警和操作控制;
熔断
什么是熔断
通过配置一系列参数条件,例如请求执行时间,达到出发熔断条件后快速返回一个备选结果(调用指定fallback方法),并在指定周期内尝试服务是否恢复
熔断类型
打开
请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
触发条件:
- 满足一定的阈值的时候(默认10秒内超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%请求失败)
半开
部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
触发条件
- 一段时间之后(默认5秒),由打开变为半开状态,会让其中一个请求进行转发,如果成功,断路器会关闭,若失败,继续开启,重复等待判断直至关闭
关闭
熔断关闭不会对服务进行熔断
触发条件
- 当半开状态下,有成功的请求后
使用方法
导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
添加启动注解
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
新增方法
@HystrixCommand(fallbackMethod = "getInstanceIdByHystrixFallBack",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("/getInstanceIdByHystrix/{number}")
public String getInstanceIdByHystrix(@PathVariable("number")int number) throws InterruptedException {
int a=0/number;
return instanceId+"number"+number;
}
/**
* 在熔断开启时,调用备选方法
* @param number
* @return
*/
public String getInstanceIdByHystrixFallBack(@PathVariable("number")int number){
return "fallback_"+number;
}
测试
传入0时,会走备选方法,当达到熔断条件后,会调用getInstanceIdByHystrixFallBack备选方法,并且在指定时间段(sleepWindowInMilliseconds)后放一个请求过来看看是否恢复
熔断开启后,在sleepWindowInMilliseconds时间段还未到,即便服务恢复,也走备选方法
降级
什么是降级
降级主要体现在消费端(客户端、上游服务。。。叫什么都行),当服务提供方自身出现异常,停机时,客户端会主动采用备选方案,避免引发更多问题
例如:double 11(双11),服务器中有一个服务A,为了给双11提供服务器资源,暂时将A服务关闭,但如果直接关了,A服务的调用方会报错,所以需要在调用方采用降级操作
使用方法
修改调用方项目
导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
修改yml,新增如下
feign:
hystrix:
enabled: true #开启hystrix
启动类新增注解 @EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@RibbonClient(name = "SERVICE-PROJECT",configuration = RbRule.class) //OpenFeign也使用RbRule策略
@EnableFeignClients
@EnableHystrix
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
修改方法
@Autowired
private FeignDemoService feignDemoService; //引入刚刚创建的接口
@HystrixCommand(fallbackMethod = "feignGetInstanceFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000") //5秒未响应则调用feignGetInstanceFallBack方法
})
@GetMapping("feignGetInstance")
public String feignGetInstance(){
return feignDemoService.getInstance();//调用,即可返回对应服务提供接口的信息
}
public String feignGetInstanceFallBack(){
return "feignGetInstanceFallBack";
}
测试
访问feignGetInstance,如果服务提供方正常,会给出对应的响应,如果在service打个断点模拟客户端请求超过指定响应时间,则调用备选方法;
服务端停止客户端调用直接进入备选方法,服务恢复后会自动恢复调用
其他降级策略
这些配置都是配置到@HystrixCommand注解中的
@HystrixCommand(fallbackMethod = "feignGetInstanceFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000"),
@HystrixProperty(name = "fallback.enable",value = "true"),
.
.
.
.
})
参数 | 描述 | 默认值 |
---|---|---|
execution.isolation.strategy | 隔离策略,有THREAD和SEMAPHORETHREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制 SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制 | 默认使用THREAD模式,以下几种场景可以使用SEMAPHORE模式:只想控制并发度外部的方法已经做了线程隔离调用的是本地方法或者可靠度非常高、耗时特别小的方法(如medis) |
execution.isolation.thread.timeoutInMilliseconds | 超时时间 | 默认值:1000在THREAD模式下,达到超时时间,可以中断在SEMAPHORE模式下,会等待执行完成后,再去判断是否超时设置标准:有retry,99meantime+avg meantime没有retry,99.5meantime |
execution.timeout.enabled | HystrixCommand.run()执行是否应该有超时。 | 默认值:true |
execution.isolation.thread.interruptOnTimeout | 在发生超时时是否应中断HystrixCommand.run()执行。 | 默认值:true THREAD模式有效 |
execution.isolation.thread.interruptOnCancel | 当发生取消时,执行是否应该中断。 | 默认值为false THREAD模式有效 |
execution.isolation.semaphore.maxConcurrentRequests | 设置在使用时允许到HystrixCommand.run()方法的最大请求数。 | 默认值:10S EMAPHORE模式有效 |
fallback.isolation.semaphore.maxConcurrentRequests | 设置从调用线程允许HystrixCommand.getFallback()方法的最大请求数。 | 默认值:10 SEMAPHORE模式有效 |
fallback.enabled | 确定在发生失败或拒绝时是否尝试调用HystrixCommand.getFallback()。 | 默认值为true |
新问题,代码冗余
上面的使用配置方式,需要每个方法都配置一个fallback,为减少代码量,可将公共的fallback提取
方式1:@DefaultProperties
通过在类上添加@DefaultProperties注解,让此类下所有添加@HystrixCommand注解的方法执行异常都会走defaultFallback方法;
单独在@HystrixCommand中配置的fallbackMethod会走单独配置的方法
- 实现如下
@RestController
@DefaultProperties(defaultFallback = "feignGetInstanceFallBackByPublic")
//第一步,配置DefaultProperties注解
public class DemoController {
@Autowired
private FeignDemoService feignDemoService; //引入刚刚创建的接口
/**
* 走特定方法的fallback
* @return
*/
@HystrixCommand(fallbackMethod = "feignGetInstanceFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
})
@GetMapping("feignGetInstance")
public String feignGetInstance(){
return feignDemoService.getInstance();//调用,即可返回对应服务提供接口的信息
}
/**
* 走通用fallback
* @return
*/
@HystrixCommand
@GetMapping("feignGetInstanceByPublic")
public String feignGetInstanceByPublic(){
return feignDemoService.getInstance();//调用,即可返回对应服务提供接口的信息
}
/**
* 特定方法的fallback
* @return
*/
public String feignGetInstanceFallBack(){
return "feignGetInstanceFallBack";
}
/**
* 公共方法的fallback
* @return
*/
public String feignGetInstanceFallBackByPublic(){
return "public-feignGetInstanceFallBack";
}
}
-
测试
当服务端异常时,
调用feignGetInstance方法会跳转feignGetInstanceFallBack备用方法
调用feignGetInstanceByPublic方法会调转feignGetInstanceFallBackByPublic备用方法
方式2:实现@FeignClient接口
实现@FeignClient标注的接口,不论多少个类都调用,通用Fallback备用方法
-
新增通用FallBack实现类
//FeignDemoService这个类就是被@FeignClient修饰的类 @Component public class FeignDemoFallBackService implements FeignDemoService { @Override public String getInstance() { return "public_getInstance"; } @Override public String getInstanceIdByTimeOut(int time) { return null; } }
-
修改@FeignClient注解
//@FeignClient注解中新增fallback,指定上一步实现的类 @Service @FeignClient(value = "SERVICE-PROJECT",fallback = FeignDemoFallBackService.class) //调用的服务名 public interface FeignDemoService { @GetMapping(value = "/getInstanceId") //服务端的请求地址,如果有入参可通过@PathVariable传入 public String getInstance(); @GetMapping(value = "/getInstanceIdByTimeOut/{time}") public String getInstanceIdByTimeOut(@PathVariable("time") int time); }
-
调用方如下
@RestController public class DemoController { @Autowired private FeignDemoService feignDemoService; //引入刚刚创建的接口 @GetMapping("feignGetInstance") public String feignGetInstance(){ return feignDemoService.getInstance();//调用,即可返回对应服务提供接口的信息 } @GetMapping("feignGetInstanceByPublic") public String feignGetInstanceByPublic(){ return feignDemoService.getInstance();//调用,即可返回对应服务提供接口的信息 } }
-
测试
http请求feignGetInstance等接口,发生错误后,会统一返回FeignDemoService实现类中对应方法的fallback信息
监控
新建监控项目
导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
启动类添加注解
@EnableHystrixDashboard
@SpringBootApplication
@EnableHystrixDashboard
public class MonitorApplication {
public static void main(String[] args) {
SpringApplication.run(MonitorApplication.class, args);
}
}
修改配置文件
server:
port: 9999
hystrix:
dashboard:
proxy-stream-allow-list: #这配置的就是要监控的ip、域名,如不配置可能会出现Unable to connect to Command Metric Stream错误
- "192.168.1.2"
- "192.168.1.3"
启动项目
访问 http://ip:prot/hystrix 出现跟刺猬一样的页面既成功
修改其他项目
目的是让Dashboard监控别的项目
导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
修改启动类
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //新增此注解
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
/**新增此Bean配置
* 此配置是为了服务监控而配置,与服务容错本身无观,springCloud 升级之后的坑
* ServletRegistrationBean因为springboot的默认路径不是/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;
}
}
调用方法修改
在需要监控的方法上需要新增@HystrixCommand注解,否则不会在Dashboard监控中体现
@HystrixCommand //必须加这个
@GetMapping("getInstanceId")
public String getInstanceId(){
return instanceId;
}
测试
-
启动上面修改好的两个项目
-
在 http://ip:prot/hystrix 打开页面中输入要监控服务的地址 ip:port/actuator/hystrix.stream
-
然后请求被@HystrixCommand注解修饰的接口,Dashboard会显示如下界面
-
至此,Dashboard就没问题了
监控看法
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康程度从 绿色 > 黄色 > 橙色 > 红色 递减;
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大实心圆就越大,所以通过该实心圆的展示,就可以在大量实例中快速的发现故障实例和高压力实例。