Dubbo's thread pool

There are three thread pool implementations provided in Dubbo:

  1. fixed A fixed-size thread pool. Threads created at startup are not closed and held forever.
  2. cached Cache thread pool, automatically deleted after being idle for one minute, and rebuilt when needed.
  3. Limited scalable thread pool, but the number of threads in the pool will only grow without shrinking. The purpose of only growing and not shrinking is to avoid the performance problems caused by sudden large flow when shrinking.

ThreadPool, thread pool interface:
@SPI ("fixed") annotation, Dubbo SPI extension point, the default is "fixed";
@Adaptive ({Constants.THREADPOOL_KEY}) annotation, based on Dubbo SPI Adaptive mechanism, load the corresponding thread pool implementation, Use the URL.threadpool attribute.

@SPI("fixed")
public interface ThreadPool {

    /**
     * Thread pool
     *
     * @param url URL contains thread parameter
     * @return thread pool
     */
    @Adaptive({Constants.THREADPOOL_KEY})
    Executor getExecutor(URL url);
// getExecutor(url) 方法,获得对应的线程池的执行器
}

FixedThreadPool, which implements the ThreadPool interface, a fixed-size thread pool, establishes threads at startup, does not close, and always holds:

 public class FixedThreadPool implements ThreadPool {
 
     @Override
     public Executor getExecutor(URL url) {
          // 线程名
          String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
         // 线程数
         int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
         // 队列数
         int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
         // 创建执行器
         return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
                 queues == 0 ? new SynchronousQueue<Runnable>() :
                         (queues < 0 ? new LinkedBlockingQueue<Runnable>()
                                 : new LinkedBlockingQueue<Runnable>(queues)),
                 new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
                 // 创建 NamedThreadFactory 对象,用于生成线程名。
                 // 创建 AbortPolicyWithReport 对象,用于当任务添加到线程池中被拒绝时。
 /*
创建执行器 ThreadPoolExecutor 对象。
根据不同的队列数,使用不同的队列实现:
queues == 0 , SynchronousQueue 对象。
queues < 0 , LinkedBlockingQueue 对象。
queues > 0 ,带队列数的 LinkedBlockingQueue 对象。
*/
     }
 
 }

The above is about obtaining the thread name, thread number, and queue number. Currently only used by service providers, the configuration method is as follows:

<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService">
    
    <dubbo:parameter key="threadname" value="shuaiqi" />
    <dubbo:parameter key="threads" value="123" />
    <dubbo:parameter key="queues" value="10" />

</dubbo:service>

CachedThreadPool, implements the ThreadPool interface, caches the thread pool, idles for a certain period of time, automatically deletes, and rebuilds when needed:

 public class CachedThreadPool implements ThreadPool {
 
     @Override
     public Executor getExecutor(URL url) {
         // 线程池名
         String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
         // 核心线程数
         int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
         // 最大线程数
         int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
         // 队列数
         int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
         // 线程存活时长
        int alive = url.getParameter(Constants.ALIVE_KEY, Constants.DEFAULT_ALIVE);
         // 创建执行器
         return new ThreadPoolExecutor(cores, threads, alive, TimeUnit.MILLISECONDS,
                 queues == 0 ? new SynchronousQueue<Runnable>() :
                        (queues < 0 ? new LinkedBlockingQueue<Runnable>()
                                 : new LinkedBlockingQueue<Runnable>(queues)),
                 new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
     }
 
 }

LimitedThreadPool, implements the ThreadPool interface, scalable thread pool, but the number of threads in the pool will only increase and not shrink. The purpose of only growing and not shrinking is to avoid performance problems caused by sudden large traffic during shrinking. The idle time is infinite, that is, it will not be automatically deleted:

 public class LimitedThreadPool implements ThreadPool {
 
     @Override
     public Executor getExecutor(URL url) {
         // 线程名
         String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
         // 核心线程数
         int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
         // 最大线程数
         int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
         // 队列数
         int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
         // 创建执行器
         return new ThreadPoolExecutor(cores, threads, Long.MAX_VALUE, TimeUnit.MILLISECONDS,
                 queues == 0 ? new SynchronousQueue<Runnable>() :
                         (queues < 0 ? new LinkedBlockingQueue<Runnable>()
                                 : new LinkedBlockingQueue<Runnable>(queues)),
                 new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
     }
 
 }

AbortPolicyWithReport, implement java.util.concurrent.ThreadPoolExecutor.AbortPolicy, reject policy implementation class. Print JStack and analyze thread status:
attributes:

/**
 * 线程名
 */
private final String threadName;
/**
 * URL 对象
 */
private final URL url;
/**
 * 最后打印时间
 */
private static volatile long lastPrintTime = 0;
/**
 * 信号量,大小为 1 。
 */
private static Semaphore guard = new Semaphore(1);

public AbortPolicyWithReport(String threadName, URL url) {
    this.threadName = threadName;
    this.url = url;
}

rejectedExecution (Runnable, ThreadPoolExecutor) Implementation method:

 @Override
 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     // 打印告警日志
     String msg = String.format("Thread pool is EXHAUSTED!" +
                     " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +
                     " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
             threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
             e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
             url.getProtocol(), url.getIp(), url.getPort());
     logger.warn(msg);
     // 打印 JStack ,分析线程状态。
     dumpJStack();
     // 抛出 RejectedExecutionException 异常
     throw new RejectedExecutionException(msg);
}

dumpJStack () method, print JStack:

 private void dumpJStack() {
     long now = System.currentTimeMillis();
 
     // 每 10 分钟,打印一次。
     // dump every 10 minutes
     if (now - lastPrintTime < 10 * 60 * 1000) {
         return;
     }
 
     // 获得信号量
     if (!guard.tryAcquire()) {
         return;
     }
 
     // 创建线程池,后台执行打印 JStack
     Executors.newSingleThreadExecutor().execute(new Runnable() {
         @Override
         public void run() {
 
             // 获得系统
             String OS = System.getProperty("os.name").toLowerCase();
             // 获得路径
             String dumpPath = url.getParameter(Constants.DUMP_DIRECTORY, System.getProperty("user.home"));
             SimpleDateFormat sdf;
             // window system don't support ":" in file name
             if(OS.contains("win")){
                 sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
             }else {
                 sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
             }
             String dateStr = sdf.format(new Date());
             // 获得输出流
             FileOutputStream jstackStream = null;
             try {
                 jstackStream = new FileOutputStream(new File(dumpPath, "Dubbo_JStack.log" + "." + dateStr));
                 // 打印 JStack
                 JVMUtil.jstack(jstackStream);
             } catch (Throwable t) {
                 logger.error("dump jstack error", t);
             } finally {
                 // 释放信号量
                 guard.release();
                 // 释放输出流
                 if (jstackStream != null) {
                     try {
                         jstackStream.flush();
                         jstackStream.close();
                     } catch (IOException e) {
                     }
                 }
             }
             // 记录最后打印时间
             lastPrintTime = System.currentTimeMillis();
         }
     });
 
 }
Published 46 original articles · won praise 6 · views 3847

Guess you like

Origin blog.csdn.net/weixin_43257196/article/details/105457914