BUG background
JDK: 11.0.4 Spring Cloud Finchley.SR3
Related configuration:
#Enable hystrix feign.hystrix.enabled=true #Close the circuit breaker hystrix.command.default.circuitBreaker.enabled=false #Disable hystrix remote call timeout hystrix.command.default.execution.timeout.enabled=false hystrix.threadpool.default.coreSize=50
Hystrix isolation strategy: thread isolation
When Feign is called, an error will be reported:
Caused by: java.util.concurrent.ExecutionException: Observable onError at rx.internal.operators.BlockingOperatorToFuture$2.getValue(BlockingOperatorToFuture.java:118) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.BlockingOperatorToFuture$2.get(BlockingOperatorToFuture.java:102) ~[rxjava-1.3.8.jar!/:1.3.8] at com.netflix.hystrix.HystrixCommand$4.get(HystrixCommand.java:423) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.HystrixCommand.execute(HystrixCommand.java:344) ~[hystrix-core-1.5.18.jar!/:1.5.18] ... 30 more Caused by: java.lang.IllegalArgumentException at java.util.concurrent.ThreadPoolExecutor.setCorePoolSize(ThreadPoolExecutor.java:1535) ~[?:?] at com.netflix.hystrix.HystrixThreadPool$HystrixThreadPoolDefault.touchConfig(HystrixThreadPool.java:230) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.HystrixThreadPool$HystrixThreadPoolDefault.getScheduler(HystrixThreadPool.java:205) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.AbstractCommand.executeCommandWithSpecifiedIsolation(AbstractCommand.java:710) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.AbstractCommand.executeCommandAndObserve(AbstractCommand.java:638) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.AbstractCommand.applyHystrixSemantics(AbstractCommand.java:546) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.AbstractCommand.access$200(AbstractCommand.java:60) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.AbstractCommand$4.call(AbstractCommand.java:419) ~[hystrix-core-1.5.18.jar!/:1.5.18] at com.netflix.hystrix.AbstractCommand$4.call(AbstractCommand.java:413) ~[hystrix-core-1.5.18.jar!/:1.5.18] at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar!/:1.3.8] at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) ~[rxjava-1.3.8.jar!/:1.3.8]
Restart sometimes repeats, sometimes does not. It is estimated that it is related to class loading and initialization order.
Problem location
View the source code of the problem, initialize the Hytrix thread pool for each microservice call, and dynamically read the configuration:
private void touchConfig() { final int dynamicCoreSize = properties.coreSize().get(); final int configuredMaximumSize = properties.maximumSize().get(); int dynamicMaximumSize = properties.actualMaximumSize(); final boolean allowSizesToDiverge = properties.getAllowMaximumSizeToDivergeFromCoreSize().get(); boolean maxTooLow = false; if (allowSizesToDiverge && configuredMaximumSize < dynamicCoreSize) { //if user sets maximum < core (or defaults get us there), we need to maintain invariant of core <= maximum dynamicMaximumSize = dynamicCoreSize; maxTooLow = true; } // In JDK 6, setCorePoolSize and setMaximumPoolSize will execute a lock operation. Avoid them if the pool size is not changed. if (threadPool.getCorePoolSize() != dynamicCoreSize || (allowSizesToDiverge && threadPool.getMaximumPoolSize() != dynamicMaximumSize)) { if (maxTooLow) { logger.error("Hystrix ThreadPool configuration for : " + metrics.getThreadPoolKey().name() + " is trying to set coreSize = " + dynamicCoreSize + " and maximumSize = " + configuredMaximumSize + ". Maximum size will be set to " + dynamicMaximumSize + ", the coreSize value, since it must be equal to or greater than the coreSize value"); } threadPool.setCorePoolSize(dynamicCoreSize); threadPool.setMaximumPoolSize(dynamicMaximumSize); } threadPool.setKeepAliveTime(properties.keepAliveTimeMinutes().get(), TimeUnit.MINUTES); }
The error is reported in threadpool.setcorepoolsize (dynamiccolesize);, see why:
public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException(); //Ignore other codes }
It is found that there is also a judgment of MaximumPoolSize < CorePoolSize. Therefore, you need to set MaximumPoolSize first, and then CorePoolSize to avoid this problem
Related ISSUES and Solutions
After looking at the community, there are already issues: https://github.com/Netflix/Hystrix/issues/1874 There is also the corresponding PULL REQUEST: https://github.com/Netflix/Hystrix/pull/1877
But at present, it has not been merged, so we have to change it by ourselves. In the project, we add a replacement class with the same name and the same path, and modify it:
//For jdk9 and above, setting coreSize will verify that coreSize must be smaller than maxSize, so change max before setting core threadPool.setMaximumPoolSize(dynamicMaximumSize); threadPool.setCorePoolSize(dynamicCoreSize);