Spring Cloud Hystrix介绍与使用

Posted 风某人~Wind

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud Hystrix介绍与使用相关的知识,希望对你有一定的参考价值。

Hystrix

Spring Cloud 里的 Hystrix,是一个容错组件。

Hystrix实现了 超时机制和断路器模式。

Hystrix是Netflix开源的一个类库,用于隔离远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。主要有以下几点功能:

1. 为系统提供保护机制。在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
2. 防止雪崩。
3. 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中运行。
4. 跳闸机制:当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。
5. 资源隔离:Hystrix为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。
6. 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
7. 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
8. 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
9. 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。

前面讲了一堆,晕不晕,相信有些人看了一遍还是没看懂Hystrix是干嘛的。

Hystrix 总的来讲就干几件事,分别是:降级、熔断、限流,隔离

1. 什么是降级?

你去外面点小姐姐陪玩,你原本想点的是那个200块一个小时的小姐姐,身材美丽,说话又好听,但是呢,别人没空,不接单了。那你也没办法是吧,只能点个20块一个小时的小姐姐凑合凑合,或者你比较专一,我这次不点了,下次再来。这就是降级。就是你原来发起向服务方的请求,连接超时了,没请求通,你还可以向其他服务发起请求,万一还是没成功,那你就可以给请求方 return “客官下次再来!”,然后返回一个比较友好的页面给用户看 。

2. 隔离

你想点的那个小姐姐,你前面还有20个人在排队呢,今天不接单了,直接就拒绝你了。就是说Hystrix会为每个请求都开一个小型线程池,如果该线程满了,则发往该服务的这个请求会被直接拒绝,而不是在继续排队等候,这就是隔离

3. 限流

限流最容易理解了,玩过英雄联盟的人,以前在战斗之夜送皮肤的时候,有些大区登录的时候是不是要排队,这就是限流。

4. 熔断

你去请求一个服务,请求失败,当你请求某个服务的连续失败次数达到阈值的时候,那我就不请求你这个服务了,我和这个服务已经断开连接了。

但是呢,这个服务过会又好了呢,我们是不是还得需要恢复策略,所以Hystrix里面有个半开状态得策略,就是我可能10次里面随机在去请求一次,我试一试,万一成功了呢,这种在Hystrix里面叫自我修复功能。

有些人还不明白,说你这讲的我还是不懂,那咱们直接用代码来解释

我们可以先定义一个计数器,用来定义连续请求失败的次数
  
  int  count =0;
  try 
  
    //在这之前我们先定义一个阈值,如果count连续失败多少次,我们就不请求了
    
    if(count ==10)//当我们连续失败10次得时候,我们就不请求了
    
       //但是我们也不能抛弃他把,所以需要一个自我修复策略
       
       //比如我们用个随机数
       Random random = new Random();
       
       //当我们随机数随机到1的时候,我就去请求一下试一试
       if(random.nextInt(10)==1)
         .......
       
       
    
   
  catch(Exeception e)
  
  		//这里是如果报错就+1
  		count++;
  		
  
//下面是如果成功,就把count重置,

逻辑大概是这样子的,不过Hystrix里面不是用try catch 做的,他是用代理做的。

这里有一点需要注意,你的Spring cloud 版本不能超过Spring Cloud 2020.0
需要了解的朋友可以看下面文档或者官方文档

Spring Cloud 2020.0发布,移除了Hystrix、Zuul等Netflix组件

不说了,咱们直接上代码

1. 先引入依赖

<dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
         <version>2.2.6.RELEASE</version>
</dependency>

2. 然后写一个HystrixTest类

并继承HystrixCommand类,并实现其中的run方法,和getFallback方法,其中run方法是跑逻辑的
run方法执行中报错的时候,才会进入getFallback方法,就相当于try catch 的逻辑
Hystrix 可以脱离spring cloud 独立使用

代码如下:

public class HystrixTest  extends HystrixCommand //单机版Hystrix

    protected HystrixTest(HystrixCommandGroupKey group) 
        super(group);
    
    @Override
    protected Object run() throws Exception 
        System.out.println("执行逻辑-相当于try");
        int i=100/0;//让它报个错,测试一下
        System.out.println("i:"+i);
        return "xxoo";
    

    @Override
    protected Object getFallback()
        System.out.println("备用逻辑-相当于catch");
        return "报错了";
    

    public static void main(String[] args) 
        //	HystrixTest hystrixTest = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("ext"));
        /**
         * execute():以同步阻塞方式执行run()。以demo为例,调用execute()后,
         * hystrix先创建一个新线程运行run(),
         * 	接着调用程序要在execute()调用处一直阻塞着,直到run()运行完成
         */
        //	System.out.println("result:" + hystrixTest.execute());
        /**
         * queue():以异步非阻塞方式执行run()。以demo为例,
         * 	一调用queue()就直接返回一个Future对象,
         * 	同时hystrix创建一个新线程运行run(),
         * 	调用程序通过Future.get()拿到run()的返回结果,
         * 	而Future.get()是阻塞执行的
         */
        Future<String> futureResult = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("ext")).queue();
        String result = "";
        try 
            result = futureResult.get();
         catch (InterruptedException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
         catch (ExecutionException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
        System.out.println("程序结果:"+result);
    


Hystrix整合Resttemplate

我这里是开了三个服务
一个是服务注册中心
一个是服务调用方
一个是服务提供方
在SpringCloud中使用Hystrix,需要在入口文件里加上@EnableCircuitBreaker 注解,

@SpringBootApplication
@EnableCircuitBreaker //使用Hystrix 需要加上这个注解
public class EurekaconsumerApplication 

创建一个RestTemplateController

做为服务调用方,用来请求服务提供方
我们在定义一个 getRestTemplate() 方法来里面使用 RestTemplate 去请求服务方的接口
在方法上加上注解 @HystrixCommand(fallbackMethod = “getRestTemplatefallback”) 说明这个方法我Hystrix 罩着了
里面属性 fallbackMethod 对应的名称是方法名,如果当前请求报错,则进入备用方法
其实比较简单的

/**
 * @Auther: wan
 * @Date: 2021/2/23 16:33
 * @Description: com.wan.eurekaconsumer.controller restTemplate接口调用测试
 * @version: 1.0
 */
@RestController
public class RestTemplateController 
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/getRestTemplate")
    @HystrixCommand(fallbackMethod = "getRestTemplatefallback")
    public Object getRestTemplate()

        String url ="http://provider/getMap";
        //restTemplate.getForObject 和restTemplate.getForEntity 区别就在于返回值不同,一个返回字符串,一个返回一个对象
        String respStr = restTemplate.getForObject(url, String.class);
        System.out.println("respStr:"+respStr);
        return respStr;
    

    private String getRestTemplatefallback()
        System.out.println("报错,进入备用方案。");
        return "error";
    

我们在服务提供方,就是 RestTemplate 调用的这个url的服务
我们需要在里面报个错,然后验证 getRestTemplatefallback 方法就可以了

Hystrix 整合 Feign

如果不知道Feign是啥,可以阅读我上一篇文章,如果没搭Feign的可以先去阅读,因为我们下面都是基于 Feign 整合 Hystrix

Spring Cloud Feign使用介绍

Feign依赖里面自带了 Hystrix 包,所以把 Feign 搭起来了就不需要在引入依赖了

入口文件也不需要加 @EnableCircuitBreaker 注解,有 @EnableFeignClients 这个Feign的注解也已经够了

  1. 先创建一个类 UserProviderFallback ,并实现我们开始定义好的 Feign API 接口UserApiService,用来统一接受错误请求的
@Component
public class UserProviderFallback implements UserApiService 
    //继承UserApiService接口,实现其中的所有方法,所有
    @Override
    public String getWan() //我这里改一个方法用来测试
        return "降级了-getWan";
    
    
    @Override
    public String getWanParams(String name) 
        return null;
    
    @Override
    public String postWan() 
        return null;
    
    @Override
    public String postWanParams(String name) 
        return null;
    

我把UserApiService 类给你们贴出来把,其实和以前Spring Mvc 都差不多的

UserApiService 类是用Feign来进行远程调用接口的,调用的是 user-provider 这个服务
如果要Feign要用Hystrix ,则加上属性 fallback = UserProviderFallback.class
类名就是上面我们自己定义的类名

/*
   @FeignClient(name = "wanFeign",url = "http://localhost:8003")
  如果结合eureka ,在FeignClient 注解中就可以不用写url,直接name指定服务名称就可以直接调用了
 */
@FeignClient(name = "user-provider",fallback = UserProviderFallback.class)
public interface UserApiService 

    @GetMapping("/getWan")
    public String getWan();

    @GetMapping("/getWanParams")
    public String getWanParams(@RequestParam("name")String name);

    @PostMapping("/postWan")
    public String postWan();

    @PostMapping("/postWanParams")
    public String postWanParams(@RequestParam("name")String name);

Controller 类也给你们贴出来

/**
 * @Auther: wan
 * @Date: 2021/2/24 11:13
 * @Description: com.wan.userconsumer.controller -测试 用 open Feign 请求
 * @version: 1.0
 */
@RestController
@RequestMapping("/user")
public class MainController 

//    @Autowired
//    UserApiService userApiService;

    //通过eureka服务名称请求
    @Autowired
    UserApiService userApiService;

    //服务直连,不通过eureka,直接进行请求
    @GetMapping("/getWan")
    public String getWan()
        return userApiService.getWan();
    

    //通过eureka服务名称请求-
    @GetMapping("/getWan2")
    public String getWan2()
        return userApiService.getWan();
    

    //通过eureka服务名称请求-带参请求
    @GetMapping("/getWanParams")
    public String getWanParams()
        String name="Hi!! wan";
        return userApiService.getWanParams(name);
    

    //通过eureka服务名称请求-post无参请求
    @GetMapping("/postWan")
    public String postWan()
        return userApiService.postWan();
    

    //通过eureka服务名称请求-post带参请求
    @GetMapping("/postWanParams")
    public String postWanParams()
        String name="Hi!! wan";
        return userApiService.postWanParams(name);
    

这样我们的服务调用方的代码就写好了,但是我们需要调用服务提供方的接口测试一下吧
服务提供方的 Controller 类

/**
 * @Auther: wan
 * @Date: 2021/2/24 11:00
 * @Description: com.wan.userprovider.controller
 * @version: 1.0
 */
@RestController
public class UserController 

    //无参get方法
    @GetMapping("/getWan")
    public String getWan() throws InterruptedException 
        int i=1/0;//在这里让请求报个错
        return "Hi wan 调用方式为:get!!";
    

    //带参get方法
    @GetMapping("/getWanParams")
    public String getWanParams(String name)
        return "Hello !! 调用方式为:get ,参数为:"+name;
    

    //无参post方法
    @PostMapping("/postWan")
    public String postWan()
        return "Hi wan 调用方式为:post!!";
    

    //带参post方法
    @PostMapping("/postWanParams")
    public String postWanParams(String name)
        return "Hi wan 调用方式为:post,参数为:"+name;
    

效果

服务提供方程序里面报错了,但是并没有把错误返回给前端,只是进行服务降级了
好处就是不会把赤裸直接的错误返回给前端页面,当服务出问题的时候,给前端返回一些相对友好的信息,比如返回一些信息页面,让用户稍后在试。

以上是关于Spring Cloud Hystrix介绍与使用的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Hystrix理解与实践:搭建简单监控集群

Spring Cloud实战之初级入门— 利用Hystrix实现服务熔断与服务监控

Spring Cloud 学习——5.使用 feign 的 hystrix 支持

微服务架构之spring cloud hystrix&hystrix dashboard

Spring Cloud总结22.Hystrix Dashboard的使用(上)

Spring Cloud笔记 断路器-hystrix