Dubbo中提供了三种线程池的实现:
- fixed 固定大小线程池,启动时建立线程不关闭,一直持有。
- cached 缓存线程池,空闲一分钟自动删除,需要时重建。
- limited 可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
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) 方法,获得对应的线程池的执行器
}
FixedThreadPool ,实现 ThreadPool 接口,固定大小线程池,启动时建立线程,不关闭,一直持有:
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();
}
});
}