在微服务架构中,我们不同的服务拆分在了不同的系统中,每个系统都运行在独立的进程中,服务之间通过远程调用进行跨进程通信,每个服务通通过服务注册中心形成了服务的订阅,注册,服务之间形成了依赖关系,当某个业务依赖的某个服务功能因为网络或者其他原因导致访问速度缓慢或者故障,会影响其他调用者服务的执行时长,从而导致整个服务慢慢的会形成阻塞,最终导致整个业务瘫痪的局面,因此我们需要一种机制来针对这些情况能够有个快速的响应,防止慢服务或者故障服务在整个分布式系统中蔓延,下面我们就来了解Hystrix-断路器。
springcloud Hystrix 实现了断路器,线程隔离等服务,他是基于netflix 的开源框架 Hystrix 实现的,他能够提供服务降级,服务熔断,线程和信号隔离,请求缓存请求合并以及服务监控等强大功能。
下面我们开始撸代码,maven 配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ethan.springcloud</groupId>
<artifactId>helloservice-client-hystrix</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>helloservice-client-hystrix</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ethan.springclod.api</groupId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
定义我们带熔断功能的service:
@Service
@Slf4j
public class HelloServiceWithFallback {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallback")
public String hello(String name) {
String responseEntity = restTemplate.getForObject("http://HELLOSERVICE/hello?name={1}", String.class, name);
log.info("hello service :{}", responseEntity);
return responseEntity;
}
public String fallback() {
return "this is fallback message! ";
}
}
下面是controller代码:
@RestController
@Slf4j
public class HelloController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private HelloServiceWithFallback helloServiceWithFallback;
/**
*
* @param name
* @return
*/
@GetMapping("hello")
public String hello(String name) {
return helloServiceWithFallback.hello(name);
}
}
springboot 引导类:
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableRetry
public class HelloserviceClientHystricxApplication {
public static void main(String[] args) {
SpringApplication.run(HelloserviceClientHystricxApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
这里介绍下两个注解的使用,@EnableCircuitBreaker 开启断路器,
@HystrixCommand ,源码注释内容释义,被该注解标注的方法会被包装成hystrix 命令模式运行,以达到熔断。简单来说就是让你通过注解来指定方法执行失败时的兜底方法。
然后我们小改下helloService 的实现,让其线程休眠3s。代码如下:
@GetMapping("hello")
public String hello(String name) throws InterruptedException {
log.info("hello:{}-线程睡眠:{}",name);
Thread.sleep(3000);
return "hello"+name;
}
下面,我们启动相应的工程,并进行访问,在浏览器输入:http://localhost:8090/hello?name=mijixiaojiang
查看控制台结果并不是我们预期的效果,居然报错了:
异常信息表示,没有找到对应的错误回调方法,这是为什么呢,其实这是一个我们开发中很可能会碰到的一个小坑,回调方法必须与原方法具有相同的参数签名,我们这里进行代码调整如下:
@HystrixCommand(fallbackMethod = "fallback")
public String hello(String name) {
String responseEntity = restTemplate.getForObject("http://HELLOSERVICE/hello?name={1}", String.class, name);
log.info("hello service :{}", responseEntity);
return responseEntity;
}
/**
* 错误回调方法参数签名必须与原方法一致
* @param name
* @return
*/
public String fallback(String name) {
return "this is fallback message! ";
}
然后再启动项目,再次进行测试:
好的,终于看到我们的预期结果了。
在对hystrix 有了一定的了解后,我们来聊一聊使用hystrix进行服务降级的时候的一些注意点。
当我们的服务出现问题需要降级时,也就是我们的fallback方法进行回调时,我们应该依赖一些静态逻辑来获取结果,而不是依赖网络请求获取,比如带有数据库请求的回调,如果一定要依赖网络请求进行降级,则降级服务也必须有有服务降级能力,形成级联降级,否则依然会造成服务不可用,最终导致雪崩事件发生。
下面我们来看下如何获取异常信息,我们只需要在回调方法中直接传入 Throwable进行参数接收即可获取到对应的异常信息。hystrix除HystrixBadRequestException之外,其他异常都会认为是Hystrix命令执行失败并触发服务降级处理逻辑. 当然@HystrixCommand注解还支持忽略指定异常信息,通过ignoreExceptions属性,并且该属性支持忽略多个异常。
下面我们修改回调方法看下异常捕获信息,以及服务实现代码(手动抛出异常):
@GetMapping("hello")
public String hello(String name) throws Exception {
log.info("hello:{}-线程睡眠:{}",name);
// Thread.sleep(3000);
if(1==1){
throw new Exception("这是测试降级异常!");
}
return "hello"+name;
}
/**
* 错误回调方法参数签名必须与原方法一致
*
* @param name
* @return
*/
public String fallback(String name, Throwable e) {
log.info("这是造成服务降级的异常信息:{}",name, e);
return "this is fallback message! ";
}
然后启动服务,发送请求,观看控制台日志:
我们看到回调方法捕获到了异常信息。
今天我们先对hystrix 了解到这里,下篇文章我们来了解下hystrix的其他相关特性功能,如果不对之处,欢迎各位指出交流!