spring-cloud / spring-cloud-circuitbreaker

Spring Cloud Circuit Breaker API and Implementations
Apache License 2.0
328 stars 109 forks source link

Enhancement Request: Customizable groupExecutorService in Resilience4J CircuitBreakerFactory #180

Closed imyzt closed 6 months ago

imyzt commented 7 months ago

Is your feature request related to a problem? Please describe.

When incorporating openfeign and circuitbreaker in a spring-boot project, our code submits tasks to the executorService for execution. The executorService is created using Executors.newCachedThreadPool(). We intend to customize this thread pool to propagate ThreadLocal information from the main thread to the child threads.

While the Resilience4JCircuitBreakerFactory#configureExecutorService method allows reconfiguration of the thread pool, the groupExecutorService is hard-coded in the code and lacks extensibility, making it challenging to fully implement the desired functionality.

Describe the solution you'd like

We propose making the groupExecutorService customizable or providing a mechanism for extension so that developers can seamlessly implement customizations to pass ThreadLocal information to the child threads.

Additional Information:

Code Reference: Resilience4JCircuitBreakerFactory#configureExecutorService Current Limitation: Inability to customize groupExecutorService for complete functionality implementation. Expected Outcome: We request enhancements to the Resilience4J CircuitBreakerFactory to allow developers to easily customize the groupExecutorService or provide an extension mechanism to fulfill the requirement of passing ThreadLocal information to child threads.

Sample code:

@Configurable
@AllArgsConstructor
public class CircuitBreakerConfiguration implements ApplicationRunner {

    private final Resilience4JCircuitBreakerFactory factory;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        ContextThreadPoolExecutor contextThreadPoolExecutor = 
                new ContextThreadPoolExecutor(2, 5, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1024));

        // **change ThreadPoolExecutor**
        factory.configureExecutorService(contextThreadPoolExecutor);
    }

    public static class ContextThreadPoolExecutor extends ThreadPoolExecutor {

        public ContextThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        public void execute(Runnable command) {
            super.execute(wrap(command));
        }

        private static Runnable wrap(Runnable runnable) {
            **SubjectContext context = SubjectContext.getContext();**
            return () -> {
                **SubjectContext.setContext(context);**
                try {
                    runnable.run();
                } finally {
                    **SubjectContext.clear();**
                }
            };
        }
    }
}
@Override
public org.springframework.cloud.client.circuitbreaker.CircuitBreaker create(String id) {
    Assert.hasText(id, "A CircuitBreaker must have an id.");
    Resilience4JCircuitBreaker resilience4JCircuitBreaker = create(id, id, this.executorService);
    return tryObservedCircuitBreaker(resilience4JCircuitBreaker);
}

@Override
public org.springframework.cloud.client.circuitbreaker.CircuitBreaker create(String id, String groupName) {
    Assert.hasText(id, "A CircuitBreaker must have an id.");
    Assert.hasText(groupName, "A CircuitBreaker must have a group name.");
    // **Fixed thread pool creation method, unable to expand**
    final ExecutorService groupExecutorService = executorServices.computeIfAbsent(groupName,
            group -> Executors.newCachedThreadPool());
    Resilience4JCircuitBreaker resilience4JCircuitBreaker = create(id, groupName, groupExecutorService);
    return tryObservedCircuitBreaker(resilience4JCircuitBreaker);
}
ryanjbaxter commented 7 months ago

Would you be interested in submitting a PR for this?

imyzt commented 7 months ago

@ryanjbaxter Of course, Please read my code to ensure it can run in any scenario. https://github.com/spring-cloud/spring-cloud-circuitbreaker/pull/181 Thanks.

yong-kurly commented 4 months ago

@ryanjbaxter I think this patch should be applied in 3.1.x as well.

ryanjbaxter commented 4 months ago

@yong-kurly it was see https://github.com/spring-cloud/spring-cloud-circuitbreaker/commit/5fa638e67b28fc9d31fdd031f7dcff835e52894b

yong-kurly commented 4 months ago

Ah! Sorry. 3.1.1 version has not been released, so I though it was not.

I'm looking forward 3.1.1 release!

yong-kurly commented 3 months ago

Just curious: May I know when the new version(3.1.x) will be released? I am waiting for this feature. 🙏

ryanjbaxter commented 3 months ago

This week.