SpringCloud详解 第三章 服务容错保护 Hystrix(一)

本章节初步了解Hystrix以及使用@HystrixCommand实现服务容错保护 

一、使用场景

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

  “断路器” 本身是一种开关设置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选相应(fallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

  在大中型分布式系统中,通常系统很多依赖(HTTP,hession,Netty,Dubbo等),如下图:

   在高并发访问下,这些依赖的稳定性与否对系统的影响非常大,但是依赖有很多不可控问题:如网络连接缓慢,资源繁忙,暂时不可用,服务脱机等.

   如下图:QPS为50的依赖 I 出现不可用,但是其他依赖仍然可用.

  当依赖I 阻塞时,大多数服务器的线程池就出现阻塞(BLOCK),影响整个线上服务的稳定性.如下图:

  在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败。高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险。

  解决问题方案:对依赖做隔离,Hystrix就是处理依赖隔离的框架,同时也是可以帮我们做依赖服务的治理和监控.

二、服务熔断:

熔断机制是应对雪崩效应的一种微服务链路保护机制。

  当扇出链路(即上面的图二)的某个微服务(I)不可用或者响应时间太长,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息。当检测到该节点微服务调用响应正常回复后恢复调用链路。在springCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务服务间调用的状况,当失败的调用到达一定的阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是 @HystrixCommand

  Hystrix中的三种降级方案

  1. 熔断触发降级
  2. 请求超时触发降级
  3. 资源隔离触发降级

  基于springcloud 学习中 Ribbon的demo进行改造 :https://www.cnblogs.com/wuzhenzhao/category/1529973.html

1、熔断触发降级:

(1).pom文件修改 添加以下依赖

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

(2).application.yml 不需要修改

(3). 修改Controller 接口 ,添加注解 @HystrixCommand

@RestController
public class RibbonController {

    //  private static final String REST_URL_PREFIX="http://localhost:8001"; 单机版
    //集群的时候  需要配置该服务在eureka里注册的名字
    private static final String REST_URL_PREFIX="http://cloud-provider";

    @Autowired
    private RestTemplate restTemplate;
  //同步
  @HystrixCommand(commandProperties = {
    //HystrixCommandProperties 类中包含配置信息所有
    //开启熔断
    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
    //最小请求数
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
    //熔断5秒
    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
    //10秒内 最少请求 5次。若百分比超过 50 则触发熔断
    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
  }, fallbackMethod = "processHystrix_Get")//熔断机制
    @RequestMapping(value ="/hello")
    public String get(Long id) {
        Map map =new HashMap<>();
        map.put("id",id);
        return restTemplate.getForObject(REST_URL_PREFIX+"/hello?id={id}", String.class,map);
    }

    //消费端可以调用服务发现
    @RequestMapping(value ="/discovery")
    public Object discovery() {

        return restTemplate.getForObject(REST_URL_PREFIX+"/discovery", Object.class);
    }

    public String processHystrix_Get(Long id) {
        return "hello Hystrix";
    }
}

修改原来的cloud-provider服务:

@GetMapping("/hello")
public String helloEureka(String id){
  if (StringUtils.isEmpty(id)) {
    throw new RuntimeException();
  }
  return  "Hello Eureka Provider";
}

(4).修改主启动类

@EnableDiscoveryClient
@SpringBootApplication
//自定义负载均衡算法 自定义配置类不能跟主启动类一个包或在子包下面
//name: 表示对哪个服务采用自定义算法
//configuration:负载算法类
@RibbonClient(name="cloud-provider")
@EnableCircuitBreaker // 对Hystrix熔断机制的支持
public class RibbonApp {
    private final static Logger log = LoggerFactory.getLogger(RibbonApp.class);

    public static void main(String[] args) {
        SpringApplication.run(RibbonApp.class,args);
        log.info("服务启动成功");

    }
}

2、请求超时触发熔断:

(1).在客户端新增一个接口:

@HystrixCommand(fallbackMethod = "timeoutFallback", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
    })
    @GetMapping("/hystrix/timeout")
    public String queryTimeout() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/timeout", String.class);
    }

    public String timeoutFallback() {
        return "timeOut Hystrix";
}

(2).在服务提供方新增测试接口:

@RequestMapping(value = "/timeout", method = RequestMethod.GET)
    public String timeout() throws InterruptedException {
        Thread.sleep(4000);
        return "Hello Eureka Provider";
    }

启动测试即可

