spring cloud深入学习(五)-----熔断器Hystrix

什么是Hystrix?

在分布式系统中,服务与服务之间的依赖错综复杂,一种不可避免的情况就是某些服务会出现故障,导致依赖于它们的其他服务出现远程调度的线程阻塞。Hystrix提供了熔断器功能,能够阻止分布式系统中出现联动故障,通过隔离服务的访问点阻止联动故障的,并提供了故障的解决方案,从而提高 了整个分布式系统的弹性。

简单来说:微服务架构中存在很多的服务,例如A、B、C、D、E5个服务,并且对于一次调用来说,并不仅仅是访问一个服务,按照功能划分来说,每个服务做得事情是单一的,所以大部分的请求都是类似于A-C-E,跨过多个服务。

如果单个服务的正常运行几率为99.00%(机房的不可靠性、运营商的不可靠性等等),那么对于A-C-E调用流程来说,几率为99.00%*99.00%*99.00%=97.03%,也就意味着10000次请求有300多次会失败,问题还是比较严重的。

Hystrix设计原则

  • 防止单个服务的故障耗尽整个服务的Servlet容器(例如Tomcat)的线程资源。
  • 快速失败机制,如果某个服务出现了故障,则调用该服务的请求快速失败,而不是线程等待。
  • 提供回退(fallback)方案,在请求发生故障时,提供设定好的回退方案。
  • 使用熔断机制,防止故障扩散到其他服务。
  • 提供熔断器的监控组件Hystrix Dashboard ,可以实时监控熔断器的状态。

Hystrix工作机制

  • 当服务的某个 API 接口的失败次数在一定时间内小于设定的阀值时,熔断器处于关闭状态,该 API 接口正常提供服务;
  • 当该API 接口处理请求的失败次数大于设定的阀值时, Hystrix 判定该 API 接口出现了故障,打开熔断器,这时请求该 API接口会执行快速失败的逻辑(即 fall back 回退的逻辑),不执行业务逻辑,请求的线程不会处于阻塞状态;
  • 处于打开状态的熔断器一段时间后会处于半打开状态,并将一定数量的请求执行正常逻辑。剩余的请求会执行快速失败,若执行正常逻辑的请求失败了,则熔断器继续打开;若成功了 ,则将熔断器关闭。这样熔断器就具有了自我修复的能力。

使用方法

1、在RestTemplate和Ribbon上使用熔断器

找到本人之前的eureka-consumer项目,gitbub地址:https://github.com/ali-mayun/eureka-consumer

新加依赖:

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

运行主类:

package com.ty.eurekaconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

@EnableEurekaClient
@SpringBootApplication
//使用这注解开启hystrix的功能
@EnableHystrix
public class EurekaConsumerApplication {

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

}

更改之前的service

package com.ty.eurekaconsumer.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.client.RestTemplate;

@Service
public class RibbonService {

    @Autowired
    private RestTemplate restTemplate;

    //使用@HystrixCommand注解启用熔断器的功能,当服务熔断后,将不调用远程服务,直接调用本地的handleError方法
    @HystrixCommand(fallbackMethod = "handleError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://eureka-provider/firstCall?name=" + name, String.class);
    }

    public String handleError(String name) {
        return name + "在访问远程服务时,被熔断了,调用本地的方法";
    }
}

现在启动eureka-server、eureka-consumer两个项目,不启动eureka-provider,制造服务不能用的假象,运行效果如下:

2、在feign上使用熔断器

上面一种方式相对来说还是比较麻烦的,使用feign则方便很多。

打开上篇博文中的eureka-feign-consumer项目,github地址:https://github.com/ali-mayun/eureka-feign-consumer

修改application.properties文件,开启Hystrix功能:

# 服务名称
spring.application.name=eureka-feign-consumer
# 端口号
server.port=4001
# 服务注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
# 增加Hystrix功能
feign.hystrix.enabled=true

更改EurekaClientFeign:

package com.ty.eurekafeignconsumer.service;

import com.ty.eurekafeignconsumer.config.FeignConfig;
import com.ty.eurekafeignconsumer.hystrix.BaseHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

//新增fallback属性指定出现熔断由哪个类来处理。
@FeignClient(value = "eureka-provider", configuration = FeignConfig.class, fallback = BaseHystrix.class)
public interface EurekaClientFeign {

    //只需要在接口中定义方法即可。调用eureka-provider服务的firstCall方法,并且feign集成了Ribbon
    @GetMapping(value = "/firstCall")
    String sayHiFromClientEureka(@RequestParam("name") String name);
}

BaseHystrix:

package com.ty.eurekafeignconsumer.hystrix;

import com.ty.eurekafeignconsumer.service.EurekaClientFeign;
import org.springframework.stereotype.Component;

//熔断处理类需要实现调用远程服务的service,例如本案例在EurekaClientFeign类中调用远程服务,就需要实现此接口,
//然后相同名字的方法就可以一一对应
@Component(value = "baseHystrix")
public class BaseHystrix implements EurekaClientFeign {
    @Override
    public String sayHiFromClientEureka(String name) {
        return name + "访问的远程服务被熔断";
    }
}

controller中有个坑,就是EurekaClientFeign是一个接口,主类中使用@EnableFeignClients开启了feign功能,该注解会扫描@FeignClient,将使用到该注解的接口通过动态代理的方式创建类,因此EurekaClientFeign接口就会有两个实现类。

另一个是BaseHystrix,用来处理发生熔断后的逻辑,因此这里使用@Autowired就出错了,但是使用@Resource就ok,但是暂不清楚为啥会正确的找到动态代理生成的那个类,后续研究。。。

package com.ty.eurekafeignconsumer.controller;

import com.ty.eurekafeignconsumer.service.EurekaClientFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class FeignController {

    //这个地方有点坑,因为EurekaClientFeign已经有另外一个实现类就是BaseHystrix,使用@Autowired会报错
    //不过暂时不清楚@Resource会注入正确的bean,后续待研究。。。
    @Resource
    private EurekaClientFeign eurekaClientFeign;

    @GetMapping("/hi")
    public String sayHi(@RequestParam(value = "name", defaultValue = "马云", required = false) String name) {

        return eurekaClientFeign.sayHiFromClientEureka(name);
    }
}

当eureka-provider正常开启,效果如下:

关闭eureka-provider,访问效果如下:

猜你喜欢

转载自www.cnblogs.com/alimayun/p/10841541.html