SpringCloud之Hystrix的基础使用

发布于:2022-12-21 ⋅ 阅读:(444) ⋅ 点赞:(0)

雪崩概述:

1)当用户访问A服务的一个url接口时,部署A服务的tomcat会给用户分配一个线程,支持用户访问。

2)A服务要完成用户的操作,又需要访问B服务。

3)A服务又去访问B服务,部署B服务的tomcat会给A服务分配一个线程,支持A服务的访问。

4)B服务要完成A服务的操作,又需要访问C服务。

5)B服务又去访问C服务,但是C服务挂了;B服务在访问C服务之前不知道C服务挂了,B去访问,直到超时,才知道C服务无法访问。

结果:

因为C服务不可用,导致B服务的线程不能及时回收,从而导致A服务的线程也不能及时回收,最终导致整个服务链的线程池中没有线程可用了;此时如果再有用户访问A服务,那么tomcat会直接报503(服务不可用)。— 这就是服务雪崩,也叫服务的联动故障,服务的故障传播。

解决思路:

服务雪崩的本质其实是线程没有及时回收;不管是调用成功还是失败,只要线程能够及时回收,就可以解决服务雪崩问题。

  1. 方案一

    ​ 将服务间的调用超时时长改小,这样就可以让线程及时回收 — 等待时间太久了不等了。

    ​ 优点:非常简单,也可以有效的解决服务雪崩。

    ​ 缺点:不够灵活,有的服务就是需要等待较长的时间,由于调用超时时长小,还没有访问到服务,线程就会被回收了。

  2. 方案二

    ​ 设置拦截器:B服务调用C服务时会先经过拦截器,拦截器知道C服务的状态,在拦截器中会对C服务的状态进行判断,如果C服务正常则继续调用,如果C服务挂了则直接return。Hystrix的实现方案就是使用拦截器。

与OpenFign一起使用:

第一步:pom文件

Hystrix常和OpenFeign、Ribbon一起使用。
但是OpenFeign集成了Hystrix和Ribbon,所以只需要导入OpenFeign的依赖即可
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
hystrix的单独依赖
        <!--引入的hystrix的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

第二步:创建在OpenFeign中声明的接口的实现类,并注入IOC容器

@Component
public class UserOrderHystrix implements UserOrderFeign {
    //重写方法
}

第三步:修改OpenFegin中创建的接口,添加属性fallback,值为实现类的class实例

/*
  1.接口名称为UserOrderFeign,表示是user-service服务访问order-service服务
    的feign接口;
  2.@FeignClient(value = "order-service")指定提供者服务(被访问的服务)的
    应用(服务)名称;
    fallback = UserOrderHystrix.class指定备选方案为实现类UserOrderHystrix;
 */
@FeignClient(value = "order-service", fallback = UserOrderHystrix.class)
public interface UserOrderFeign {

    /*
      3.指定提供者服务处理请求的方法签名:
        指定提供者服务处理请求的方法的名称、参数、返回值类型、标注的注解、
        处理的请求的url -- 除了没有方法体以外,其它的和提供者服务处理请求
        的方法一模一样;
     */
    @RequestMapping("/doOrder")
    public String doOrder();

第四步:在controller中注入OpenFegin创建的接口

/*
      注入UserOrderFeign:
      会爆红,因为容器中目前有两个UserOrderFeign的bean对象,一个是OpenFeign
      为其提供的代理对象,一个是我们定义的UserOrderFeign接口的实现类UserOrderHystrix
      的bean对象 --- 不用管
     */
    @Autowired
    private UserOrderFeign userOrderFeign;

	//处理/userDoOrder的请求,并向客户端响应字符串文本
    @RequestMapping("/userDoOrder")
    public String userDoOrder(){
        /*
          调用UserOrderFeign的doOrder()方法,即向order-service服务发出了
          /doOrder的请求,并接收了其响应的字符串文本;

          如果order-service服务正常,则执行order-service服务中处理/doOrder
          请求的doOrder()方法;如果order-service服务出现问题,则执行我们定义
          的UserOrderFeign接口的实现类UserOrderHystrix中重写的doOrder()方法;
         */
        String result = userOrderFeign.doOrder();
        //将接收的order-service服务的/doOrder请求响应的字符串文本再响应给客户端
        return result;
    }

第五步:修改application.properties

#开启hystrix断路器
feign.hystrix.enabled=true

注意事项:

#因为是与OpenFeign一起使用的,所以主启动类上一定不能忘记添加注解
@EnableFeignClients

与Ribbon一起使用:

第一步:pom文件

Hystrix常和OpenFeign、Ribbon一起使用。
但是OpenFeign集成了Hystrix和Ribbon,所以只需要导入OpenFeign的依赖即可
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
hystrix的单独依赖
        <!--引入的hystrix的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

第二步:主启动类

//开启Hystrix断路器:或者使用@EnableCircuitBreaker,因为@EnableHystrix中包含的就是@EnableCircuitBreaker
@EnableHystrix
//标注@EnableFeignClients注解,表示此应用(服务)是被feign管理的客户端服务
@EnableFeignClients
//标记此应用(服务)为eureka客户端
@EnableEurekaClient
@SpringBootApplication
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

第三步:配置类

