-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Introduce thread pool for multi node pipeline #3333
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
Open
yangbodong22011
wants to merge
1
commit into
redis:master
Choose a base branch
from
yangbodong22011:feature-introduce-jedis-thread-factory
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
140 changes: 140 additions & 0 deletions
140
src/main/java/redis/clients/jedis/JedisThreadFactoryBuilder.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| package redis.clients.jedis; | ||
|
|
||
| import java.lang.Thread.UncaughtExceptionHandler; | ||
| import java.util.concurrent.Executors; | ||
| import java.util.concurrent.ThreadFactory; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
|
|
||
| /** | ||
| * JedisThreadFactoryBuilder is a class that builds a ThreadFactory for Jedis. | ||
| */ | ||
| public class JedisThreadFactoryBuilder { | ||
| private String namePrefix = null; | ||
| private boolean daemon = false; | ||
| private int priority = Thread.NORM_PRIORITY; | ||
| private ThreadFactory backingThreadFactory = null; | ||
| private UncaughtExceptionHandler uncaughtExceptionHandler = null; | ||
|
|
||
| /** | ||
| * Sets the name prefix for the threads created by the ThreadFactory. | ||
| * | ||
| * @param namePrefix the name prefix for the threads | ||
| * @return the JedisThreadFactoryBuilder instance | ||
| * @throws NullPointerException if namePrefix is null | ||
| */ | ||
| public JedisThreadFactoryBuilder setNamePrefix(String namePrefix) { | ||
| if (namePrefix == null) { | ||
| throw new NullPointerException(); | ||
| } | ||
| this.namePrefix = namePrefix; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets whether the threads created by the ThreadFactory are daemon threads. | ||
| * | ||
| * @param daemon true if the threads are daemon threads, false otherwise | ||
| * @return the JedisThreadFactoryBuilder instance | ||
| */ | ||
| public JedisThreadFactoryBuilder setDaemon(boolean daemon) { | ||
| this.daemon = daemon; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the priority for the threads created by the ThreadFactory. | ||
| * | ||
| * @param priority the priority for the threads | ||
| * @return the JedisThreadFactoryBuilder instance | ||
| * @throws IllegalArgumentException if priority is not in the range of Thread.MIN_PRIORITY to Thread.MAX_PRIORITY | ||
| */ | ||
| public JedisThreadFactoryBuilder setPriority(int priority) { | ||
| if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { | ||
| throw new IllegalArgumentException(String.format( | ||
| "Thread priority (%s) must be in %d ~ %d", priority, | ||
| Thread.MIN_PRIORITY, Thread.MAX_PRIORITY)); | ||
| } | ||
|
|
||
| this.priority = priority; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the UncaughtExceptionHandler for the threads created by the ThreadFactory. | ||
| * | ||
| * @param uncaughtExceptionHandler the UncaughtExceptionHandler for the threads | ||
| * @return the JedisThreadFactoryBuilder instance | ||
| * @throws NullPointerException if uncaughtExceptionHandler is null | ||
| */ | ||
| public JedisThreadFactoryBuilder setUncaughtExceptionHandler( | ||
| UncaughtExceptionHandler uncaughtExceptionHandler) { | ||
| if (uncaughtExceptionHandler == null) { | ||
| throw new NullPointerException( | ||
| "UncaughtExceptionHandler cannot be null"); | ||
| } | ||
| this.uncaughtExceptionHandler = uncaughtExceptionHandler; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the backing ThreadFactory for the JedisThreadFactoryBuilder. | ||
| * | ||
| * @param backingThreadFactory the backing ThreadFactory | ||
| * @return the JedisThreadFactoryBuilder instance | ||
| * @throws NullPointerException if backingThreadFactory is null | ||
| */ | ||
| public JedisThreadFactoryBuilder setThreadFactory( | ||
| ThreadFactory backingThreadFactory) { | ||
| if (uncaughtExceptionHandler == null) { | ||
| throw new NullPointerException( | ||
| "BackingThreadFactory cannot be null"); | ||
| } | ||
| this.backingThreadFactory = backingThreadFactory; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Builds a ThreadFactory using the JedisThreadFactoryBuilder instance. | ||
| * | ||
| * @return the ThreadFactory | ||
| */ | ||
| public ThreadFactory build() { | ||
| return build(this); | ||
| } | ||
|
|
||
| /** | ||
| * Builds a ThreadFactory by JedisThreadFactoryBuilder. | ||
| * | ||
| * @param builder JedisThreadFactoryBuilder | ||
| * @return ThreadFactory | ||
| */ | ||
| private static ThreadFactory build(JedisThreadFactoryBuilder builder) { | ||
| final String namePrefix = builder.namePrefix; | ||
| final Boolean daemon = builder.daemon; | ||
| final Integer priority = builder.priority; | ||
| final UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler; | ||
| final ThreadFactory backingThreadFactory = (builder.backingThreadFactory != null) ? builder.backingThreadFactory | ||
| : Executors.defaultThreadFactory(); | ||
| final AtomicLong count = new AtomicLong(0); | ||
|
|
||
| return new ThreadFactory() { | ||
| @Override | ||
| public Thread newThread(Runnable runnable) { | ||
| Thread thread = backingThreadFactory.newThread(runnable); | ||
| if (daemon) { | ||
| thread.setDaemon(daemon); | ||
| } | ||
| if (priority != Thread.NORM_PRIORITY) { | ||
| thread.setPriority(priority); | ||
| } | ||
| if (namePrefix != null) { | ||
| thread.setName(namePrefix + "-" + count.getAndIncrement()); | ||
| } | ||
| if (uncaughtExceptionHandler != null) { | ||
| thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); | ||
| } | ||
| return thread; | ||
| } | ||
| }; | ||
| } | ||
| } |
120 changes: 120 additions & 0 deletions
120
src/main/java/redis/clients/jedis/JedisThreadPoolBuilder.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| package redis.clients.jedis; | ||
|
|
||
| import java.lang.Thread.UncaughtExceptionHandler; | ||
| import java.util.concurrent.BlockingQueue; | ||
| import java.util.concurrent.ExecutorService; | ||
| import java.util.concurrent.Executors; | ||
| import java.util.concurrent.RejectedExecutionHandler; | ||
| import java.util.concurrent.ThreadFactory; | ||
| import java.util.concurrent.ThreadPoolExecutor; | ||
| import java.util.concurrent.ThreadPoolExecutor.AbortPolicy; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| /** | ||
| * This class is used to build a thread pool for Jedis. | ||
| */ | ||
| public class JedisThreadPoolBuilder { | ||
| private static final Logger log = LoggerFactory.getLogger(JedisThreadPoolBuilder.class); | ||
|
|
||
| private static final RejectedExecutionHandler defaultRejectHandler = new AbortPolicy(); | ||
|
|
||
| public static PoolBuilder pool() { | ||
| return new PoolBuilder(); | ||
| } | ||
|
|
||
| /** | ||
| * Custom thread factory or use default | ||
| * @param threadNamePrefix the thread name prefix | ||
| * @param daemon daemon | ||
| * @return ThreadFactory | ||
| */ | ||
| private static ThreadFactory createThreadFactory(String threadNamePrefix, boolean daemon) { | ||
| if (threadNamePrefix != null) { | ||
| return new JedisThreadFactoryBuilder().setNamePrefix(threadNamePrefix).setDaemon(daemon) | ||
| .setUncaughtExceptionHandler(new UncaughtExceptionHandler() { | ||
| @Override | ||
| public void uncaughtException(Thread t, Throwable e) { | ||
| log.error(String.format("Thread %s threw exception %s", t.getName(), e.getMessage())); | ||
| } | ||
| }).build(); | ||
| } | ||
|
|
||
| return Executors.defaultThreadFactory(); | ||
| } | ||
|
|
||
| /** | ||
| * This class is used to build a thread pool. | ||
| */ | ||
| public static class PoolBuilder { | ||
| private int coreSize = 0; | ||
| private int maxSize = Integer.MAX_VALUE; | ||
| private long keepAliveMillSecs = 10; | ||
| private ThreadFactory threadFactory; | ||
| private String threadNamePrefix; | ||
| private boolean daemon; | ||
| private RejectedExecutionHandler rejectHandler; | ||
| private BlockingQueue<Runnable> workQueue; | ||
|
|
||
| public PoolBuilder setCoreSize(int coreSize) { | ||
| this.coreSize = coreSize; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setMaxSize(int maxSize) { | ||
| this.maxSize = maxSize; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setKeepAliveMillSecs(long keepAliveMillSecs) { | ||
| this.keepAliveMillSecs = keepAliveMillSecs; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setThreadNamePrefix(String threadNamePrefix) { | ||
| this.threadNamePrefix = threadNamePrefix; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setDaemon(boolean daemon) { | ||
| this.daemon = daemon; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setThreadFactory(ThreadFactory threadFactory) { | ||
| this.threadFactory = threadFactory; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setRejectHandler(RejectedExecutionHandler rejectHandler) { | ||
| this.rejectHandler = rejectHandler; | ||
| return this; | ||
| } | ||
|
|
||
| public PoolBuilder setWorkQueue(BlockingQueue<Runnable> workQueue) { | ||
| this.workQueue = workQueue; | ||
| return this; | ||
| } | ||
|
|
||
| public ExecutorService build() { | ||
| if (threadFactory == null) { | ||
| threadFactory = createThreadFactory(threadNamePrefix, daemon); | ||
| } | ||
|
|
||
| if (workQueue == null) { | ||
| throw new IllegalArgumentException("workQueue can't be null"); | ||
| } | ||
|
|
||
| if (rejectHandler == null) { | ||
| rejectHandler = defaultRejectHandler; | ||
| } | ||
|
|
||
| ExecutorService executorService = new ThreadPoolExecutor(coreSize, maxSize, keepAliveMillSecs, | ||
| TimeUnit.MILLISECONDS, workQueue, threadFactory, rejectHandler); | ||
|
|
||
| return executorService; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This comment was marked as off-topic.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dengliming This is set once, works every time approach :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to pass the executor when
new JedisClusterornew JedisSharding, and need a clear name to avoid misunderstanding by users, and there are many modifications(JedisCluster->ClusterPipeline->MultiNodePipelineBase).But the benefit is that we can create the executor lazily, if the user passes it, we use it; otherwise, we can create a static object.
But I personally prefer the current code, and it is one-time and clear to set directly through the
MultiNodePipelineBase#setExecutorServiceinterface (About cost: there is only a part of memory usage, if the user is not using it, no thread will be created).