3、资源隔离熔断:资源隔离熔断可以分为 信号量隔离和线程池隔离。

/**
* 信号量隔离实现
* 不会使用Hystrix管理的线程池处理请求。使用容器(Tomcat)的线程处理请求逻辑。
* 不涉及线程切换,资源调度,上下文的转换等,相对效率高。
* 信号量隔离也会启动熔断机制。如果请求并发数超标,则触发熔断,返回fallback数据。
* commandProperties - 命令配置,HystrixPropertiesManager中的常量或字符串来配置。
* execution.isolation.strategy - 隔离的种类,可选值只有THREAD(线程池隔离)和SEMAPHORE(信号量隔离)。默认是THREAD线程池隔离。
* 设置信号量隔离后,线程池相关配置失效。
* execution.isolation.semaphore.maxConcurrentRequests - 信号量最大并发数。默认值是10。常见配置500~1000。
* 如果并发请求超过配置,其他请求进入fallback逻辑。
*   
*/
@HystrixCommand(fallbackMethod = "semaphoreFallback",
            commandProperties = {
                    // 信号量隔离
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
                    // 信号量最大并发数
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "5"),               @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_TIMEOUT_ENABLED, value = "true"),
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "3000")
            }
)
@GetMapping("/hystrix/semaphore")
public String semaphore() {
        Map map = new HashMap<>();
        map.put("id", "1");
        return restTemplate.getForObject(REST_URL_PREFIX + "/hello?id={id}", String.class, map);
}

@HystrixCommand(
//            groupKey = "order-service", commandKey = "queryOrder", threadPoolKey = "order-service",
  threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "30"),//线程池大小
                    @HystrixProperty(name = "maxQueueSize", value = "100"),//最大队列长度
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//线程存活时间
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求
  },
  commandProperties = {
                    // 隔离
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD"),
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_TIMEOUT_ENABLED, value = "true"),
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT, value = "3000"),
  },
  fallbackMethod = "semaphoreFallback")
@GetMapping("/hystrix/thread")
public String thread() {
        Map map = new HashMap<>();
        map.put("id", "1");
        return restTemplate.getForObject(REST_URL_PREFIX + "/hello?id={id}", String.class, map);
}

public String semaphoreFallback() {
        System.out.println("semaphore Hystrix");
        return "semaphore Hystrix";
}

public String threadFallback() {
        return "thread Hystrix";
}

基于资源隔离我们可能需要借助 dashboard 来看效果,首先要开启 management.endpoints.web.exposure.include=refresh,hystrix.stream,然后通过Jmeter进行压测,就可以看到效果

 

4、基于Feign 客户端的熔断实现:

@FeignClient(value = "cloud-provider", fallbackFactory = HystrixClientService.class)
public interface ClientService {
    //如果feign代理的是get请求,必须用@RequestMapping 不能用@GetMapping
    // 每个参数必须带上@RequestParam,否则会报post not support!
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String hello(@RequestParam("id") String id) throws InterruptedException;
}

@Component
public class HystrixClientService implements FallbackFactory<ClientService> {
    @Override
    public ClientService create(Throwable throwable) {
        return new ClientService() {
            @Override
            public String hello(String id) {
                System.out.println("feign 服务降级");
                return "feign 服务降级";
            }
        };
    }
}

然后在引用该Feign服务的项目中开启feign支持@EnableFeignClients(basePackages = "com.wuzz.demo")、@EnableCircuitBreaker // 对Hystrix熔断机制的支持。这样即可

5、基于Feign 客户端的资源隔离熔断实现:引入相关依赖,即Feign Service  的依赖,然后

@Autowired
private ClientService clientService;

@GetMapping("/hystrix/feign")
public String feign() throws InterruptedException {
    return clientService.hello("11115");
}

接着开启 Feign 熔断、及Feign Service 的方法级别的熔断策略

feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:  #全局配置, feignclient#method(param)
      execution:
        timeout:
          enable: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000
#    ClientService#hello(String):
#      execution:
#        isolation:
#          strategy: SEMAPHORE
#          semaphore:
#            maxConcurrentRequests: 10
    ClientService#hello(String):
      execution:
        isolation:
          strategy: THREAD
  threadpool:
    cloud-provider:
      coreSize: 2
      maxQueueSize: 1000
      queueSizeRejectionThreshold:  800

测试结果如下:

 

posted @ 2020-10-02 17:44  跃小云  阅读(288)  评论(0编辑  收藏  举报