Hystrix使用
使用Hystrix實現熔斷
要實現熔斷,首先需要在請求調用方pom文件中加入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在啓動類上加入@EnableCircuitBreaker註解,並在調用到另一個微服務的方法上加入一些配置
@GetMapping("/checkHystrix/{userId}")
@HystrixCommand(threadPoolKey = "checkHystrix1",
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "1"),
@HystrixProperty(name = "maxQueueSize",value = "20")
},
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000")
})
public Integer checkHystrix(@PathVariable Long userId) {
String url = "http://zhao-service-resume/resume/openstate/"+userId;
Integer forObject =
restTemplate.getForObject(url, Integer.class);
return forObject;
}
將調用到的服務中加入線程休眠十秒。訪問上述服務,在界面上即可發現Hystrix的超時錯誤
服務降級
在配置中再增加一個 fallbackMethod = "customeFallback",
配置降級兜底方法的具體形式是
public Integer customeFallback(Long userId){
return -1;
}
當某個服務熔斷之後,服務器將不再被調⽤,此刻客戶端可以⾃⼰準備⼀個本地的fallback回調,返回⼀個缺省值,這樣做,雖然服務⽔平下降,但好⽍可⽤,⽐直接掛掉要強。但是在配置服務降級策略時,降級(兜底)⽅法必須和被降級⽅法相同的⽅法簽名(相同參數列表、相同返回值)
如果參數不同會出現com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: fallback method wasn't found: customeFallback([class java.lang.Long])
如果返回值不同會出現。且包裝類和基本類型不能共用
Hint: Fallback method 'public int com.zp.controller.AutoDeliverController.customeFallback(java.lang.Long)' must return: class java.lang.Integer or its subclass
倉壁模式(線程池隔離模式)
如果不進⾏任何線程池設置,所有熔斷⽅法使⽤⼀個Hystrix線程池(默認爲10個線程),那麼這樣的話會導致問題,這個問題並不是扇出鏈路微服務不可⽤導致的,⽽是我們的線程機制導致的,如果⽅法A的請求把10個線程都⽤了,⽅法2請求處理的時候壓根都、沒法去訪問B,因爲沒有線程可⽤,並不是B服務不可⽤。因此在配置Hystrix線程時,多個方法應該寫多個線程池。這樣能夠讓線程之間互不影響
@GetMapping("/checkHystrix/{userId}")
@HystrixCommand(threadPoolKey = "checkHystrix1",
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "1"),
@HystrixProperty(name = "maxQueueSize",value = "20")
},
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000")
})
public Integer checkHystrix(@PathVariable Long userId) {
String url = "http://zhao-service-resume/resume/openstate/"+userId;
Integer forObject =
restTemplate.getForObject(url, Integer.class);
return forObject;
}
@GetMapping("/checkHystrixFallback/{userId}")
@HystrixCommand(threadPoolKey = "checkHystrix2",
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "2"),
@HystrixProperty(name = "maxQueueSize",value = "20")
},
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000")
},
fallbackMethod = "customeFallback"
)
通過postman發送批量請求,並通過jstack命令可以看到兩個方法的線程池進行了隔離
其他屬性的含義
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "8000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "2"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value="50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value="3000")
}
metrics.rollingStats.timeInMilliseconds該屬性是下面的熔斷的統計時間窗口定義
circuitBreaker.requestVolumeThreshold該屬性統計時間窗⼝內的失敗的次數,達到此次數之後開啓熔斷操作
circuitBreaker.errorThresholdPercentage 窗口內失敗的次數的百分比,達到這個百分比之後開啓熔斷操作
circuitBreaker.sleepWindowInMilliseconds 熔斷多久間隔多久以後開始嘗試是否恢復
源碼簡要分析
首先我們根據註解類@EnableCircuitBreaker可以找到SpringFactoryImportSelector類,該類通過泛型在spring.factories文件中找到註解了該泛型的配置類
@Override
public String[] selectImports(AnnotationMetadata metadata) {
if (!isEnabled()) {
return new String[0];
}
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
+ metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
// Find all possible auto configuration classes, filtering duplicates
List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
if (factories.isEmpty() && !hasDefaultFactory()) {
throw new IllegalStateException("Annotation @" + getSimpleName()
+ " found, but there are no implementations. Did you forget to include a starter?");
}
if (factories.size() > 1) {
// there should only ever be one DiscoveryClient, but there might be more than
// one factory
log.warn("More than one implementation " + "of @" + getSimpleName()
+ " (now relying on @Conditionals to pick one): " + factories);
}
return factories.toArray(new String[factories.size()]);
}
隨後在spring.factories中找到了加載的配置類
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
配置類中配置了一個切面HystrixCommandAspect,Hystrix實現的主要功能都在這個切面中進行了執行
public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethodFromTarget(joinPoint);
Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " +
"annotations at the same time");
}
MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
Object result;
try {
if (!metaHolder.isObservable()) {
result = CommandExecutor.execute(invokable, executionType, metaHolder);
} else {
result = executeObservable(invokable, executionType, metaHolder);
}
} catch (HystrixBadRequestException e) {
throw e.getCause();
} catch (HystrixRuntimeException e) {
throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
}
return result;
}
使用同步調用會走executeObservable最終進入HystrixCommand進入最終的處理邏輯
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
進入執行隊列並異步獲取
最後執行 final Future
歡迎搜索關注本人與朋友共同開發的微信面經小程序【大廠面試助手】和公衆號【微瞰技術】,以及總結的分類面試題https://github.com/zhendiao/JavaInterview