[Dubbo]基礎(chǔ)組件之ThreadPool

介紹

ThreadPool 我們在開發(fā)過程中經(jīng)常使用跺嗽,java線程池的相關(guān)知識見
線程池相關(guān)文章
dubbo也不例外會使用線程池,見dubbo線程池

看完本文章主要學(xué)習(xí):

  • dubbo的線程池是如何實現(xiàn)
  • dubbo線程如何配置
  • 我們自己是先線程池注意事項

使用方式

需要通過不同的派發(fā)策略和不同的線程池配置的組合來應(yīng)對不同的場景:

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />

服務(wù)提供者

<dubbo:provider>標簽下配置threadpool 屬性羔味,默認是fixed

詳細說明

源碼位置

634200dd16f92abe846572378c902729.jpg

繼承關(guān)系

屏幕快照 2018-09-10 下午9.26.44.png

Cached:緩存線程池乡革,空閑一分鐘自動刪除高帖,需要時重建
Fixed:固定大小的線程池族沃,默認
Limited:可伸縮線程池误辑,但池中的線程數(shù)只會增長不會收縮茉兰。只增長不收縮的目的是為了避免收縮時突然來了大流量引起的性能問題尤泽。

ThreadPool

/**
 * ThreadPool
 * 
 * @author william.liangf
 */
@SPI("fixed")
public interface ThreadPool {
    
    /**
     * 線程池
     * 
     * @param url 線程參數(shù)
     * @return 線程池
     */
    @Adaptive({Constants.THREADPOOL_KEY})
    Executor getExecutor(URL url);

}

ThreadPool$Adaptive

啟動的時候創(chuàng)建的適配對象
注:Adaptive注解在類上和方法上面表達的意義不一樣

注解在類上:代表人工實現(xiàn),實現(xiàn)一個裝飾類(設(shè)計模式中的裝飾模式)
注解在方法上:代表自動生成和編譯一個動態(tài)的Adpative類,它主要是用于SPI坯约,因為spi的類是不固定熊咽、未知的擴展類,所以設(shè)計了動態(tài)$Adaptive類.

public class ThreadPool$Adaptive implements com.alibaba.dubbo.common.threadpool.ThreadPool {
    @Override
    public java.util.concurrent.Executor getExecutor(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = url.getParameter("threadpool", "fixed");
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.threadpool.ThreadPool) name from url(" + url.toString() + ") use keys([threadpool])");
        ThreadPool extension = ExtensionLoader.getExtensionLoader(ThreadPool.class).getExtension(extName);
        return extension.getExecutor(arg0);
    }
}

FixedThreadPool

/**
 * Creates a thread pool that reuses a fixed number of threads
 * 固定線程池
 *
 * @see java.util.concurrent.Executors#newFixedThreadPool(int)
 */
public class FixedThreadPool implements ThreadPool {

    @Override
    public Executor getExecutor(URL url) {
        // 線程名
        String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
        // 固定線程數(shù)
        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
        // 隊列大小
        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
        // 線程池執(zhí)行器
        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));
    }

}

CachedThreadPool

/**
 * This thread pool is self-tuned. Thread will be recycled after idle for one minute, and new thread will be created for
 * the upcoming request.
 * 緩存線程池闹丐,空閑一分鐘自動刪除横殴,需要時重建
 *
 * @see java.util.concurrent.Executors#newCachedThreadPool()
 */
public class CachedThreadPool implements ThreadPool {

    @Override
    public Executor getExecutor(URL url) {
        // 線程池名稱
        String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
        // 核心線程池個數(shù)
        int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
        // 最大線程池個數(shù)
        int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
        //隊列個數(shù)
        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
        // 存活時間,默認是毫秒數(shù)
        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

/**
 * Creates a thread pool that creates new threads as needed until limits reaches. This thread pool will not shrink
 * automatically.
 * 可伸縮線程池妇智,但池中的線程數(shù)只會增長不會收縮滥玷。只增長不收縮的目的是為了避免收縮時突然來了大流量引起的性能問題。
 */
public class LimitedThreadPool implements ThreadPool {

    @Override
    public Executor getExecutor(URL url) {
        // 線程名
        String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
        // 核心線程池個數(shù)
        int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
        // 最大線程池個數(shù)
        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
        // 隊列大小
        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
        // 線程池執(zhí)行器
        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

帶打印異常堆棧的拒絕策略

@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);
        dumpJStack();
        // 將異常拋出去
        throw new RejectedExecutionException(msg);
    }

    private void dumpJStack() {
        long now = System.currentTimeMillis();

        //dump every 10 minutes 10分鐘打印一次
        if (now - lastPrintTime < 10 * 60 * 1000) {
            return;
        }

        if (!guard.tryAcquire()) {
            return;
        }

        // 異步打印
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                //獲取打印的目錄
                String dumpPath = url.getParameter(Constants.DUMP_DIRECTORY, System.getProperty("user.home"));

                SimpleDateFormat sdf;
                //系統(tǒng)
                String OS = System.getProperty("os.name").toLowerCase();

                // 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();
            }
        });

    }

總結(jié)

dubbo客戶端使用的是 shared類型的線程池

 public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException{
        super(url, wrapChannelHandler(url, handler));
    }
protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) {
        url = ExecutorUtil.setThreadName(url, CLIENT_THREAD_POOL_NAME);
        url = url.addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
        return ChannelHandlers.wrap(handler, url);
    }

    public static final String DEFAULT_CLIENT_THREADPOOL = "shared";

服務(wù)提供者使用的是fixed方式的線程池

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巍棱,一起剝皮案震驚了整個濱河市惑畴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌航徙,老刑警劉巖如贷,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異到踏,居然都是意外死亡杠袱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門窝稿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楣富,“玉大人,你說我怎么就攤上這事伴榔∥坪” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵踪少,是天一觀的道長塘安。 經(jīng)常有香客問我,道長援奢,這世上最難降的妖魔是什么兼犯? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮集漾,結(jié)果婚禮上切黔,老公的妹妹穿的比我還像新娘。我一直安慰自己帆竹,他們只是感情好绕娘,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著栽连,像睡著了一般险领。 火紅的嫁衣襯著肌膚如雪侨舆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天绢陌,我揣著相機與錄音挨下,去河邊找鬼。 笑死脐湾,一個胖子當著我的面吹牛臭笆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秤掌,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼愁铺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闻鉴?” 一聲冷哼從身側(cè)響起茵乱,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎孟岛,沒想到半個月后瓶竭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡渠羞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年斤贰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片次询。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡荧恍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屯吊,到底是詐尸還是另有隱情块饺,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布雌芽,位于F島的核電站,受9級特大地震影響辨嗽,放射性物質(zhì)發(fā)生泄漏世落。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一糟需、第九天 我趴在偏房一處隱蔽的房頂上張望屉佳。 院中可真熱鬧,春花似錦洲押、人聲如沸武花。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽体箕。三九已至专钉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間累铅,已是汗流浹背跃须。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娃兽,地道東北人菇民。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像投储,于是被迫代替她去往敵國和親第练。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容