    //配置RestTemplate的bean对象到容器,使用其可以发送请求
    @Bean
    //标注@LoadBalanced注解,表示让ribbon来管理RestTemplate
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

第四步:controller中

 	//注入RestTemplate
    @Autowired
    private RestTemplate restTemplate;
	//声明备选方案
    public String userDoOrder2RibbonHytrix(String serverName){
        return "下单失败喽";
    }
	//备选方案方法声明要求:修饰符,返回值,属性名,属性个数必须与方法保持一致,不用加注解
	//本来的url访问的处理方法上添加注解@HystrixCommand
	//给出属性值(fallbackMethod = "备选方案的方法名")
/*
      指定备选方案为userDoOrder2RibbonHytrix()方法:
      如果order-service服务正常,则执行order-service服务中处理/doOrder请求的
doOrder()方法;如果order-service服务出现问题,则执行userDoOrder2RibbonHytrix()
方法;
     */
    @HystrixCommand(fallbackMethod = "userDoOrder2RibbonHytrix")
    @RequestMapping("/userDoOrder2")
    public String userDoOrder2(String serverName){
        //组装url
        String url = "http://" + serverName + "/doOrder";
        //发出请求,并接收order-service服务的/doOrder请求响应的字符串文本
        String result = restTemplate.getForObject(url, String.class);
        //将接收的order-service服务的/doOrder请求响应的字符串文本再响应给客户端
        return result;
    }
遇到的一个bug,导入依赖后,无法使用@HystrixCommand注解
解决方法:import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
在最上边手动导入,导入后会报错,idea使用alt+enter后会自动去修改pom文件,再idea自动修改后,这个注解就可以使用了
遇到的小问题:在使用ribbon实现服务调用的时候,如果被调用方是一个集群,根据负载均衡,会将url依次分发,但是将其中某一台服务挂掉以后,负载均衡发送的请求依旧发送到了挂了的服务器,没有将其发送到正常的服务器。
原因:因为调用方会周期性的拉去注册中心中的服务列表,所以在被调用的服务挂掉后,而还没到时间去注册中心再次拉取服务列表,导致这样一个小bug

注意事项:

#主启动类上注解:
#开启Hystrix断路器:或者使用@EnableCircuitBreaker,因为@EnableHystrix中包含的就是@EnableCircuitBreaker
@EnableHystrix
#标注@EnableFeignClients注解,表示此应用(服务)是被feign管理的客户端服务
@EnableFeignClients
#不能忘!!!!

相关概念:

  1. 降级:是指当请求超时、资源不足等情况发生时,进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条的完整,避免服务雪崩。

    //OpenFegin版
    @FeignClient(value = "服务名", fallback = 实现类.class)
    //Ribbon版:
    @HystrixCommand(fallbackMethod = "controller中声明的备选方法名")
    
  2. 熔断:当一定时间内,异常请求(请求超时、网络故障、服务异常等)比例达到阈值时,启动熔断器,熔断器一旦启动,则会停止调用具体服务逻辑,通过fallback快速返回托底数据(降级),保证服务链的完整,避免服务雪崩。

    ​ 熔断器有自动恢复机制。如:当熔断器启动后,每隔5秒,尝试将新的请求发送给服务,如果服务可正常执行并返回结果,则关闭熔断器,服务恢复;如果仍旧调用失败,则继续返回托底数据,熔断器持续开启状态。

    ​ 降级仅是出错了返回托底数据;而熔断是出错后开启熔断器返回托底数据,且在一定时间内不再访问服务。

  3. 资源隔离策略:Hystrix的资源隔离策略有两种:线程池信号量

    ​ **使用线程池:**当A服务的某个任务线程出现阻塞的时候,该服务的任务线程会被设置为不可用,但该A服务的其他任务线程依旧可以使用,避免了一旦某个任务出现阻塞导致整个A服务直接崩掉。

    ​ **使用信号量:**采用信号量隔离策略,每接收一个请求,都是服务自身线程去直接调用被访问服务,信号量就相当于一个关卡,每个线程通过关卡后,信号量数量减1,当为0时不再允许线程通过,而是直接执行fallback返回托底数据,仅仅做了一个限流;

常用的其他配置:

其他配置:

hystrix.command.default.execution.isolation.Strategy
#配置资源隔离策略,THREAD线程池(默认值),SEMAPHORE信号量。

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
#设置请求超时时长,默认1000毫秒。

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests
#设置最大并发请求数(即线程数),默认10。

hystrix.command.default.circuitBreaker.enabled
#设置是否启用熔断器机制,默认true。

hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds
#设置当熔断器启动后,再次尝试请求服务的窗口时间(间隔时间),默认5000毫秒。

hystrix.command.default.circuitBreaker.requestVolumeThreshold
#设置请求失败启用熔断器的阈值,即在窗口时间内请求失败多少次启用熔断器,默认20。

hystrix.command.default.circuitBreaker.errorThresholdPercentage
#设置请求失败启用熔断器的比例阈值,即在窗口时间内请求失败次数达到此比例值则启动熔断器,默认50。

说明:
default是全局配置,即对提供者服务所有url接口的方法的调用的配置;如果仅对提供者服务指定url接口的方法的调用进行配置,则将default改为提供者服务对应url接口的方法名。