【SpringCloud技术专题】「Hystrix」(1)基本使用以及参数动态配置更新实现

### 概述 > 只介绍同步模式下简单的使用, 有助于快速接入, 会有一些官方文档中没有涉及的细节. ### 默认方式 ```java public class CommandHelloWorld extends HystrixCommand { private final String name; // 构造方法中传入需要用到的数据, run 方法处理逻辑. public CommandHelloWorld(String name) { // 构造 Setter 比较麻烦, 后面会进一步介绍 super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.name = name; } @Override protected String run() { // a real example would do work like a network call here return "Hello " + name + "!"; } // fallback 方法可选, 如果没有, 默认抛异常. @Override protected String getFallback() { return "fallback"; } } ``` ### 构造Setter Hystrix 讲求的大而全, 设计的比较模式, 用起来不是很方便. 下面是一个构造 Setter 的实例: ```java Setter setter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(site)) // groupKey 对command进行分组, 用于 reporting, alerting, dashboards, or team/library ownership, 也是 ThreadPool 的默认 key .andCommandKey(HystrixCommandKey.Factory.asKey(site)) // 可以根据 commandKey 具体的运行时参数 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(site)) // 指定 ThreadPool key, 这样就不会默认使用 GroupKey .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() // 初始化 Command 相关属性 .withMetricsRollingStatisticalWindowInMilliseconds( 100 * 1000) // 设置统计窗口为100秒 .withCircuitBreakerSleepWindowInMilliseconds( 10 * 1000) // 设置熔断以后, 试探间隔为10秒 .withCircuitBreakerRequestVolumeThreshold( 10) // 设置判断熔断请求阈值为10 .withCircuitBreakerErrorThresholdPercentage( 80) // 设置判断熔断失败率为80% .withExecutionTimeoutInMilliseconds(3 * 1000)) // 设置每个请求超时时间为3秒 .andThreadPoolPropertiesDefaults( // 设置和threadPool相关 HystrixThreadPoolProperties.Setter().withCoreSize(20)); // 设置 threadPool 大小为20(最大20个并发) ``` - **另外threadPool还有个设置统计窗口的选项, 因为Hystrix统计维度会从主线程和线程池两个维度来统计.** - 还有好多配置信息可以配置, 这里只提供了几个重要的,其他配置参考:[hystrix-configuration](https://github.com/Netflix/Hystrix/wiki/Configuration) ### Spring cloud 注解方式 需要用到两个注解: **@EnableCircuitBreaker, @HystrixCommand(fallbackMethod = "reliable")** 启动类: ```java package hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.web.client.RestTemplate; @EnableCircuitBreaker @RestController @SpringBootApplication public class ReadingApplication { @Autowired private BookService bookService; @Bean public RestTemplate rest(RestTemplateBuilder builder) { return builder.build(); } @RequestMapping("/to-read") public String toRead() { return bookService.readingList(); } public static void main(String[] args) { SpringApplication.run(ReadingApplication.class, args); } } ``` 服务类: ```java package hello; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.net.URI; @Service public class BookService { private final RestTemplate restTemplate; public BookService(RestTemplate rest) { this.restTemplate = rest; } @HystrixCommand(fallbackMethod = "reliable") public String readingList() { URI uri = URI.create("http://localhost:8090/recommended"); return this.restTemplate.getForObject(uri, String.class); } public String reliable() { return "Cloud Native Java (O'Reilly)"; } } ``` ### 动态更新配置 Hystrix 使用 Archaius 来实现动态配置. 使用 Spring 配置方式: #### 创建配置获取源 > 实现接口 PolledConfigurationSource, 并返回一个 Map. 注意 Key 为 hystrix 配置key, 可以参考:[hystrix-configuration](https://github.com/Netflix/Hystrix/wiki/Configuration) ```java package com.rh.config.hystrix; import com.netflix.config.PollResult; import com.netflix.config.PolledConfigurationSource; import org.springframework.core.env.*; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.*; /** * @Auther: wangyunfei * @Date: 2019/12/10 14:12 * @Description: */ @Component /** * @Author wangyunfei * @Description 定期读取配置文件中的hystrix配置 * @Date 2019/12/10 14:13 * @Param * @return **/ public class DegradeConfigSource implements PolledConfigurationSource { public final String HystrixPrefix = "hystrix"; //spring的Environment private ConfigurableEnvironment environment; private PropertySourcesPropertyResolver resolver; DegradeConfigSource(ConfigurableEnvironment environment) { this.environment = environment; this.resolver = new PropertySourcesPropertyResolver(this.environment.getPropertySources()); this.resolver.setIgnoreUnresolvableNestedPlaceholders(true); } @Override public PollResult poll(boolean initial, Object checkPoint) throws Exception { //Map<String, Object> complete = getHystrixConfig(); Map<String, Object> complete = getProperties(); return PollResult.createFull(complete); } @Resource private ResourceLoader resourceLoader; /** * @Author wangyunfei * @Description 获取配置文件中的指定hystrix前缀信息 * 缺点:1)不支持yml格式 只支持properties * 2)只能读取某一个配置文件中的信息 如果配置是写在不同配置文件则不支持 * @Date 2019/12/20 14:58 * @Param [] * @return java.util.HashMap<java.lang.String,java.lang.String> **/ public HashMap<String,Object> getHystrixConfig() throws Exception { HashMap<String,Object> hystrixMap = new HashMap<String,Object>(); org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:application-mod.properties"); Properties props = new Properties(); props.load(resource.getInputStream()); Enumeration keys = props.propertyNames(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); System.out.println(key + "=" + props.getProperty(key)); if(key.contains(HystrixPrefix)) { hystrixMap.put(key, props.getProperty(key)); } } return hystrixMap; } /** * @Author wangyunfei * @Description 获取所有配置信息 * @Date 2019/12/20 15:06 * @Param [] * @return java.util.Map<java.lang.String,java.lang.Object> **/ public Map<String, Object> getProperties() { Map<String, Object> properties = new LinkedHashMap<>(); //spring env 里面也是多个source组合的 for (Map.Entry<String, PropertySource<? >> entry : getPropertySources().entrySet()) { PropertySource<? > source = entry.getValue(); if (source instanceof EnumerablePropertySource) { EnumerablePropertySource<? > enumerable = (EnumerablePropertySource<? >) source; for (String name : enumerable.getPropertyNames()) { if (!properties.containsKey(name) && name.contains(HystrixPrefix)) { properties.put(name, resolver.getProperty(name)); } } } } return properties; } //PropertySource也可能是组合的,通过递归获取 private Map<String, PropertySource<? >> getPropertySources() { Map<String, PropertySource<? >> map = new LinkedHashMap<String, PropertySource<? >>(); MutablePropertySources sources = null; if (environment != null) { sources = environment.getPropertySources(); } else { sources = new StandardEnvironment().getPropertySources(); } for (PropertySource<? > source : sources) { extract("", map, source); } return map; } private void extract(String root, Map<String, PropertySource<? >> map, PropertySource<? > source) { if (source instanceof CompositePropertySource) { for (PropertySource<? > nest : ((CompositePropertySource) source) .getPropertySources()) { extract(source.getName() + ":", map, nest); } } else { map.put(root + source.getName(), source); } } } ``` #### 配置并初始化自动配置 创建一个 DynamicConfiguration, 并注册一下就可以了. 注意, 我们使用了 FixedDelayPollingScheduler 来定期加载新的配置. 默认60秒加载一次. ```java @Configuration public class DegradeDynamicConfig { @Bean public DynamicConfiguration dynamicConfiguration(@Autowired DegradeConfigSource degradeConfigSource) { AbstractPollingScheduler scheduler = new FixedDelayPollingScheduler(30 * 1000, 60 * 1000, false); DynamicConfiguration configuration = new DynamicConfiguration(degradeConfigSource, scheduler); ConfigurationManager.install(configuration); // must install to enable configuration return configuration; } } ``` 其中从配置文件获取hystrix配置信息等参考如下代码 http://techblog.ppdai.com/2018/05/08/20180508/ ### 参考 https://github.com/Netflix/Hystrix/wiki/How-To-Use https://github.com/Netflix/Hystrix/wiki/Configuration https://github.com/Netflix/Hystrix/issues/1717 https://github.com/Netflix/archaius/wiki/Users-Guide https://spring.io/guides/gs/circuit-breaker/
上一篇:TM模块开发


下一篇:springcloud 2020.0.x版本移除ribbon后如何修改负载均衡策略