[SpringCloud] 入门-第四篇: 熔断机制 hystrix

1. hystrix

hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制, 所以开发团队取了这个名字. (⊙o⊙)…

为什么要用断路器? 其实这个跟保险丝一样, 是为了避免分布式系统中的”雪崩效应”,或者我觉得也是多米诺骨牌效应.

比如A调用服务B,服务B调用服务C…如果C因为某些原因挂掉了,或者网络原因掉不到C,这时候B这个方法也挂掉了,A这边也跟着挂….这就是雪崩效应,其实跟spring管理实例一样,当实例很多,又互相依赖的时候, 通常我们需要靠框架来帮我们进行管理.

当某个服务挂掉之后,通过hystrix,我们可以跟定义变量的默认值一样,给方法定义一个Fallback方法来执行
这里写图片描述

2. 启动服务

  1. 启动eurekaServer
  2. 启动eureka-client-01, 端口号是8803

这样我们就有了服务提供者和注册中心

3. ribbon方式使用熔断器

service-ribbon的端口号是8805

使用熔断器很简单,首先在 service-ribbon中 引入依赖


<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-hystrixartifactId>
dependency>

在启动app上打上注解,开启hystrix

package com.zgd.springcloud.eurekaClient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;


/**
 * eureka client 客户端
 */
@EnableDiscoveryClient
@EnableEurekaClient
//开启hystrix熔断
@EnableHystrix
@SpringBootApplication
public class RibbonApp {

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

    /**
     * 使用restTemplate消费方法
     * @return
     *  @LoadBalanced 负载均衡
     */
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

在回到我们的service层, 首先定义一个error方法,在熔断的时候执行这个方法

 /**
     * 熔断执行的方法
     * @param name
     * @return
     */
    public String hiError(String name){
        return name + ",client1出现问题,进行了熔断";
    }

然后我们在service方法上打上注解@HystrixCommand指定fallback方法

/**
     * 通过restTemplate采用rest直接访问eureka-client-01的路由调用方法
     * @param name
     * @return
     */
    @HystrixCommand(fallbackMethod = "hiError")//指定熔断时调用的方法
   public String hiService(String name) {
        //eureka-client-01工程的applicationName
        String client01Name = "client01";
        //eureka-client-01工程的ProducerController的hello方法的路由
        String prefix = "hello";

        //访问eureka-client-01的路由,远程调用eureka-client-01的方法
        return restTemplate.getForObject("http://" + client01Name + "/" + prefix + "?name=" + name, String.class);
    }

然后我们启动service-ribbon, 访问 http://localhost:8085/getHi?name=zgd
这个时候是没问题的
[SpringCloud] 入门-第四篇: 熔断机制 hystrix_第1张图片

然后我们关闭eureka-client-01 ,按理来说会报错,但是访问连接显示
[SpringCloud] 入门-第四篇: 熔断机制 hystrix_第2张图片

可见我们的fallback正常调用了

4. feign方式使用熔断器

feign同样的相对来说简单一些

首先配置文件增加配置feign.hystrix.enable = true开启熔断器

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: service-feign
server:
  port: 8086

feign:
  hystrix:
    enabled: true

我们之前是只写了一个接口,并没有写具体实现类, 然后我们实现一下FeignService接口,写一个业务的具体实现类,

package com.zgd.springcloud.eurekaClient.imp;

import com.zgd.springcloud.eurekaClient.interf.FeignService;
import org.springframework.stereotype.Component;

@Component
public class FeignServiceImpl implements FeignService {

    @Override
    public String hiService(String name) {
        return name+",这里被熔断了";
    }
}

其中覆写的方法就是我们的fallback方法

然后在接口上标明我们的实现类是fallback类

package com.zgd.springcloud.eurekaClient.interf;

import com.zgd.springcloud.eurekaClient.imp.FeignServiceImpl;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @FeignClient value是指定的服务提供者在eurekaServer中的服务名,eureka-client-01的applicationName就是client01
 */
@FeignClient(value = "client01",fallback = FeignServiceImpl.class)
@Component
public interface FeignService{

    /**
     * 这里还是访问的eureka-client-01的
     *      com.zgd.springcloud.eurekaClient.controller.ProducerController
     *      的hello方法的路由
     * @param name
     * @return
     */
    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    String hiService(@RequestParam(value = "name") String name);
}

这样就ok了, 不启动client ,直接启动service-feign, 访问http://localhost:8086/getHi?name=zgd

这里写图片描述

熔断成功.

区别

所以ribbon和feign之间,主要的一个区别就是ribbon是在方法级上定义熔断后调用的fallback,feign是在类级上,直接创建一个实现类,去调用实现类的方法


隔离机制THREAD和SEMAPHORE

@HystrixCommand有两种隔离机制,THREAD(默认,推荐)和SEMAPHORE

THREAD

顾名思义,thread隔离机制,跟线程池相关,Hystrix的线程池配置如下:

线程数默认值10,适用于大部分情况(有时可以设置得更小),如果需要设置得更大,那有个基本得公式可以follow:
requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room
每秒最大支撑的请求数 (99%平均响应时间 + 缓存值)
比如:每秒能处理1000个请求,99%的请求响应时间是60ms,那么公式是:
1000 (0.060+0.012)

基本得原则时保持线程池尽可能小,他主要是为了释放压力,防止资源被阻塞。
当一切都是正常的时候,线程池一般仅会有1到2个线程激活来提供服务

hystrix.threadpool.default.coreSize 并发执行的最大线程数,默认10
hystrix.threadpool.default.maxQueueSize BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。该设置只会在初始化时有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默认-1。
hystrix.threadpool.default.queueSizeRejectionThreshold 即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。因为maxQueueSize不能被动态修改,这个参数将允许我们动态设置该值。if maxQueueSize == -1,该字段将不起作用
hystrix.threadpool.default.keepAliveTimeMinutes 如果corePoolSize和maxPoolSize设成一样(默认实现)该设置无效。如果通过plugin(https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定义实现,该设置才有用,默认1.
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds 线程池统计指标的时间,默认10000
hystrix.threadpool.default.metrics.rollingStats.numBuckets 将rolling window划分为n个buckets,默认10

//name一般都是上面一串中default后面的
@HystrixCommand(fallbackMethod = "hiError"
           ,threadPoolProperties = {
           @HystrixProperty(name = "coreSize",value = "100")
       }
    )

如果在高并发情况下,大量请求超过了线程池处理能力, 将会熔断, 采用jmeter发出1000条并发请求,结果如下:
[SpringCloud] 入门-第四篇: 熔断机制 hystrix_第3张图片

SEMAPHORE

Execution相关的属性的配置:
hystrix.command.default.execution.isolation.strategy 隔离策略,默认是Thread, 可选Thread|Semaphore
thread 通过线程数量来限制并发请求数,可以提供额外的保护,但有一定的延迟。一般用于网络调用
semaphore 通过semaphore count来限制并发请求数,适用于无网络的高并发请求
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令执行超时时间,默认1000ms
hystrix.command.default.execution.timeout.enabled 执行是否启用超时,默认启用true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout 发生超时是是否中断,默认true
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。

如果达到最大并发请求数,请求会被拒绝。理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。
semaphore应该占整个容器(tomcat)的线程池的一小部分。

  @HystrixCommand(fallbackMethod = "hiError"
            , commandProperties = {
            //隔离策略,默认推荐是THREAD,还有就是SEMAPHORE(信号)
            @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
            //熔断的最大并发请求,只有在隔离策略是SEMAPHORE时才生效
            @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "1000")
            }
    )

github代码

https://github.com/zzzgd/SpringCloudDemo/tree/master/spring-cloud-part2

你可能感兴趣的:(spring,cloud)