1、简介

Hystix,即熔断器。

主页:https://github.com/Netflix/Hystrix/

【 Hystix 熔断 】

Hystix 是 Netflix 开源的一个延迟和容错库用于隔离访问远程服务、第三方库,防止出现级联失败

【 Hystix 熔断 】

2、雪崩问题

微服务中,服务间调用关系错综复杂,一个请求,可能需要调用多个服务接口才能实现,会形成非常复杂的调用链路:
【 Hystix 熔断 】
如图,一次业务请求,需要调用 A、P、H、I 四个服务,这四个服务又可能调用其它服务。
如果此时,某个服务出现异常:
【 Hystix 熔断 】
例如微服务 I 发生异常,请求阻塞,用户不会得到响应,则 tomcat 的这个线程不会释放,于是 越来越多的用户请求到来,越来越多的线程会阻塞:
【 Hystix 熔断 】
服务器支持的线程和并发数有限,请求一致阻塞,会导致服务器资源耗尽,从而导致所有其他服务都不可用,形成雪崩效应。

Hystix 解决雪崩效应的手段有两个:

  • 线程隔离
  • 服务熔断

3、线程隔离、服务降级

原理

线程隔离示意图:
【 Hystix 熔断 】
解读:
Hystrix 为每个依赖服务调用分配一个小的线程池,如果线程池已满,调用将立即被拒绝,默认不采用排队、加速失败判定时间。

用户的请求将不再直接访问服务,而是通过线程池中的闲置线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理,什么是服务降级?

服务降级:优先保证核心服务,而非核心服务不可用或弱可用。

用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息)。

服务降级虽然会导致请求失败,但不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其他服务没有响应。

触发 Hystrix 服务降级的情况:

  • 线程池已满
  • 请求超时

4、动手实践 —— 在服务的消费方降级 —— 自己失败

引入依赖

首先在user-consumer中引入 Hystix 依赖:

<!-- Hystix 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

开启熔断

启动项加注解 @EnableCircuitBreaker

package cn.ys;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient // 开启 Eureka 客户端
@EnableCircuitBreaker // 开启熔断
@SpringBootApplication
public class ConsumerApplication {

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

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

}

可以用 注解 @SpringCloudApplication 替代上面的三个注解
【 Hystix 熔断 】

改造消费者

我们改造 user-consumer,添加一个用来访问的 user 服务的 DAO,并且声明一个失败时的回滚处理函数:

package cn.ys.consumer.controller;

import cn.ys.consumer.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("{id}")
    @HystrixCommand(fallbackMethod = "queryByIdFallback")
    public User queryById(@PathVariable("id") Long id ){

        // 获取ip和端口信息
        String url = "http://user-service/user/" + id;
        User user = restTemplate.getForObject(url, User.class);
        return user;
    }

    public User queryByIdFallback(Long id ){

        User user = new User();
        user.setId(id);
        user.setName("不好意思,服务器拥挤!");
        return user;
    }

}

【 Hystix 熔断 】

  • @HystrixCommand(fallbackMethod="queryByIdFallback"):声明一个失败回滚处理函数queryByIdFallback,当queryById执行超时(默认是1000毫秒),就会执行queryByIdFallback函数,返回错误提示。
  • 为了方便查看熔断的触发时机,我们记录请求访问时间。

改造服务提供者

改造服务提供者,随机休眠一段时间,模拟超时,以触发熔断
【 Hystix 熔断 】

启动测试

【 Hystix 熔断 】

如果有多个方法,每个方法都对应一个 降级的逻辑,那么太多了。

优化 —— 通用的降级逻辑

package cn.ys.consumer.controller;

import cn.ys.consumer.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("consumer")
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("{id}")
    @HystrixCommand
    public String queryById(@PathVariable("id") Long id ){

        // 获取ip和端口信息
        String url = "http://user-service/user/" + id;
        String s = restTemplate.getForObject(url, String.class);
        return s;
    }

    public String defaultFallback(){
        return "不好意思,服务器拥挤!";
    }

}

【 Hystix 熔断 】

【 Hystix 熔断 】

优化:自定义超时时长

如果某个方法查询的对应时间比较长,单独设置超时时长:

【 Hystix 熔断 】

全局超时时长设置:

虽然熔断实现了,但是我们的重试机制似乎没有生效,是这样吗?

其实这里是因为我们的 Ribbon 超时时间设置的是1000ms:

【 Hystix 熔断 】

而 Hystix 的超时时间默认也是1000ms,因此重试机制没有被触发,而是先触发了熔断。

所以,Ribbon 的超时时间一定要小于 Hystix 的超时时间。

我们可以通过hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds来设 置Hystrix 超时时间。

hystrix:
  command:
  	default:
        execution:
          isolation:
            thread:
              timeoutInMillisecond: 2000 # 设置hystrix的超时时间为2000ms

也可以设置单独的方法超时时长:

hystrix:
  command:
  	user-server:
        execution:
          isolation:
            thread:
              timeoutInMillisecond: 2000 # 设置hystrix的超时时间为2000ms

5、服务熔断:

熔断原理:

熔断器,也叫断路器,其英文单词为:Circuit Breaker
【 Hystix 熔断 】

Hystrix 的熔断状态机模型:
【 Hystix 熔断 】
【 Hystix 熔断 】

模拟环境:
【 Hystix 熔断 】
【 Hystix 熔断 】

相关文章: