springcloud

Spring Cloud Hystrix断路器

2019-10-28  本文已影响0人  yunqing_71

Hystrix介绍

当微服务之间调用的时候,假设A服务调用B服务,B服务调用C服务,如果调用链路上的任何一环出现异常响应时间过长或者服务不可用,就会造成调用方长时间得不到响应或者错误异常响应而占用线程,占用系统资源过多甚至会引起服务雪崩。

Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。

话不多说,开干!!!

  1. 创建hystrix-service模块,模块的创建过程参考Spring Cloud Eureka服务注册中心

    添加依赖有:

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--这个依赖是我之后介绍使用WebClient代替RestTemplate需要引入的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    
  2. 添加application.yml配置


server:
  port: 8401 #端口号
spring:
  application:
    name: hystrix-service #服务名称

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8001/eureka/ #注册中心地址

#这是我声明的一个值,在springboot中通过@Value注解可调用
service-url:
  emp-service: http://emp-service # emp-service的调用路径,cloud的模块之间的调用要用服务名称

3.需要通过启动类加上@EnableCircuitBreaker启用对断路器的支持

/**
 * @EnableCircuitBreaker 开启断路器功能
 */
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {

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


}

4.参考上一篇文章给RestTemplate和WebClient负载均衡的能力,也就是加入RibbonConfig类

@Configuration
public class RibbonConfig {

    /**
     * @LoadBalanced 使 WebClient能够进行负载均衡
     * @return
     */
    @Bean
    @LoadBalanced
    public WebClient.Builder webClient() {
        return WebClient.builder();
    }

    /**
     * @LoadBalanced 使 RestTemplate能够进行负载均衡
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

5.通过本模块调用emp-service的服务来讲解断路器的功能,添加HystrixController来调用emp-service的服务。首先是通过RestTemplate完成调用。

@RestController
@RequestMapping("/emp")
public class HystrixController {

    /**
     * 注入RestTemplate
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取application.yml中声明的emp-service的服务调用路径
     */
    @Value("${service-url.emp-service}")
    private String empServiceUrl;

    /**
     * @HystrixCommand 可以声明该方法需要使用断路器保护
     * fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
     * 注意这里的fallback必须和当前方法拥有相同的返回值,参数
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/{id}")
    public String getEmpById(@PathVariable Long id){

        return restTemplate.getForObject(empServiceUrl + "/emp/{1}", String.class, id);

    }



    public String fallback(@PathVariable Long id) {
        return "服务降级处理";
    }
}

@HystrixCommand 可以声明该方法需要使用断路器保护,然后fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法

6.启动这四个服务,然后访问http://localhost:8401/emp/11

image

查询员工返回200调用成功,刷新几次发现交替在8201和8202端口调用emp-service的根据id查询员工的服务

image

负载均衡的调用emp-service成功

image image

7.下面停掉8202端口的emp-service的服务,再次刷新

image

刷新发现会出现200服务降级处理两种结果,这是因为当负载均衡到8202端口的时候由于服务已经停掉,所以就开启了断路器,执行服务熔断降级,根据配置的降级处理调用fallback方法,所以返回服务降级处理这个结果。

image

8.刚才测试了直接关闭服务之后的降级处理,现在测试一下服务开启的状态,emp-service服务的代码中加一个运行时异常之后的处理结果。

image

重新启动emp-service服务,访问localhost:8401/emp/11

返回结果,服务降级处理

image

查看一下emp-service服务的控制台,发现报异常了,所以是出现异常然后断路器做了降级处理了。

image

9.restTemplate调用时通过@HystrixCommand(fallbackMethod = "fallback")设置服务降级处理,那如果通过WebClient调用呢?直接上代码!!!

@RestController
@RequestMapping("/emp")
public class HystrixController {

    /**
     * 注入webClient
     */
    @Autowired
    private WebClient.Builder webClient;

    /**
     * 注入RestTemplate
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取application.yml中声明的emp-service的服务调用路径
     */
    @Value("${service-url.emp-service}")
    private String empServiceUrl;

    /**
     * @HystrixCommand 可以声明该方法需要使用断路器保护
     * fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
     * 注意这里的fallback必须和当前方法拥有相同的返回值,参数
     * @param id
     * @return
     */
    //@HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/{id}")
    public Mono<String> getEmpById(@PathVariable Long id){

        Mono<String> call = webClient.build()
                .get()
                .uri(empServiceUrl + "/emp/{id}", id)
                .retrieve()
                .bodyToMono(String.class);
        Mono<String> mono = HystrixCommands
                .from(call)
                .fallback(Mono.just("服务降级处理"))
                .commandName("getEmpById")
                .toMono();
        return mono;
        //return restTemplate.getForObject(empServiceUrl + "/emp/{1}", String.class, id);

    }



    /*public String fallback(@PathVariable Long id) {
        return "服务降级处理";
    }*/
}

额。可以发现不需要注解@HystrixCommand,具体写法就是如上面代码,我也是找了好多资料才在https://stackoverflow.com上找到的。

@HystrixCommand won't really work, because Hystrix doesn't threat Mono/Flux any different from Java primitives.

Hystrix doesn't monitor content of Mono, but only the result of call public Mono<WeatherApiResponse> getWeatherByCityName(String cityName).

This result is always OK, because reactive-call-chain creation will always succeed.

What you need, is to make Hystrix threat Mono/Flux differently. In Spring Cloud, there is a builder, to wrap Mono/Flux with HystrixCommand.

Mono<WeatherApiResponse> call = this.getWeatherByCityName(String cityName);

Mono<WeatherApiResponse> callWrappedWithHystrix = HystrixCommands
                                .from(call)
                                .fallback(Mono.just(WeatherApiResponse.EMPTY))
                                .commandName("getWeatherByCityName")
                                .toMono();

项目源码见 https://github.com/kangqing/cloud

上一篇下一篇

猜你喜欢

热点阅读