天天看点

SpringCloud浅尝(八)——Hystrix

Hystrix 具备服务降级、服务容错、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。

多层服务调用中,较低级别的服务中的服务故障可能导致用户级联故障。当对特定服务的呼叫达到一定阈值时(Hystrix中的默认值为5秒内的20次故障),电路打开,不进行通话。在错误和开路的情况下,开发人员可以提供后备。

下图来自官方网站

SpringCloud浅尝(八)——Hystrix

Hystrix特性

  1.请求熔断: 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN).这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). 

  2.服务降级:Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.告知后面的请求服务不可用了,不要再来了。

  3.依赖隔离(采用舱壁模式,Docker就是舱壁模式的一种):在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池.比如说,一个服务调用两外两个服务,你如果调用两个服务都用一个线程池,那么如果一个服务卡在哪里,资源没被释放。后面的请求又来了,导致后面的请求都卡在哪里等待,导致你依赖的A服务把你卡在哪里,耗尽了资源,也导致了你另外一个B服务也不可用了。这时如果依赖隔离,某一个服务调用A B两个服务,如果这时我有100个线程可用,我给A服务分配50个,给B服务分配50个,这样就算A服务挂了,我的B服务依然可以用。

  4.请求缓存:比如一个请求过来请求用户的数据,你后面的请求也过来请求同一个用户的数据,这时我不会继续走原来的那条请求链路了,而是把第一次请求缓存过了,把第一次的请求结果返回给后面的请求。

  5.请求合并:我依赖于某一个服务,我要调用N次,比如说查数据库的时候,我发了N条请求发了N条SQL然后拿到一堆结果,这时候我们可以把多个请求合并成一个请求,发送一个查询多条数据的SQL的请求,这样我们只需查询一次数据库,提升了效率。

我们前面EurekaDiscovery2实例中,使用了Feign来调用EurekaDiscovery中的接口,Feign内部已包含了hystrix。下面我们先使用Feign来实现降级融断。

在EurekaDiscovery2配置中添加

feign:
  hystrix:
    enabled: true  # 开启hystrix
           

我们添加一个Fallback类并实现HelloRemote接口

@Component
public class HelloFallback implements HelloRemote {
    @Override
    public String home(@RequestParam("name") String name) {
        return name+"现在太拥挤了, 请稍后重试~-Feign";
    }
}
           

我们指定fallback类为HelloFallback 

@FeignClient(name= "eurekadiscovery", fallback = HelloFallback.class)
public interface HelloRemote {
    @GetMapping("/hi")
    public String home(@RequestParam("name") String name);
}
           

下面我们启动实例来验证一下

正常调用:

SpringCloud浅尝(八)——Hystrix

我们模拟EurekaDiscovery挂掉(关闭),触发了服务降级

SpringCloud浅尝(八)——Hystrix

超时导致服务降级,默认feign调用超时是1000毫秒,我们EurekaDiscovery中将线程sleep1000豪秒,同样出现了上面一样的服务降级。

我们可以通下面的配置来设置超时时间

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
           

通过调整hystrix的超时时间,服务能正常调用

SpringCloud浅尝(八)——Hystrix

当我们没有使用Feign时也可直接引用Hystrix组件来实现在,我们在项目中引入spring-cloud-starter-netflix-hystrix

启动类中我们添加注解@EnableCircuitBreaker 允许断路器

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class EurekaDiscovery2Application {

	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}

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

}
           

我们通过@HystrixCommand

@HystrixCommand(fallbackMethod = "fallback")
    public String home(@RequestParam("name") String name)
    {
        return restTemplate.getForEntity("http://EUREKADISCOVERY/hi?name="+name,String.class).getBody();
    }

     public String fallback(String name) {
        return name+",现在太拥挤了, 请稍后重试~";
    }
           

我们启动实例,能正常调用

SpringCloud浅尝(八)——Hystrix

我们模拟EurekaDiscovery挂掉(关闭),触发了服务降级

SpringCloud浅尝(八)——Hystrix

我们还可以通过@DefaultProperties(defaultFallback = "defaultFallback"),来指定默认的FallBack函数,这样就不需要每个 @HystrixCommand 注解都指定一个FallBack函数了

@RestController
@DefaultProperties(defaultFallback = "defaultFallback")
public class HelloController {
    @Autowired
    private  RestTemplate restTemplate;


    @GetMapping("/hi")
    @HystrixCommand
    // @HystrixCommand(fallbackMethod = "fallback")
    public String home(@RequestParam("name") String name)
    {
        return restTemplate.getForEntity("http://EUREKADISCOVERY/hi?name="+name,String.class).getBody();
    }

    public String fallback(String name) {
        return name+",现在太拥挤了, 请稍后重试~";
    }

    public String defaultFallback()
    {
        return "现在太拥挤了, 请稍后重试~";
    }
           

这样我们降级后就会调用默认的FallBack函数

SpringCloud浅尝(八)——Hystrix

我们还可以通过@HystrixCommand来设定超时时间,我们一般都是采用在配置文件中设置超时间时间。

@HystrixCommand(commandProperties = {
            // 设置超时时间为3秒
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
           
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    home:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000
           

继续阅读