目录
- 雪崩问题
- 服务降级
- 原理
- 实践
- order降级处理
- user降级处理
 
 
- 服务熔断
- 原理
- 实践
 
hystrix,英文意思是豪猪,全是是刺,一种保护机制,即熔断器。
主页:https://github.com/Netflix/Hystrix/
雪崩问题
在微服务中,服务与服务之间的调用是错综复杂的,一个请求,可能需要调用多个微服务的接口才能实现,会形成非常复杂的调用链路。
 如图,一次业务请求,需要调用A、P、H、I四个服务,这四个服务又可能调用其他服务。如果此时,某个服务出现异常,例如微服务I发送异常,请求阻塞,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞。而tomcat的最大连接数不能超过700(默认200)。
 
 如果此时更多的请求阻塞到一个服务器下,当请求耗尽之后,会消耗其他服务器线程从而导致其它服务器都不可用,就形成了雪崩效应。
服务降级
原理
Hystrix 为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队。 用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满 ,或者请求超时 ,则会进行降级处理。
 什么是服务降级?
 服务降级:优先保证核心服务,而非核心服务不可用或弱可用。
- 用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息) 。
- 服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。
- 触发 Hystrix 服务降级的情况:
 1. 线程池已满
 2. 请求超时
 3. 服务爆炸(宕机)
实践
order降级处理
注意: 是服务的消费者请求服务担心返回异常,所以需要在消费者一方配置自己的降级处理,所以我们的操作在消费者一方。
 首先引入Hystix依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
开启服务降级
 加注解:@EnableCircuitBreaker //开启服务降级
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //开启服务降级
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class);
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

 因为我们默认使用了hystrix 所以我们应该处理一下,如果失败之后返回的信息,那么我们应该重新改造一下方法:
 controller:
    /**
     * 根基ID查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
   @HystrixCommand(fallbackMethod = "ww") //降级处理方法
    public Order byId(@PathVariable("id") Long id) {
         Order order = orderService.getById(id);
        User user = restTemplate.getForObject("http://user-service/user/" + order.getUserId(), User.class);
         order.setUser(user);
        return order;
    }
    /**
     * 降级的处理方法,要求方法的返回值,参数都要一致
     * @param id
     * @return
     */
    public Order ww(@PathVariable Long id){
        return  new Order();
    }

 为了能够模拟超时,我们需要改造一下user-service 有意让其返回结果慢一点:
 
 @GetMapping("/{id}")
    public User byId(@PathVariable("id") Long id) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return userService.getById(id);
    }
结果:
 因为user的查询超时,所以order降级处理返回了一个空的order对象
 
 到了这里大家就应该能明白,Hystrix给我们做的功能就是为了避免雪崩问题,对请求进行了降级处理。
user降级处理
当order请求user时order产生超时user触发降级处理返回一个空的user对象给order来展示。
 

服务熔断
原理
熔断器,也叫断路器(铅丝原理), 英文单词为:CircuitBreaker。
 Hystrix熔断器模型:
 
 工作原理:阈值默认是最近20次请求内,有50%的请求发生降级处理(超时),触发打开熔断器!,此时进入5秒的休眠期,5秒后进入Half open(半开状态)并且放一定的请求通过,测试请求是否正常,如果请求依然失败,直接进入打开状态(5秒循环),如果请求成功,关闭熔断器。
 熔断器有三个状态:
- open状态说明打开熔断,也就是服务调用方执行本地降级策略,不进行远程调用。
- closed状态说明关闭了熔断,这时候服务调用方直接发起远程调用。
- half-open状态,则是一个中间状态,当熔断器处于这种状态时候,直接发起远程调用。
实践
首先打开Hystrix配置文件 HystrixCommandProperties:
 
- 第一个为熔断器请求量阈值 默认为20
- 第二个为熔断器休眠时间窗 默认5s
- 第三个为熔断器服务错误(降级或者超时) 百分比 默认50%
- ---- 非案例一般默认即可。
修改 controller 代码去自定义规则
@GetMapping("/{id}")
    @HystrixCommand(fallbackMethod = "ww",
            commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value ="10"),
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")
            })
    public Order byId(@PathVariable("id") Long id) {
         Order order = orderService.getById(id);
        User user = restTemplate.getForObject("http://user-service/user/" + order.getUserId(), User.class);
         order.setUser(user);
        return order;
    }

 测试:
 给controller添加一个异常用来测试
@GetMapping("/{id}")
    @HystrixCommand(fallbackMethod = "ww",
            commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value ="10"),
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")
            })
    public Order byId(@PathVariable("id") Long id) {
        if (id == 101){
            throw new RuntimeException("模拟请求异常!!!");
        }
         Order order = orderService.getById(id);
        User user = restTemplate.getForObject("http://user-service/user/" + order.getUserId(), User.class);
         order.setUser(user);
        return order;
    }

 测试 多次访问101触发熔断,之后请求102 测试 ==> 102不能正常访问 ==> 代表已经触发了熔断休眠
 
 10秒后继续测试102 ==> 发现102可以正常访问了 ==> 表示熔断器关闭 恢复正常请求。
 



















