先说一下背景,最近做一个Unique ID生成的项目,由于很多其他模块的项目都会依赖Unique ID生成项目,所以在使用的过程中必须要保证生成Unique ID接口的高可用特征,这里准备采用Hystrix来对Unique ID生成接口进行降级处理(降级后采用本地算法对Unique ID进行生成),结果发现当我进行性能测试的时候,部分线程成功生成了;部分线程降级成功;部分线程竟然没有降级。
直接上代码看一下:
@HystrixCommand(fallbackMethod = "fallback", threadPoolKey = "UniqueIdGenerator",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "20")
}
)
public Long nextId() {
log.info("调用远程分布式UD生成器获取uid成功.")
return uidGeneratorClient.generate();
}
private Long fallback() {
log.error("调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级");
return idGeneratorFallback.nextId();
}
这里对于Hystrix命令进行以上设置的原因是,当【请求处理的时间超过1秒钟】或者【请求并行数超过20个线程(20线程是为了测试,实际肯定不止。。)】的时候,对请求进行降级处理。测试的时候我用了50个线程同时请求Unique ID生成接口,代码如下:
class GetUid implements Runnable{
CyclicBarrier cyclicBarrier;
public GetUid(CyclicBarrier b){
this.cyclicBarrier = b;
}
@Override
public void run() {
try {
cyclicBarrier.await();
long uid = uniqueIdGenerator.nextId();
} catch (Exception e){
//e.printStackTrace();
System.out.println("没拿到UID异常");
}
}
}
结果得到以下测试结果:
2020-06-15 11:06:58,983 ERROR [Thread-55] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,983 ERROR [Thread-39] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-43] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-27] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-63] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-40] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-35] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-59] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-28] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:06:58,984 ERROR [Thread-53] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
没拿到UID异常
2020-06-15 11:06:59,003 INFO [hystrix-UniqueIdGenerator-18] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,003 INFO [hystrix-UniqueIdGenerator-16] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-1] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-4] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-20] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-2] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-8] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-13] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,003 INFO [hystrix-UniqueIdGenerator-11] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,003 INFO [hystrix-UniqueIdGenerator-5] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-17] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-14] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-19] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-7] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-6] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-3] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-12] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-15] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-9] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:06:59,004 INFO [hystrix-UniqueIdGenerator-10] - 调用远程分布式UD生成器获取uid成功.
我们可以看到其中有20个请求成功进入到了UniqueID生成逻辑中,有10个请求成功降级进入到降级方法中,剩下的20个请求没有被降级而是异常报错了,我们来打印一下报错信息:
com.netflix.hystrix.exception.HystrixRuntimeException: nextId fallback execution rejected.
at com.netflix.hystrix.AbstractCommand.handleFallbackRejectionByEmittingError(AbstractCommand.java:1043)
at com.netflix.hystrix.AbstractCommand.getFallbackOrThrowException(AbstractCommand.java:875)
at com.netflix.hystrix.AbstractCommand.handleThreadPoolRejectionViaFallback(AbstractCommand.java:993)
at com.netflix.hystrix.AbstractCommand.access$400(AbstractCommand.java:60)
at com.netflix.hystrix.AbstractCommand$12.call(AbstractCommand.java:608)
at com.netflix.hystrix.AbstractCommand$12.call(AbstractCommand.java:601)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$2.onError(AbstractCommand.java:1194)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:54)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.subscribe(Observable.java:10307)
at rx.Observable.subscribe(Observable.java:10274)
at rx.internal.operators.BlockingOperatorToFuture.toFuture(BlockingOperatorToFuture.java:51)
at rx.observables.BlockingObservable.toFuture(BlockingObservable.java:412)
at com.netflix.hystrix.HystrixCommand.queue(HystrixCommand.java:378)
at com.netflix.hystrix.HystrixCommand.execute(HystrixCommand.java:344)
at com.netflix.hystrix.contrib.javanica.command.CommandExecutor.execute(CommandExecutor.java:52)
at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.methodsAnnotatedWithHystrixCommand(HystrixCommandAspect.java:103)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.xxx.unique.id.impl.UniqueIdGeneratorImpl$$EnhancerBySpringCGLIB$$388653b4.nextId()
at com.xxx.UidGeneratorTest$GetUid.run(UidGeneratorTest.java:72)
at java.lang.Thread.run(Thread.java:748)
我们可以看到“nextId Fallback execution rejected”的HystrixRuntimeException异常,众所周知,Hystrix对于普通RuntimeException异常报错请求都会进行降级处理,但是对于HystrixRuntimeException不会降级,这个异常是Hystrix自己抛出来的,用来提示请求方请求失败。
作为面向百度开发的我,就开始在百度疯狂的搜索起来,这个异常是什么原因导致的,为什么只有10个线程成功降级了,其他线程错被拒绝了呢?结果最终锁定在了Hystrix的一个默认配置值:
这个"default_fallbackIsolationSemaphoreMaxConcurrentRequests = 10" 的配置就是最终导致只有10个线程发生降级的"元凶"。我们先来看一下GITHUB上面对这个属性的描述:这个属性主要是来控制Hystrix降级并行的线程数,如果需要降级的线程并行数超过这个属性所设置的值,则会直接报上面HystrixRuntimeException的异常。一开始通过这个配置的名字来看,以为只是针对于Semaphore模式下的Hystrix的降级处理,结果在Thread模式下也会生效。。
知道了这个参数之后,我就开始进行尝试,将这个配置的值设置大一些,看看是否可用:
@HystrixCommand(fallbackMethod = "fallback", threadPoolKey = "UniqueIdGenerator",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "1000"),
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "20")
}
)
public Long nextId() {
log.info("调用远程分布式UD生成器获取uid成功.");
return uidGeneratorClient.generate();
}
private Long fallback() {
log.error("调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级");
return idGeneratorFallback.nextId();
}
改完之后,再来运行一下测试用例,得到了以下结果:
2020-06-15 11:25:59,745 ERROR [Thread-33] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-19] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-49] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-24] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-32] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-31] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-48] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-47] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-50] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-29] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-65] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-59] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-38] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-42] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-25] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-62] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-52] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-22] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-43] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-21] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-39] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,746 ERROR [Thread-61] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-36] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-56] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-53] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-44] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-40] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,745 ERROR [Thread-17] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,751 ERROR [Thread-20] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,751 ERROR [Thread-18] - 调用远程分布式ID生成器获取uid失败, 将使用JVM级ID生成器进行降级
2020-06-15 11:25:59,766 INFO [hystrix-UniqueIdGenerator-2] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,767 INFO [hystrix-UniqueIdGenerator-8] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,767 INFO [hystrix-UniqueIdGenerator-20] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,767 INFO [hystrix-UniqueIdGenerator-16] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,767 INFO [hystrix-UniqueIdGenerator-11] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,767 INFO [hystrix-UniqueIdGenerator-13] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,768 INFO [hystrix-UniqueIdGenerator-7] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,768 INFO [hystrix-UniqueIdGenerator-9] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,766 INFO [hystrix-UniqueIdGenerator-15] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,768 INFO [hystrix-UniqueIdGenerator-18] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,768 INFO [hystrix-UniqueIdGenerator-6] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,769 INFO [hystrix-UniqueIdGenerator-1] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,768 INFO [hystrix-UniqueIdGenerator-12] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,769 INFO [hystrix-UniqueIdGenerator-3] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,769 INFO [hystrix-UniqueIdGenerator-10] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,770 INFO [hystrix-UniqueIdGenerator-17] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,770 INFO [hystrix-UniqueIdGenerator-19] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,770 INFO [hystrix-UniqueIdGenerator-4] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,770 INFO [hystrix-UniqueIdGenerator-14] - 调用远程分布式UD生成器获取uid成功.
2020-06-15 11:25:59,770 INFO [hystrix-UniqueIdGenerator-5] - 调用远程分布式UD生成器获取uid成功.
我们可以看到现在有20个线程成功的通过方法生成了UniqueID,而另外30个线程则都进行了降级的处理逻辑中,总体上达到了想要的目的,当然这个参数具体配置多少还是要根据项目的实际情况。
总结:
个人理解Hystrix设置这个属性并且给与默认值主要还是为了对高并发下请求数量的控制,毕竟如果走了降级处理逻辑也还是会增加服务器的请求线程数,对服务器可能造成压力。
Hystrix fallback方法调用源码:
-> com.netflix.hystrix.getFallbackOrThrowException()