这是我参与更文挑战的第 13 天,活动详情查看: 更文挑战
Hystrix
一、概述
Hystrix是一个用于分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖会不可避免的调用失败,Hystrix能够在保证在一个依赖出问题的情况下,不会影响到整体的服务失败,避免联级故障,提高发生系统的弹性。
问题引出
多个微服务之间的调用会形成一个链路,当链路上的某个微服务发生故障失效(超时、异常等),这个有问题的模块再去调用其他模块,这样就会发生级联故障,也叫雪崩。
解决问题
‘断路器’是一种开关装置,当某个服务发生故障时,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选方案,而不是长时间的等待或抛出异常,保证了服务调用放的线程不会被长期不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix的功能:
- 服务降级:在程序运行异常、超时、线程池\信号量打满的情况下会触发降级,给客户端一个友好的提示,fallback。
- 服务熔断:类似保险丝,达到最大访问量后直接拒绝访问,然后调用访问降级给出友好提示。
- 服务限流:秒杀高并发操作,防止一窝蜂的访问访问,设置每秒执行多上个请求,超出的请求进行排队。
- 实时监控
二、服务降级
1、单侧降级
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
复制代码
配置
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka
feign:
hystrix:
enabled: true
复制代码
Order80服务调用8001提供的服务,其中有一个延迟的接口
@Service
public class PaymentService {
public String paymentInfoSuccess(Integer id){
return "线程池:" + Thread.currentThread().getName() + " paymentInfoSuccessId:" + id;
}
public String paymentInfoFailed(Integer id){
//制造超时错误
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfoFailedId:" + id;
}
}
复制代码
因为OpenFeign的默认等待时间为1秒,所以调用超过1秒的接口会抛出异常,这样后面的服务可能都会因为这个延迟而故障,接下来添加服务降级功能解决问题。
降级配置,设置一个兜底方案。
提供服务8001模块异常后的fallback
@Service
public class PaymentService {
//fallbackMethod:指定兜底方法,commandProperties指定出现哪些异常进行fallbackMethod
@HystrixCommand(fallbackMethod = "paymentInfoFailedHandler",commandProperties = {
//如果超时3秒就执行fallbackMethod
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfoFailed(Integer id){
//制造超时错误
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfoFailedId:" + id;
}
public String paymentInfoFailedHandler(){
return "Payment8001的paymentInfoFailed的fallbackMethod执行了";
}
}
复制代码
消费者Order80配置fallback,通常在消费者端配置
@RestController
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@HystrixCommand(fallbackMethod = "paymentInfoFailedHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
@GetMapping(value = "/consumer/paymentInfoFailed/{id}")
public String paymentInfoFailed(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfoFailed(id);
}
@GetMapping(value = "/consumer/paymentInfoSuccess/{id}")
public String paymentInfoSuccess(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfoSuccess(id);
}
public String paymentInfoFailedHandler(){
return "Order80的paymentInfoFailed的fallbackMethod执行了";
}
}
复制代码
2、全局降级
上面的情况会把fallback代码和业务代码耦合在一起,并且每一个方法都要单独设置一个fallback方法,用起来并不方便,使用全局的fallback来改进。
使用全局降级
@RestController
//指定全局降级的fallback方法
@DefaultProperties(defaultFallback = "payment_Global_RollbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
//开启全局降级,如果指定了fallback方法就使用已指定的,没有指定就使用全局的
@HystrixCommand
@GetMapping(value = "/consumer/paymentInfoFailed/{id}")
public String paymentInfoFailed(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfoFailed(id);
}
public String payment_Global_RollbackMethod() {
return "Order80的paymentInfoFailed的payment_Global_RollbackMethod执行了";
}
}
复制代码
3、通配服务降级
消费端在远程调用提供服务的接口时对其进行降级,如果提供服务的服务器发生宕机,也能降级成功。
创建一个类,实现远程调用的接口
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfoFailed(Integer id) {
return "PaymentFallbackService的PaymentFallbackService fallback 方法";
}
@Override
public String paymentInfoSuccess(Integer id) {
return "PaymentFallbackService 的paymentInfoSuccess fallback 方法";
}
}
复制代码
在远程调用的接口上注明降级的类
@Component
@FeignClient(name = "CLOUD-PAYMENT-HYSTRIX-SERVICE",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping(value = "/paymentInfoFailed/{id}")
String paymentInfoFailed(@PathVariable("id") Integer id);
@GetMapping(value = "/paymentInfoSuccess/{id}")
String paymentInfoSuccess(@PathVariable("id") Integer id);
}
复制代码
三、服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错或不可用时,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务响应正常后,恢复链路调用。
SpringCloud中使用Hystrix实现熔断机制,Hystrix会监控微服务之间的调用状况,当失败的调用到一定的y阈值,就会启用熔断机制。
在payment8001服务中配置测试,在Service中配置熔断
@Service
public class PaymentService {
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",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"), //失败率达到多少后熔断
})
public String paymentCircuitBreaker(Integer id){
if (id < 0) {
throw new RuntimeException("id 不能小于0");
}
String uuid = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "SUCCESS,code:" + uuid;
}
public String paymentCircuitBreaker_fallback(Integer id){
return "id不能为负数,请稍后再试!" + id;
}
}
复制代码
该方法在10秒钟之内访问次数达到10次以上并且失败率如果达到百分之60,id传入负数,就会启动熔断机制(熔断器全开),这时即使发送的id为整数,也还是会继续走fallback方法(熔断器半开状态),稍等一会就会恢复正常的调用(熔断器关闭)。
熔断器状态:
- 熔断打开:请求不再调用当前服务,内部设置时钟(平均故障处理时间),当打开时间达到时钟所设时间进入半开状态。
- 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则认为当前服务恢复正常,关闭熔断。
- 熔断关闭:不会对服务进行熔断
四、服务监控
除了上述功能,Hystrix还提供的准实时的调用监控,Hystrix会持续的记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行成功、失败多少次。
1、搭建dashboard9001服务
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!--注意:只要用到web图形化都需要该依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
复制代码
主启动
@SpringBootApplication
@EnableHystrixDashboard
public class DashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(DashboardMain9001.class,args);
}
}
复制代码
启动9001,访问http://localhost:9001/hystrix即可看到Hystrix Dashboard的主页
2、使用Dashboard监控服务
注意:在被监控的8001端主启动类需要加一个配置,springCloud升级后导致的
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
@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;
}
}
复制代码
监控8001服务
在hystrix主页输入监控地址http://localhost:8001/hystrix.stream