Hystrix 是什么?
在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个
延迟和容错库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix 有什么用?
Hystrix 旨在执行以下操作:
Hystrix 重要概念
更多概念:https://zhuanlan.zhihu.com/p/74436717
服务端
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
<version>2.2.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.atguigu.springbootgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
dependencies>
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
//异常访问
//fallbackMethod:指定回调函数的函数名,当被标注方法出现指定异常时调用
//commandProperties:HystrixCommand的属性,值为数组类型,用来配置异常信息
@HystrixCommand(fallbackMethod = "paymentInfo_TIMEOUT_handler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_TIMEOUT(Integer id){
int time = 5;
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " payment_timeout, id:" + id + "\t" + "O(∩_∩)O哈哈~,耗时(秒):" + time;
}
public String paymentInfo_TIMEOUT_handler(Integer id){
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TIMEOUT_handler, id:" + id + "\t" + "o(╥﹏╥)o,请求出错了";
}
消费端:通常服务降级的设置端
server:
port: 80
spring:
application:
name: hystrix-consumer-order
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka/
feign:
hystrix:
enabled: true
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@EnableHystrix
public class ConsumerHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixMain80.class,args);
}
}
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String payment_ok(@PathVariable("id") Integer id){
String info_ok = paymentHystrixService.payment_ok(id);
System.out.println("info_ok" + info_ok);
return info_ok;
}
/*@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String payment_timeout(@PathVariable("id") Integer id){
String info_timeout = paymentHystrixService.payment_timeout(id);
System.out.println("info_timeout" + info_timeout);
return info_timeout;
}*/
@HystrixCommand(fallbackMethod = "payment_timeout_handler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
})
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String payment_timeout(@PathVariable("id") Integer id){
String info_timeout = paymentHystrixService.payment_timeout(id);
System.out.println("info_timeout" + info_timeout);
return info_timeout;
};
public String payment_timeout_handler(Integer id){
return "线程池:" + Thread.currentThread().getName() + " payment_timeout_handler, id:" + id + "\t" + "o(╥﹏╥)o,请求出错了";
}
}
1. 使用注解配置通用fallback回调函数:@DefaultProperties
指定默认的服务降级回调方法,当方法出错且没有指定特有的回调方法时会使用默认方法。这样可以防止为每个方法指定fallback函数而导致代码膨胀
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_timeout_handler")
public class OrderHystrixController {
......
}
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String payment_timeout(@PathVariable("id") Integer id){
String info_timeout = paymentHystrixService.payment_timeout(id);
System.out.println("info_timeout" + info_timeout);
return info_timeout;
};
//defaultFallback
public String payment_Global_FallbackMethod(){
return "payment_Global_FallbackMethod: 系统出现错误";
}
2. 将业务处理逻辑和fallback函数解耦
解决代码混乱和耦合度高的问题
@Service
public class PaymentHystrixFallbackServiceImpl implements PaymentHystrixService {
@Override
public String payment_ok(Integer id) {
return "payment_ok :default_global_fall_back-payment_ok o(╥﹏╥)o";
}
@Override
public String payment_timeout(Integer id) {
return "payment_ok :default_global_fall_back-payment_timeout o(╥﹏╥)o";
}
}
@FeignClient(value = "cloud-provider-hystrix-payment" ,fallback = PaymentHystrixFallbackServiceImpl.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String payment_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String payment_timeout(@PathVariable("id") Integer id);
}
在微服务中服务间依赖非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,而这时反垃圾服务超时了,由于审核服务依赖反垃圾服务,反垃圾服务超时导致审核服务逻辑一直等待,而这个时候评论服务又在一直调用审核服务,审核服务就有可能因为堆积了大量请求而导致服务宕机
由此可见,在整个调用链中,中间的某一个环节出现异常就会引起上游调用服务出现一些列的问题,甚至导致整个调用链的服务都宕机,这是非常可怕的。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所以调用服务需要进行自我保护,而保护的常用手段就是熔断
熔断器原理
熔断机制其实是参考了我们日常生活中的保险丝的保护机制,当电路超负荷运行时,保险丝会自动的断开,从而保证电路中的电器不受损害。比如带空开的公牛插线板,它规定了一个上限瓦数,当这个插线板上的电器发生短路瓦数突然超标,空开就会断开,通过牺牲这个插线板上的电器正常使用,而保证整个房间的电路是通畅的。
而服务治理中的熔断机制,指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误
在这种模式下,服务调用方为每一个调用服务 (调用路径) 维护一个状态机,在这个状态机中有三个状态:
关闭 (Closed):在这种状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进去断开状态
打开 (Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复
半打开 (Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到关闭状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮
服务治理中引入熔断机制,使得系统更加稳定和有弹性,在系统从错误中恢复的时候提供稳定性,并且减少了错误对系统性能的影响,可以快速拒绝可能导致错误的服务调用,而不需要等待真正的错误返回
熔断的配置和使用
在服务降级的基础下,配置:还是使用@HystrixCommand注解进行配置
//服务熔断
//从熔断服务启动开始,十秒内接收到10次请求(如果不够10次就算请求全部失败也不会触发断路器)计算一次“错误百分比”
//“错误百分比”大于60%时切换为open状态,open后,在10秒空窗期内不会放行请求
//10秒后,放行一个请求确定是否应该关闭断路器。
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 触发阈值:断路器请求量阈值
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 断路器错误百分比阈值
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 短路器打开后的空窗期时间设置
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if(id < 0){
throw new RuntimeException("*****id不能为负数");
}
UUID uuid = UUID.randomUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + uuid;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id不能为负数,请稍后再试!";
}
更多配置属性信息见HystrixCommandProperties类
1. pom引用
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
2. 主启动类启用监控服务
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
3. 在需要被监控的服务主启动类中配置servlet
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*否则,Unable to connect to Command Metric Stream 404
*/
@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;
}
}
4. 启动监控页面,输入需要进行监控的服务地址
启动监控页面:http://localhost:9001/hystrix
输入需要监控的服务器流地址:http://localhost:8001/hystrix.stream
监控页面:
页面内容解释: