Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement Request: Customizable groupExecutorService in Resilience4J CircuitBreakerFactory #180

Closed
imyzt opened this issue Dec 7, 2023 · 7 comments · Fixed by #181
Closed
Labels
enhancement New feature or request feedback-provided
Milestone

Comments

@imyzt
Copy link
Contributor

imyzt commented Dec 7, 2023

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
Copy link
Contributor

Would you be interested in submitting a PR for this?

imyzt added a commit to imyzt/spring-cloud-circuitbreaker that referenced this issue Dec 8, 2023
@imyzt
Copy link
Contributor Author

imyzt commented Dec 10, 2023

@ryanjbaxter Of course, Please read my code to ensure it can run in any scenario. #181
Thanks.

imyzt added a commit to imyzt/spring-cloud-circuitbreaker that referenced this issue Dec 12, 2023
@ryanjbaxter ryanjbaxter linked a pull request Dec 12, 2023 that will close this issue
@ryanjbaxter ryanjbaxter added this to the 3.0.4 milestone Dec 12, 2023
ryanjbaxter added a commit that referenced this issue Dec 12, 2023
Customizable groupExecutorService #180
@yong-kurly
Copy link

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

@ryanjbaxter
Copy link
Contributor

@yong-kurly it was see 5fa638e

@yong-kurly
Copy link

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
Copy link

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

@ryanjbaxter
Copy link
Contributor

This week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feedback-provided
Projects
No open projects
Status: Done
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants