Hystrix
一.简介
1.官网
2.分布式微服务面临的问题?
- 服务雪崩
某个或者某些服务出现故障,导致调用本服务的其他服务调用缓慢,或者调用失败,发生级联反应,从而导致多个服务不能使用,发生服务雪崩不能使用.
3.Hystrix的作用
在微服务之间作为断路器,保险丝使用,避免发生级联错误,从而保护服务,避免服务雪崩.
(1)服务降级(出现错误怎么办?)
#服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback.
可能出现服务降级的原因:
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
(2)服务熔断(错误过多怎么办?)
#类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示.
总结:
就是单位时间内出现多少次错误;或者访问的错误比率达到多少就对外不提供服务;等过一段时间(时间窗口期)再次尝试提供服务.
(3) 服务限流
#秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
二.代码使用
1.基础使用
1) 建module
略
2) 改pom
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
3) 改yml
- 生产者
则不需要配置什么,可能需要配置的就是暴露监控断点给hystrix dashboard.
# 在被监控服务上添加暴露端点
management:
endpoints:
web:
exposure:
include: hystrix.stream
- 消费者
#如果消费者使用feign作为调用客户端的话,则需要开启hystrix;如果使用的是RestTemplate调用的话,以下yml配置不用配置.
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
4) 主启动
@EnableHystrix注解和@EnableCircuitBreaker随便使用一个就可以了.
@SpringBootApplication
@EnableFeignClients
//以下两个注解随便使用一个就可以
@EnableHystrix
//@EnableCircuitBreaker //启动服务熔断,使注解 @HystrixCommand生效
public class PaymentHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain80.class,args);
}
}
@EnableHystrix注解
#@EnableHystrix注解就是@EnableCircuitBreaker的子类,并且和他一模一样
所以,以后我们使用@EnableHystrix就好.
5) 业务类
(1) 单独使用Hystrix(生产者和消费者均可)
在任何方法上都可以,一般是在controller类或者service类中使用.
- [1] 单独配置方法的熔断方法
优点:
精确控制每个方法的服务降级
缺点:
1.每个方法都需要编写,服务降级方法,麻烦
2.服务降级方法和业务方法在一个类中,糅杂.
//服务熔断
/**
* 当id为正数的时候,访问正常
* 当id为负数的时候,进行服务降级.
* 当请求的次数超过10次,并且请求的降级率为60%,则开启服务熔断.
* 过一段时间,服务会再次尝试开启请求,检查是否能通过
*/
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = { //fallbackMethod表示出现服务熔断调用的方法
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
/**
* 这是服务熔断之后,调用的方法
*/
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
- [2] 给整个类配置熔断方法
类上用注解@DefaultProperties指明本类的默认熔断方法;
方法上用注解 @HystrixCommand 标明本方法需要开启服务降级.
优点:
减少代码的编写
缺点:
业务代码和服务降级方法糅杂在一起,在一个类中.
@RestController
@Slf4j
//这是Hystrix的注解,指定本类的默认的熔断执行方法是哪个.
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
....
/**
* 服务降级的fallBackMethod方法调用的优先级:
* 1.@HystrixCommand加上方法上指明fallbackMethod调用的fallback方法.
* 2.@HystrixCommand只配置了本注解,使用类上@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")配置的方法
* 3.@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
* 使用feign接口上配置的fallback类中对应的方法
*/
@HystrixCommand //没有fallbackMethod这个属性,就表示使用全局服务降级方法,
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
log.info("*******result:"+result);
return result;
}
//下面是全局fallback方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试,(┬_┬)";
}
}
(2) openFeign + Hystrix
openFeign即可使用上述的两种方式,也可在feign客户端调用api接口的时候,使用服务降级.
优点:
面向接口编程,服务降级方法和业务方法分离.
- 第一步:feign调用interface类
feign调用的interface类,指明使用哪个类作为服务降级类.
@Component
//fallback标明,feign调用这个服务的时候,如果服务出现异常的服务降级类是哪个
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
- 第二步:Hystrix服务降级实现类
/**
* @author gl
* @time 2020-06-18 22:26
* @function :Openfeign调用的服务降级类.
* @step :
*/
@Component
//需要实现feign调用的接口interface
public class PaymentFallbackService implements PaymentHystrixService{
//对应feign调用方法中的paymentInfo_OK这个方法的降级方法.
@Override
public String paymentInfo_OK(Integer id) {
return "-------PaymentFallbackService.paymentInfo_OK-----对方服务已宕机,请稍后重试";
}
//对应feign调用方法中的paymentInfo_TimeOut这个方法的降级方法.
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-------PaymentFallbackService.paymentInfo_TimeOut-----对方服务已宕机,请稍后重试";
}
}
2.服务降级(程序规定时间获取不到结果,怎么办?)
服务降级可能发生的地方
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
其余代码省略,列举服务降级可能发生的地方.
@Service
public class PaymentService {
/**
* 成功的方法
*/
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ;
}
/**
* 服务降级 --出现错误
*/
@HystrixCommand(fallbackMethod = "paymentInfo_handler" ,commandProperties = { //这个表示服务如果出现异常,执行哪个方法
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000") //3秒钟以内就是正常的业务逻辑
})
public String paymentInfo_Exception(Integer id){
//测试出现错误
int i= 10/0;
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)";
}
/**
* 服务降级--超时
*/
@HystrixCommand(fallbackMethod = "paymentInfo_handler" ,commandProperties = { //这个表示服务如果出现异常,执行哪个方法
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000") //3秒钟以内就是正常的业务逻辑
})
public String paymentInfo_TimeOut(Integer id){
//测试超时
int timeNumber = 2;
try {
TimeUnit.SECONDS.sleep(timeNumber);
}catch (Exception e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}
/**
* 这是hystrix服务降级的回调方法.
* @param id
* @return
*/
public String paymentInfo_handler(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_handler,当前方法超时或者出现错误,请稍后尝试id: "+id+"\t"+"呜呜呜"+" 耗时(秒)";
}
.....
}
3.服务熔断(程序错误过多,怎么办?)
- 一定时间周期内,错误效应次数达到多少次,则熔断即不再调用该服务,经过一定时间窗口期,再尝试去请求服务.
- 一定时间周期内,当请求次数达到多少,错误比例达到多少,则熔断即不再调用该服务,经过一定时间窗口期,再次尝试去请求服务.
//================服务熔断
//服务熔断
/**
* 当id为正数的时候,访问正常
* 当id为负数的时候,进行服务降级.
* 当请求的次数超过10次,并且请求的降级率为60%,则开启服务熔断.
* 过一段时间,服务会再次尝试开启请求,检查是否能通过
*/
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = { //fallbackMethod表示出现服务熔断调用的方法
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
/**
* 这是服务熔断之后,调用的方法
*/
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
4.@HystrixCommand 注解详解
这个注解是Hystrix最重要的注解,关于这个注解的属性可以参考官网.
参考文章: https://github.com/Netflix/Hystrix/wiki/Configuration
/**
* This annotation used to specify some methods which should be processes as hystrix commands.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
String groupKey() default "";
String commandKey() default "";
String threadPoolKey() default "";
String fallbackMethod() default "";
//定义熔断相关的属性配置.具体的属性参考官网
HystrixProperty[] commandProperties() default {};
//hystrix的线程池的配置,具体属性参考官网
HystrixProperty[] threadPoolProperties() default {};
//异常忽略
Class<? extends Throwable>[] ignoreExceptions() default {};
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
HystrixException[] raiseHystrixExceptions() default {};
String defaultFallback() default "";
}
Hystrix Dashboard
因为Hystrix Dashboard也是一个微服务,所以也需要搭建一个微服务模块,其实就有点类似于Eureka的server端一样的.
1.dashboard的搭建
(1) 建module
略
(2) 改pom
<!--新增hystrix dashboard,这个会自动依赖导入web模块-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
(3) 写yml
#配置端口号即可
server:
port: 9001
(4) 主启动
@EnableHystrixDashboard //启动HystrixDashboard
@SpringBootApplication
//启动HystrixDashboard
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
(5) 测试页面
根据你项目的端口号.访问如下路劲,出现如下页面即成功
http://localhost:9001/hystrix
2.监控具体的项目
(1).被监控的微服务都必须引入依赖
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--健康检查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
(2) 被监控的微服务必须暴露监控断点
# 在被监控服务上添加暴露端点
management:
endpoints:
web:
exposure:
include: hystrix.stream
(3) 在监控面板填写监控地址
(4) 如果出现Unable to connect to Command Metric Stream
则在被监控的微服务中注入如下bean
/**
* 这是为了hystri dashboard监控仪表盘,而配置的bean
* @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;
}
3.监控面板说明
总体就是7色,一线,一圈.
- 7色:成功数/熔断数/错误请求/超时数/线程拒绝数/异常失败数/错误百分比
- 1线:表示请求的变化图
- 1圈:圈越大,qps越大,并发越大,颜色绿色表示正常运行,红色表示错误比例大.
4.Turbine(监控多个微服务)
上面的监控,我们只能监控单个微服务,我们可以利用turbine整合多个监控流,一次性监控多个微服务.
具体略.