一、简介
Hystrix 是针对微服务分布式系统采用的熔断保护中间件,在微服务中都相互依赖,如果不能对依赖的服务进行隔离,服务本身也有可能发生故障,Hystrix 通过 HystrixCommand 对调用进行隔离,这样可以故障连锁反应,能够让接口调用快速失败并迅速恢复正常,或者回退并优雅降级。
二、Hystrix简单的使用
1. 引入依赖
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.18</version>
</dependency>
2. 编写代码 继承 HystrixCommand
public class MyHystrixCommand extends HystrixCommand<String> {
private final String name;
public MyHystrixCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey("MyGroup"));
this.name = name;
}
@Override
protected String run() throws Exception {
return this.name + ":" +Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//同步调用
String result = new MyHystrixCommand("Alliex").execute();
System.out.println(result);
//异步调用
Future<String> future = new MyHystrixCommand("Alliex").queue();
System.out.println(future.get());
}
3. 回退支持
@Override
protected String run() throws Exception {
TimeUnit.SECONDS.sleep(2);
System.out.println("get Data");
return this.name + ":" +Thread.currentThread().getName();
}
//回退支持
@Override
protected String getFallback() {
return "执行失败了";
}
运行代码结果如下,证明已经触发了回调
4. 信号量策略配置
public MyHystrixCommand(String name) {
//信号量策略配置
super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties
.ExecutionIsolationStrategy.SEMAPHORE)));
this.name = name;
}
运行结果如下,之前在run方法中输出了线程的名称,通过这个就可以确定是线程隔离还是信号量隔离
5. 线程隔离策略配置
运行结果如下
6. 结果缓存
增加 getCacheKey() 方法, 在run方法中输出 get Data ,将main方法中的代码改为如下写法
@Override
protected String getCacheKey() {
return String.valueOf(this.name);
}
@Override
protected String run() throws Exception {
// TimeUnit.SECONDS.sleep(2);
System.out.println("get Data");
return this.name + ":" +Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//缓存的测试代码
HystrixRequestContext hystrixRequestContext = HystrixRequestContext.initializeContext();
String result = new MyHystrixCommand("Alliex").execute();
System.out.println(result);
Future<String> future = new MyHystrixCommand("Alliex").queue();
System.out.println(future.get());
hystrixRequestContext.shutdown();
}
运行结果如下,可以看到只输出一个get Data 证明走了缓存
7.缓存清除
public class ClearCacheHystrixCommand extends HystrixCommand<String> {
private static final HystrixCommandKey GETTER_KEY = HystrixCommandKey.Factory.asKey("MyKey");
private final String name;
public ClearCacheHystrixCommand(String name){
super(HystrixCommand.Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("MyGroup")
).andCommandKey(GETTER_KEY));
this.name = name;
}
/*----------------------清除缓存-------------------------------*/
public static void flushCache(String name){
HystrixRequestCache.getInstance(GETTER_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear(name);
}
@Override
protected String getCacheKey() {
return String.valueOf(name);
}
@Override
protected String run() throws Exception {
System.out.println("get data");
return this.name + ":" + Thread.currentThread().getName();
}
@Override
protected String getFallback() {
return "失败了";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
String result = new ClearCacheHystrixCommand("Alliex").execute();
System.out.println(result);
ClearCacheHystrixCommand.flushCache("Alliex");
Future<String> future = new ClearCacheHystrixCommand("Alliex").queue();
System.out.println(future.get());
}
}
运行结果如下,可以看到输出了两次 get data 证明缓存清楚成功
8. 合并请求
public class MyHystrixCollapser extends HystrixCollapser<List<String>,String,String> {
private final String name;
public MyHystrixCollapser(String name) {
this.name = name;
}
@Override
public String getRequestArgument() {
return name;
}
@Override
protected HystrixCommand<List<String>> createCommand(Collection<CollapsedRequest<String, String>> requests) {
return new BatchCommand(requests);
}
@Override
protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, String>> requests) {
int count = 0;
for (CollapsedRequest<String,String> request:requests) {
request.setResponse(batchResponse.get(count++));
}
}
private static final class BatchCommand extends HystrixCommand<List<String>>{
private final Collection<CollapsedRequest<String,String>> requests;
private BatchCommand(Collection<CollapsedRequest<String,String>> requests) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueOfKey")));
this.requests = requests;
}
@Override
protected List<String> run() throws Exception {
System.out.println("真正执行请求---------------------");
ArrayList<String> response = new ArrayList<String>();
for (CollapsedRequest<String,String> request: requests) {
response.add("返回结果: "+request.getArgument());
}
return response;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
HystrixRequestContext hystrixRequestContext = HystrixRequestContext.initializeContext();
Future<String> future = new MyHystrixCollapser("Alliex").queue();
Future<String> future2 = new MyHystrixCollapser("Alliex2").queue();
System.out.println(future.get()+"="+future2.get());
hystrixRequestContext.shutdown();
}
}
运行结果如下,可以看到指输出了一次,证明两个任务被合并到了一起
三、Springcloud 整合 Hystrix
1. 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
2. 加注解
在启动类上加 @EnableHystrix 或者 @EnableCircuitBreaker 这两个主句只能加一个。
3. 写测试方法
@RestController
public class TestHystrixController {
private RestTemplate restTemplate;
@Autowired
public TestHystrixController(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
@GetMapping("/callHello")
@HystrixCommand(defaultFallback = "defaultCallHello")
public String callHello(){
String result = restTemplate.getForObject("http://localhost:8088/hello",String.class);
return result;
}
public String defaultCallHello(){
return "hello fail";
}
}
运行结果如下, 证明执行了回退方法
四、 Feign 整合 Hystrix
1. 开启 Feign 对 Hystrix的支持
#开启hystrix false为禁用
feign.hystrix.enabled=true
2. Feign回退内容定义
@Component
public class UserRemoteClientFallback implements UserRemoteClient {
@Override
public String sayHelloToPerson(String userName) {
return "hello fail";
}
}
3. 制定 Fallback的方式
@FeignClient(value = "eureka-client-user-service"
,configuration = FeignConfiguration.class
,fallback = UserRemoteClientFallback.class
// ,fallbackFactory = UserRemoteClientFallbackFactory.class
)
public interface UserRemoteClient {
@GetMapping("/user/helloToPerson")
String sayHelloToPerson(@RequestParam("userName") String userName);
}
停掉 eureka-client-user-service 服务,运行结果如下,证明进入了回退的方法
4. FallbackFactory方式
通过fallback可以实现服务不可用时的回退功能,如果想知道触发回退的原因可以使用 FallbackFactory
@Component
public class UserRemoteClientFallbackFactory implements FallbackFactory<UserRemoteClient> {
private Logger logger = LoggerFactory.getLogger(UserRemoteClientFallbackFactory.class);
@Override
public UserRemoteClient create( Throwable cause) {
logger.error("UserRemoteClient 回退" ,cause);
return userName -> "hello fail";
}
}
@FeignClient(value = "eureka-client-user-service"
,configuration = FeignConfiguration.class
// ,fallback = UserRemoteClientFallback.class
,fallbackFactory = UserRemoteClientFallbackFactory.class
)
public interface UserRemoteClient {
@GetMapping("/user/helloToPerson")
String sayHelloToPerson(@RequestParam("userName") String userName);
}
运行结果如下:
可以看到控制台已经输出了错误原因。
5. Hystrix 监控
a. 引入依赖
<!-- Hystrix监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version> 2.2.2.RELEASE</version>
</dependency>
b. 在配置文件中加如下的配置
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
c. 访问地址 http://localhost:8080/actuator/hystrix.stream 可以看到一只在ping ,直到你访问 http://localhost:8080/test/fegin?userName=xxx 时就有内容输出了,如下图
6. 整合Dashboard 查看监控数据
a. 引入依赖
<!-- 整合dasboard查看监控数据-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
b. 启动类中加入注解 @EnableHystrixDashboard
c. 访问 http://localhost:8080/hystrix ,可以看到下面的页面
在第一输入框中输入我们刚才监控的地址:http://localhost:8080/actuator/hystrix.stream Delay 和title可以自定义,点击按钮后可以看到下图所示的界面: