Dubboには3つのスレッドプール実装があります。
- 固定サイズのスレッドプール起動時に作成されたスレッドは閉じられず、永久に保持されません。
- cachedキャッシュスレッドプール。1分間アイドル状態になると自動的に削除され、必要に応じて再構築されます。
- スケーラブルなスレッドプールには制限がありますが、プール内のスレッド数は、縮小せずに増加するだけです。縮小するのではなく拡大するだけの目的は、縮小時に突然の大きな流れによって引き起こされるパフォーマンスの問題を回避することです。
ThreadPool、スレッドプールインターフェイス:
@SPI( "fixed")アノテーション、Dubbo SPI拡張ポイント、デフォルトは "fixed";
@Adaptive({Constants.THREADPOOL_KEY})アノテーションは、Dubbo SPI Adaptiveメカニズムに基づいて、対応するスレッドプールの実装をロードします。 URL.threadpool属性を使用します。
@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) 方法,获得对应的线程池的执行器
}
固定サイズのスレッドプールであるThreadPoolインターフェイスを実装するFixedThreadPoolは、起動時にスレッドを確立し、閉じず、常に保持します。
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 对象。
*/
}
}
上記は、スレッド名、スレッド番号、およびキュー番号の取得に関するものです。現在、サービスプロバイダーのみが使用しており、設定方法は次のとおりです。
<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は、ThreadPoolインターフェースを実装し、スレッドプールをキャッシュし、一定時間アイドル状態にし、必要に応じて自動的に削除して再構築します。
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は、ThreadPoolインターフェイス、スケーラブルなスレッドプールを実装しますが、プール内のスレッド数は増加するだけで、縮小することはありません。縮小するのではなく拡大するだけの目的は、縮小時に突然の大量のトラフィックによって引き起こされるパフォーマンスの問題を回避することです。アイドル時間は無限です。つまり、自動的に削除されません。
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、java.util.concurrent.ThreadPoolExecutor.AbortPolicyの実装、ポリシー実装クラスの拒否。JStackを印刷し、スレッドのステータスを分析します:
属性:
/**
* 线程名
*/
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)実装メソッド:
@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()メソッド、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();
}
});
}