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中的三种降级方案
- 熔断触发降级
- 请求超时触发降级
- 资源隔离触发降级
基于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
测试结果如下: