I am doing a lot of tests using Resilience4j and monitoring the behaviour of threads.
I am using spring-boot 2.2.5, resilience4j 1.4.0. Gatling (load tool), visualvm (Analyze thread)
I realized that:
When I use bulkhead type = SEMAPHORE.
I realized that the async execution will use default ExecutorService, in this case, ForkJoinPool and this configuration will it works:
maxConcurrentCalls, maxWaitDuration
If you are using bulkhead type = THREADPOOL these above configuration will be ignored.
When I am using bulkhed THREADPOOL these configuration will works:
maxThreadPoolSize, coreThreadPoolSize, queueCapacity, keepAliveDuration
These configuration will be ignored:
maxConcurrentCalls, maxWaitDuration
My code:
@CircuitBreaker(name = MyServiceCommand.BEAN_NAME, fallbackMethod = "fallback")
@TimeLimiter(name = MyServiceCommand.BEAN_NAME)
@Bulkhead(name = MyServiceCommand.BEAN_NAME, type = Bulkhead.Type.THREADPOOL)
@Component
@AllArgsConstructor
@Slf4j
public class MyServiceCommand extends ResilienceAbstractCommand {
protected static final String BEAN_NAME = "MyService";
private SlowService slowService;
public CompletableFuture<String> perform() {
return CompletableFuture.completedFuture(slowService.get());
}
private CompletableFuture<String> fallback() {
return CompletableFuture.completedFuture("fallback");
}
}
Configuration:
resilience4j.circuitbreaker:
configs:
default:
failureRateThreshold: 40
waitDurationInOpenState: 20s
minimumNumberOfCalls: 5
slowCallRateThreshold: 30
slowCallDurationThreshold: 300s
instances:
MyService:
baseConfig: default
ignoreExceptions:
- com.hotmart.display.commons.exception.BusinessException
resilience4j.bulkhead:
configs:
default:
maxConcurrentCalls: 800
maxWaitDuration: 15000
instances:
MyService:
baseConfig: default
resilience4j.thread-pool-bulkhead:
configs:
default:
maxThreadPoolSize: 10
queueCapacity: 500
instances:
MyService:
baseConfig: default
resilience4j.timelimiter:
configs:
default:
timeoutDuration: 300s
cancelRunningFuture: true
instances:
MyService:
baseConfig: default
SlowService make a slow network call to another application
I have configured these values only in order to test, in production the values will be different.
The questions are:
1 - My application is I/O Bound, I wish when there are many requests (40/s) the thread-pool created by resilience4j achieved maxThreadPoolSize number, but were created only 3 thread (the amount of cpu core -1) I saw that create only:
bulkhead-MyService-1, bulkhead-MyService-2, bulkhead-MyService-3
What is the problem?
2 - I did this configuration:
maxThreadPoolSize: 1
coreThreadPoolSize: 1
queueCapacity: 2
keepAliveDuration: 3
I configured slowService to take 10s, so slow.
I did 3 request at time, the system behaved like this:
- Process only 1 at time, it is ok, because the core and maxThread are 1.
- The request was queried, it is ok, because the queueCapacity is 2.
But, I hope that request waiting in the queue only wait for 3ms, because is the configured time.
How can I limit the max time that the thread will wait in the queue?
[Edit][update] I did more tests and realized that:
While a thread is busy, another requests that arrive will start a new task until reach coreThread, if all coreThread are busy.
If the pool reach the coreThread, each new request will be put on queue, after queue is full, new requests will create new threads until rearch maxPoolThread.
About keepAliveDuration, threads will terminating after idle for amount of time configured. The coreThreadSize will be idle, only excess thread will be terminated.
All my doubts were answered by my tests.
For now I would like just to know about the difference behaviour between THREADPOOL and SEMAPHORE?