精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

面試官:一個(gè) SpringBoot 項(xiàng)目能處理多少請(qǐng)求?

開發(fā) 前端
有時(shí)候,面試官就喜歡給出這樣的“模糊”的問題,因?yàn)閱栴}越模糊,坑就越多,當(dāng)面試者跳進(jìn)自己挖好的坑里面的時(shí)候,就是結(jié)束一次交鋒的時(shí)候;當(dāng)面試者看出來自己挖好的坑,并繞過去的時(shí)候,也是結(jié)束一輪交鋒的時(shí)候。

你好呀,我是歪歪。

這篇文章帶大家盤一個(gè)讀者遇到的面試題哈。

根據(jù)讀者轉(zhuǎn)述,面試官的原問題就是:一個(gè) SpringBoot 項(xiàng)目能同時(shí)處理多少請(qǐng)求?

不知道你聽到這個(gè)問題之后的第一反應(yīng)是什么。

我大概知道他要問的是哪個(gè)方向,但是對(duì)于這種只有一句話的面試題,我的第一反應(yīng)是:會(huì)不會(huì)有坑?

所以并不會(huì)貿(mào)然答題,先追問一些消息,比如:這個(gè)項(xiàng)目具體是干什么的?項(xiàng)目大概進(jìn)行了哪些參數(shù)配置?使用的 web 容器是什么?部署的服務(wù)器配置如何?有哪些接口?接口響應(yīng)平均時(shí)間大概是多少?

這樣,在幾個(gè)問題的拉扯之后,至少在面試題考察的方向方面能基本和面試官達(dá)成了一致。

比如前面的面試問題,經(jīng)過幾次拉扯之后,面試官可能會(huì)修改為:

一個(gè) SpringBoot 項(xiàng)目,未進(jìn)行任何特殊配置,全部采用默認(rèn)設(shè)置,這個(gè)項(xiàng)目同一時(shí)刻,最多能同時(shí)處理多少請(qǐng)求?

能處理多少呢?

我也不知道,但是當(dāng)問題變成上面這樣之后,我找到了探索答案的角度。

既然“未進(jìn)行任何特殊配置”,那我自己搞個(gè) Demo 出來,壓一把不就完事了嗎?

坐穩(wěn)扶好,準(zhǔn)備發(fā)車。

Demo

小手一抖,先搞個(gè) Demo 出來。

這個(gè) Demo 非常的簡(jiǎn)單,就是通過 idea 創(chuàng)建一個(gè)全新的 SpringBoot 項(xiàng)目就行。

我的 SpringBoot 版本使用的是 2.7.13。

整個(gè)項(xiàng)目只有這兩個(gè)依賴:

圖片圖片

整個(gè)項(xiàng)目也只有兩個(gè)類,要得就是一個(gè)空空如也,一清二白。

圖片圖片

項(xiàng)目中的 TestController,里面只有一個(gè) getTest 方法,用來測(cè)試,方法里面接受到請(qǐng)求之后直接 sleep 一小時(shí)。

目的就是直接把當(dāng)前請(qǐng)求線程占著,這樣我們才能知道項(xiàng)目中一共有多少個(gè)線程可以使用:

@Slf4j
@RestController
public class TestController {

    @GetMapping("/getTest")
    public void getTest(int num) throws Exception {
        log.info("{} 接受到請(qǐng)求:num={}", Thread.currentThread().getName(), num);
        TimeUnit.HOURS.sleep(1);
    }
}

項(xiàng)目中的 application.properties 文件也是空的:

圖片圖片

這樣,一個(gè)“未進(jìn)行任何特殊配置”的 SpringBoot 不就有了嗎?

基于這個(gè) Demo,前面的面試題就要變成了:我短時(shí)間內(nèi)不斷的調(diào)用這個(gè) Demo 的 getTest 方法,最多能調(diào)用多少次?

問題是不是又變得更加簡(jiǎn)單了一點(diǎn)?

那么前面這個(gè)“短時(shí)間內(nèi)不斷的調(diào)用”,用代碼怎么表示呢?

很簡(jiǎn)單,就是在循環(huán)中不斷的進(jìn)行接口調(diào)用就行了。

public class MainTest {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            new Thread(() -> {
                HttpUtil.get("127.0.0.1:8080/getTest?num=" + finalI);
            }).start();
        }
        //阻塞主線程
        Thread.yield();
    }
}

當(dāng)然了,這個(gè)地方你用一些壓測(cè)工具,比如 jmeter 啥的,會(huì)顯得逼格更高,更專業(yè)。我這里就偷個(gè)懶,直接上代碼了。

答案

經(jīng)過前面的準(zhǔn)備工作,Demo 和測(cè)試代碼都就緒了。

接下來就是先把 Demo 跑起來:

圖片圖片

然后跑一把 MainTest。

當(dāng) MainTest 跑起來之后,Demo 這邊就會(huì)快速的、大量的輸出這樣的日志:

圖片圖片

也就是我前面 getTest 方法中寫的日志:

圖片圖片

好,現(xiàn)在我們回到這個(gè)問題:

我短時(shí)間內(nèi)不斷的調(diào)用這個(gè) Demo 的 getTest 方法,最多能調(diào)用多少次?

來,請(qǐng)你告訴我怎么得到這個(gè)問題的答案?

我這里就是一個(gè)大力出奇跡,直接統(tǒng)計(jì)“接受到請(qǐng)求”關(guān)鍵字在日志中出現(xiàn)的次數(shù)就行了:

圖片圖片

很顯然,答案就是:

所以,當(dāng)面試官問你:一個(gè) SpringBoot 項(xiàng)目能同時(shí)處理多少請(qǐng)求?

你裝作仔細(xì)思考之后,篤定的說:200 次。

面試官微微點(diǎn)頭,并等著你繼續(xù)說下去。

你也暗自歡喜,幸好看了歪歪歪師傅的文章,背了個(gè)答案。然后等著面試官繼續(xù)問其他問題。

氣氛突然就尷尬了起來。

接著,你就回家等通知了。

200 次,這個(gè)回答是對(duì)的,但是你只說 200 次,這個(gè)回答就顯得有點(diǎn)尬了。

重要的是,這個(gè)值是怎么來的?

所以,下面這一部分,你也要背下來。

怎么來的?

在開始探索怎么來的之前,我先問你一個(gè)問題,這個(gè) 200 個(gè)線程,是誰的線程,或者說是誰在管理這個(gè)線程?

是 SpringBoot 嗎?

肯定不是,SpringBoot 并不是一個(gè) web 容器。

應(yīng)該是 Tomcat 在管理這 200 個(gè)線程。

這一點(diǎn),我們通過線程 Dump 也能進(jìn)行驗(yàn)證:

圖片圖片

圖片圖片

通過線程 Dump 文件,我們可以知道,大量的線程都在 sleep 狀態(tài)。而點(diǎn)擊這些線程,查看其堆棧消息,可以看到 Tomcat、threads、ThreadPoolExecutor 等關(guān)鍵字:

at org.apache.Tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.Tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.Tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.Tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.Tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

基于“短時(shí)間內(nèi)有 200 個(gè)請(qǐng)求被立馬處理的”這個(gè)現(xiàn)象,結(jié)合你背的滾瓜爛熟的、非常扎實(shí)的線程池知識(shí),你先大膽的猜一個(gè):Tomcat 默認(rèn)核心線程數(shù)是 200。

接下來,我們就是要去源碼里面驗(yàn)證這個(gè)猜測(cè)是否正確了。

我之前分享過閱讀源碼的方式,《我試圖通過這篇文章,教會(huì)你一種閱讀源碼的方式。》,其中最重要的一條就是打一個(gè)有效的斷點(diǎn),然后基于斷點(diǎn)處的調(diào)用棧去定位源碼。

這里我再教你一個(gè)不用打斷點(diǎn)也能獲取到調(diào)用棧的方法。

在前面已經(jīng)展示過了,就是線程 Dump。

右邊就是一個(gè)線程完整的調(diào)用棧:

圖片圖片

從這個(gè)調(diào)用棧中,由于我們要找的是 Tomcat 線程池相關(guān)的源碼,所以第一次出現(xiàn)相關(guān)關(guān)鍵字的地方就是這一行:

org.apache.Tomcat.util.threads.ThreadPoolExecutor.Worker#run

圖片圖片

然后我們?cè)谶@一行打上斷點(diǎn)。

重啟項(xiàng)目,開始調(diào)試。

進(jìn)入 runWorker 之后,這部分代碼看起來就非常眼熟了:

圖片圖片

簡(jiǎn)直和 JDK 里面的線程池源碼一模一樣。

如果你熟悉 JDK 線程池源碼的話,調(diào)試 Tomcat 的線程池,那個(gè)感覺,就像是回家一樣。

如果你不熟悉的話,我建議你盡快去熟悉熟悉。

隨著斷點(diǎn)往下走,在 getTask 方法里面,可以看到關(guān)于線程池的幾個(gè)關(guān)鍵參數(shù):

org.apache.Tomcat.util.threads.ThreadPoolExecutor#getTask

圖片圖片

corePoolSize,核心線程數(shù),值為 10。

maximumPoolSize,最大線程數(shù),值為 200。

而且基于 maximumPoolSize 這個(gè)參數(shù),你往前翻代碼,會(huì)發(fā)現(xiàn)這個(gè)默認(rèn)值就是 200:

圖片圖片

好,到這里,你發(fā)現(xiàn)你之前猜測(cè)的“Tomcat 默認(rèn)核心線程數(shù)是 200”是不對(duì)的。

但是你一點(diǎn)也不慌,再次結(jié)合你背的滾瓜爛熟的、非常扎實(shí)的線程池知識(shí)。

并在心里又默念了一次:當(dāng)線程池接受到任務(wù)之后,先啟用核心線程數(shù),再使用隊(duì)列長(zhǎng)度,最后啟用最大線程數(shù)。

因?yàn)槲覀兦懊骝?yàn)證了,Tomcat 可以同時(shí)間處理 200 個(gè)請(qǐng)求,而它的線程池核心線程數(shù)只有 10,最大線程數(shù)是 200。

這說明,我前面這個(gè)測(cè)試用例,把隊(duì)列給塞滿了,從而導(dǎo)致 Tomcat 線程池啟用了最大線程數(shù):

圖片圖片

嗯,一定是這樣的!

那么,現(xiàn)在的關(guān)鍵問題就是:Tomcat 線程池默認(rèn)的隊(duì)列長(zhǎng)度是多少呢?

在當(dāng)前的這個(gè) Debug 模式下,隊(duì)列長(zhǎng)度可以通過 Alt+F8 進(jìn)行查看:

圖片圖片

wc,這個(gè)值是 Integer.MAX_VALUE,這么大?

我一共也才 1000 個(gè)任務(wù),不可能被占滿啊?

一個(gè)線程池:

  • 核心線程數(shù),值為 10。
  • 最大線程數(shù),值為 200。
  • 隊(duì)列長(zhǎng)度,值為 Integer.MAX_VALUE。

1000 個(gè)比較耗時(shí)的任務(wù)過來之后,應(yīng)該是只有 10 個(gè)線程在工作,然后剩下的 990 個(gè)進(jìn)隊(duì)列才對(duì)啊?

難道我八股文背錯(cuò)了?

這個(gè)時(shí)候不要慌,嗦根辣條冷靜一下。

目前已知的是核心線程數(shù),值為 10。這 10 個(gè)線程的工作流程是符合我們認(rèn)知的。

但是第 11 個(gè)任務(wù)過來的時(shí)候,本應(yīng)該進(jìn)入隊(duì)列去排隊(duì)。

現(xiàn)在看起來,是直接啟用最大線程數(shù)了。

所以,我們先把測(cè)試用例修改一下:

圖片圖片

那么問題就來了:最后一個(gè)請(qǐng)求到底是怎么提交到線程池里面的?

前面說了,Tomcat 的線程池源碼和 JDK 的基本一樣。

往線程池里面提交任務(wù)的時(shí)候,會(huì)執(zhí)行 execute 這個(gè)方法:

org.apache.Tomcat.util.threads.ThreadPoolExecutor#execute(java.lang.Runnable)

圖片圖片

對(duì)于 Tomcat 它會(huì)調(diào)用到 executeInternal 這個(gè)方法:

org.apache.Tomcat.util.threads.ThreadPoolExecutor#executeInternal

圖片圖片

這個(gè)方法里面,標(biāo)號(hào)為 ① 的地方,就是判斷當(dāng)前工作線程數(shù)是否小于核心線程數(shù),小于則直接調(diào)用 addWorker 方法,創(chuàng)建線程。

標(biāo)號(hào)為 ② 的地方主要是調(diào)用了 offer 方法,看看隊(duì)列里面是否還能繼續(xù)添加任務(wù)。

如果不能繼續(xù)添加,說明隊(duì)列滿了,則來到標(biāo)號(hào)為 ③ 的地方,看看是否能執(zhí)行 addWorker 方法,創(chuàng)建非核心線程,即啟用最大線程數(shù)。

把這個(gè)邏輯捋順之后,接下來我們應(yīng)該去看哪部分的代碼,就很清晰了。

主要就是去看 workQueue.offer(command) 這個(gè)邏輯。

如果返回 true 則表示加入到隊(duì)列,返回 false 則表示啟用最大線程數(shù)嘛。

這個(gè) workQueue 是 TaskQueue,看起來一點(diǎn)也不眼熟:

圖片圖片

當(dāng)然不眼熟了,因?yàn)檫@個(gè)是 Tomcat 自己基于 LinkedBlockingQueue 搞的一個(gè)隊(duì)列。

圖片圖片

問題的答案就藏在 TaskQueue 的 offer 方法里面。

所以我重點(diǎn)帶你盤一下這個(gè) offer 方法:

org.apache.Tomcat.util.threads.TaskQueue#offer

圖片圖片

標(biāo)號(hào)為 ① 的地方,判斷了 parent 是否為 null,如果是則直接調(diào)用父類的 offer 方法。說明要啟用這個(gè)邏輯,我們的 parent 不能為 null。

那么這個(gè) parent 是什么玩意,從哪里來的呢?

圖片圖片

parent 就是 Tomcat 線程池,通過其 set 方法可以知道,是在線程池完成初始化之后,進(jìn)行了賦值。

也就是說,你可以理解為,在 Tomcat 的場(chǎng)景下,parent 不會(huì)為空。

標(biāo)號(hào)為 ② 的地方,調(diào)用了 getPoolSizeNoLock 方法:

圖片圖片

這個(gè)方法是獲取當(dāng)前線程池中有多個(gè)線程。

所以如果這個(gè)表達(dá)式為 true:

parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()

就表明當(dāng)前線程池的線程數(shù)已經(jīng)是配置的最大線程數(shù)了,那就調(diào)用 offer 方法,把當(dāng)前請(qǐng)求放到到隊(duì)列里面去。

標(biāo)號(hào)為 ③ 的地方,是判斷已經(jīng)提交到線程池里面待執(zhí)行或者正在執(zhí)行的任務(wù)個(gè)數(shù),是否比當(dāng)前線程池的線程數(shù)還少。

如果是,則說明當(dāng)前線程池有空閑線程可以執(zhí)行任務(wù),則把任務(wù)放到隊(duì)列里面去,就會(huì)被空閑線程給取走執(zhí)行。

然后,關(guān)鍵的來了,標(biāo)號(hào)為 ④ 的地方。

如果當(dāng)前線程池的線程數(shù)比線程池配置的最大線程數(shù)還少,則返回 false。

前面說了,offer 方法返回 false,會(huì)出現(xiàn)什么情況?

圖片圖片

是不是直接開始到上圖中標(biāo)號(hào)為 ③ 的地方,去嘗試添加非核心線程了?

也就是啟用最大線程數(shù)這個(gè)配置了。

所以,朋友們,這個(gè)是什么情況?

這個(gè)情況確實(shí)就和我們背的線程池的八股文不一樣了啊。

JDK 的線程池,是先使用核心線程數(shù)配置,接著使用隊(duì)列長(zhǎng)度,最后再使用最大線程配置。

Tomcat 的線程池,就是先使用核心線程數(shù)配置,再使用最大線程配置,最后才使用隊(duì)列長(zhǎng)度。

所以,以后當(dāng)面試官給你說:我們聊聊線程池的工作機(jī)制吧?

你就先追問一句:你是說的 JDK 的線程池呢還是 Tomcat 的線程池呢,因?yàn)檫@兩個(gè)在運(yùn)行機(jī)制上有一點(diǎn)差異。

然后,你就看他的表情。

如果透露出一絲絲遲疑,然后輕描淡寫的說一句:那就對(duì)比著說一下吧。

那么恭喜你,在這個(gè)題目上開始掌握了一點(diǎn)主動(dòng)權(quán)。

最后,為了讓你更加深刻的理解到 Tomcat 線程池和 JDK 線程池的不一樣,我給你搞一個(gè)直接復(fù)制過去就能運(yùn)行的代碼。

當(dāng)你把 taskqueue.setParent(executor) 這行代碼注釋掉的時(shí)候,它的運(yùn)行機(jī)制就是 JDK 的線程池。

當(dāng)存在這行代碼的時(shí)候,它的運(yùn)行機(jī)制就變成了 Tomcat 的線程池。

玩去吧。

import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

public class TomcatThreadPoolExecutorTest {

    public static void main(String[] args) throws InterruptedException {
        String namePrefix = "歪歪歪-exec-";
        boolean daemon = true;
        TaskQueue taskqueue = new TaskQueue(300);
        TaskThreadFactory tf = new TaskThreadFactory(namePrefix, daemon, Thread.NORM_PRIORITY);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                150, 60000, TimeUnit.MILLISECONDS, taskqueue, tf);
        taskqueue.setParent(executor);
        for (int i = 0; i < 300; i++) {
            try {
                executor.execute(() -> {
                    logStatus(executor, "創(chuàng)建任務(wù)");
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Thread.currentThread().join();
    }

    private static void logStatus(ThreadPoolExecutor executor, String name) {
        TaskQueue queue = (TaskQueue) executor.getQueue();
        System.out.println(Thread.currentThread().getName() + "-" + name + "-:" +
                "核心線程數(shù):" + executor.getCorePoolSize() +
                "\t活動(dòng)線程數(shù):" + executor.getActiveCount() +
                "\t最大線程數(shù):" + executor.getMaximumPoolSize() +
                "\t總?cè)蝿?wù)數(shù):" + executor.getTaskCount() +
                "\t當(dāng)前排隊(duì)線程數(shù):" + queue.size() +
                "\t隊(duì)列剩余大小:" + queue.remainingCapacity());
    }
}

等等

如果你之前確實(shí)沒了解過 Tomcat 線程池的工作機(jī)制,那么看到這里的時(shí)候也許你會(huì)覺得確實(shí)是有一點(diǎn)點(diǎn)收獲。

但是,注意我要說但是了。

還記得最開始的時(shí)候面試官的問題嗎?

面試官的原問題就是:一個(gè) SpringBoot 項(xiàng)目能同時(shí)處理多少請(qǐng)求?

那么請(qǐng)問,前面我講了這么大一坨 Tomcat 線程池運(yùn)行原理,這個(gè)回答,和這個(gè)問題匹配嗎?

是的,除了最開始提出的 200 這個(gè)數(shù)值之外,并不匹配,甚至在面試官的眼里完全是答非所問了。

所以,為了把這兩個(gè)“并不匹配”的東西比較順暢的鏈接起來,你必須要先回答面試官的問題,然后再開始擴(kuò)展。

比如這樣答:一個(gè)未進(jìn)行任何特殊配置,全部采用默認(rèn)設(shè)置的 SpringBoot 項(xiàng)目,這個(gè)項(xiàng)目同一時(shí)刻最多能同時(shí)處理多少請(qǐng)求,取決于我們使用的 web 容器,而 SpringBoot 默認(rèn)使用的是 Tomcat。

Tomcat 的默認(rèn)核心線程數(shù)是 10,最大線程數(shù) 200,隊(duì)列長(zhǎng)度是無限長(zhǎng)。但是由于其運(yùn)行機(jī)制和 JDK 線程池不一樣,在核心線程數(shù)滿了之后,會(huì)直接啟用最大線程數(shù)。所以,在默認(rèn)的配置下,同一時(shí)刻,可以處理 200 個(gè)請(qǐng)求。

在實(shí)際使用過程中,應(yīng)該基于服務(wù)實(shí)際情況和服務(wù)器配置等相關(guān)消息,對(duì)該參數(shù)進(jìn)行評(píng)估設(shè)置。

這個(gè)回答就算是差不多了。

但是,如果很不幸,如果你遇到了我,為了驗(yàn)證你是真的自己去摸索過,還是僅僅只是看了幾篇文章,我可能還會(huì)追問一下:

那么其他什么都不動(dòng),如果我僅僅加入 server.tomcat.max-cnotallow=10 這個(gè)配置呢,那么這個(gè)時(shí)候最多能處理多少個(gè)請(qǐng)求?

你可能就要猜了:10 個(gè)。

是的,我重新提交 1000 個(gè)任務(wù)過來,在控制臺(tái)輸出的確實(shí)是 10 個(gè),

圖片圖片

那么 max-connections 這個(gè)參數(shù)它怎么也能控制請(qǐng)求個(gè)數(shù)呢?

為什么在前面的分析過程中我們并沒有注意到這個(gè)參數(shù)呢?

首先我們看一下它的默認(rèn)值:

圖片圖片

因?yàn)樗哪J(rèn)值是 8192,比最大線程數(shù) 200 大,這個(gè)參數(shù)并沒有限制到我們,所以我們沒有關(guān)注到它。

當(dāng)我們把它調(diào)整為 10 的時(shí)候,小于最大線程數(shù) 200,它就開始變成限制項(xiàng)了。

那么 max-connections 這個(gè)參數(shù)到底是干啥的呢?

你先自己去摸索摸索吧。

同時(shí),還有這樣的一個(gè)參數(shù),默認(rèn)是 100:

server.tomcat.accept-count=100

圖片圖片

它又是干什么的呢?

“和連接數(shù)有關(guān)”,我只能提示到這里了,自己去摸索吧。

再等等

通過前面的分析,我們知道了,要回答“一個(gè) SpringBoot 項(xiàng)目默認(rèn)能處理的任務(wù)數(shù)”,這個(gè)問題,得先明確其使用的 web 容器。

那么問題又來了:SpringBoot 內(nèi)置了哪些容器呢?

Tomcat、Jetty、Netty、Undertow

圖片圖片

前面我們都是基于 Tomcat 分析的,如果我們換一個(gè)容器呢?

比如換成 Undertow,這個(gè)玩意我只是聽過,沒有實(shí)際使用過,它對(duì)我來說就是一個(gè)黑盒。

管它的,先換了再說。

從 Tomcat 換成 Undertow,只需要修改 Maven 依賴即可,其他什么都不需要?jiǎng)樱?/p>

圖片圖片

再次啟動(dòng)項(xiàng)目,從日志可以發(fā)現(xiàn)已經(jīng)修改為了 Undertow 容器:

圖片圖片

此時(shí)我再次執(zhí)行 MainTest 方法,還是提交 1000 個(gè)請(qǐng)求:

圖片圖片

從日志來看,發(fā)現(xiàn)只有 48 個(gè)請(qǐng)求被處理了。

就很懵逼,48 是怎么回事兒,怎么都不是一個(gè)整數(shù)呢,這讓強(qiáng)迫癥很難受啊。

這個(gè)時(shí)候你的想法是什么,是不是想要看看 48 這個(gè)數(shù)字到底是從哪里來的?

怎么看?

之前找 Tomcat 的 200 的時(shí)候不是才教了你的嘛,直接往 Undertow 上套就行了嘛。

打線程 Dump,然后看堆棧消息:

圖片圖片

發(fā)現(xiàn) EnhancedQueueExecutor 這個(gè)線程池,接著在這個(gè)類里面去找構(gòu)建線程池時(shí)的參數(shù)。

很容易就找到了這個(gè)構(gòu)造方法:

圖片圖片

所以,在這里打上斷點(diǎn),重啟項(xiàng)目。

通過 Debug 可以知道,關(guān)鍵參數(shù)都是從 builder 里面來的。

而 builder 里面,coreSize 和 maxSize 都是 48,隊(duì)列長(zhǎng)度是 Integer.MAX_VALUE。

圖片圖片

所以看一下 Builder 里面的 coreSize 是怎么來的。

點(diǎn)過來發(fā)現(xiàn) coreSize 的默認(rèn)值是 16:

圖片圖片

不要慌,再打斷點(diǎn),再重啟項(xiàng)目。

然后你會(huì)在它的 setCorePoolSize 方法處停下來,而這個(gè)方法的入?yún)⒕褪俏覀円业?48:

圖片圖片

順藤摸瓜,重復(fù)幾次打斷點(diǎn)、重啟的動(dòng)作之后,你會(huì)找到 48 是一個(gè)名為 WORKER_TASK_CORE_THREADS 的變量,是從這里來的:

圖片圖片

而 WORKER_TASK_CORE_THREADS 這個(gè)變量設(shè)置的地方是這樣的:

io.undertow.Undertow#start

圖片圖片

而這里的 workerThreads 取值是這樣的:

io.undertow.Undertow.Builder#Builder

圖片圖片

取的是機(jī)器的 CPU 個(gè)數(shù)乘以 8。

圖片圖片

所以我這里是 6*8=48。

哦,真相大白,原來 48 是這樣來的。

沒意思。

確實(shí)沒意思,但是既然都已經(jīng)替換為 Undertow 了,那么你去研究一下它的 NIO ByteBuffer、NIO Channel、BufferPool、XNIO Worker、IO 線程池、Worker 線程池...

然后再和 Tomcat 對(duì)比著學(xué),

就開始有點(diǎn)意思了。

最后再等等

這篇文章是基于“一個(gè) SpringBoot 項(xiàng)目能同時(shí)處理多少請(qǐng)求?”這個(gè)面試題出發(fā)的。

但是經(jīng)過我們前面簡(jiǎn)單的分析,你也知道,這個(gè)問題如果在沒有加一些特定的前提條件的情況下,答案是各不一樣的。

比如我再給你舉一個(gè)例子,還是我們的 Demo,只是使用一下 @Async 注解,其他什么都不變:

圖片圖片

再次啟動(dòng)項(xiàng)目,發(fā)起訪問,日志輸出變成了這樣:

圖片圖片

同時(shí)能處理的請(qǐng)求,直接從 Tomcat 的默認(rèn) 200 個(gè)變成了 8 個(gè)?

因?yàn)?@Async 注解對(duì)應(yīng)的線程池,默認(rèn)的核心線程數(shù)是 8。

之前寫過這篇文章《別問了,我真的不喜歡@Async這個(gè)注解!》分析過這個(gè)注解。

所以你看,稍微一變化,答案看起來又不一樣了,同時(shí)這個(gè)請(qǐng)求在內(nèi)部流轉(zhuǎn)的過程也不一樣了,又是一個(gè)可以鋪開談的點(diǎn)。

在面試過程中也是這樣的,不要急于答題,當(dāng)你覺得面試官問題描述的不清楚的地方,你可以先試探性的問一下,看看能不能挖掘出一點(diǎn)他沒有說出來的默認(rèn)條件。

當(dāng)“默認(rèn)條件”挖掘的越多,你的回答就會(huì)更容易被面試官接受。而這個(gè)挖掘的過程,也是面試過程中一個(gè)重要的表現(xiàn)環(huán)節(jié)。

而且,有時(shí)候,面試官就喜歡給出這樣的“模糊”的問題,因?yàn)閱栴}越模糊,坑就越多,當(dāng)面試者跳進(jìn)自己挖好的坑里面的時(shí)候,就是結(jié)束一次交鋒的時(shí)候;當(dāng)面試者看出來自己挖好的坑,并繞過去的時(shí)候,也是結(jié)束一輪交鋒的時(shí)候。

所以,不要急于答題,多想,多問。不管是對(duì)于面試者還是面試官,一個(gè)好的面試體驗(yàn),一定不是沒有互動(dòng)的一問一答,而是一個(gè)相互拉鋸的過程。

責(zé)任編輯:武曉燕 來源: why技術(shù)
相關(guān)推薦

2022-04-08 08:26:03

JavaHTTP請(qǐng)求

2024-05-28 10:14:31

JavaScrip模板引擎

2023-01-18 17:50:35

系統(tǒng)架構(gòu)Kafka

2021-09-28 13:42:55

Chrome Devwebsocket網(wǎng)絡(luò)協(xié)議

2025-10-20 04:00:00

2025-11-11 09:25:19

2021-01-18 05:13:04

TomcatHttp

2021-03-29 08:47:24

線程面試官線程池

2024-03-18 14:06:00

停機(jī)Spring服務(wù)器

2024-05-24 10:36:27

2025-03-17 00:00:00

2021-05-19 08:17:35

秒殺場(chǎng)景高并發(fā)

2020-05-13 14:35:47

HashMap面試官Java

2022-08-18 20:02:04

JSLRU緩存

2022-01-10 11:04:41

單鏈表面試編程

2020-06-22 07:47:46

提交面試官訂單

2017-03-16 15:27:10

面試官測(cè)試技術(shù)

2025-09-19 09:57:46

2024-01-19 14:03:59

Redis緩存系統(tǒng)Spring

2022-04-01 12:38:32

cookie代碼面試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

色婷婷视频在线观看| 欧美激情视频一区二区| 国产免费一区二区三区在线能观看 | 97avcom| 精品亚洲一区二区三区四区| 精品无码av在线| 婷婷精品久久久久久久久久不卡| 久久久精品五月天| 亚洲精品久久久久中文字幕欢迎你 | 亚洲精品第三页| 精品视频二区| 国产精品色网| 亚洲国产精品久久91精品| 国产欧美综合一区| 国产主播第一页| 综合伊思人在钱三区| 国产精品久久久久久妇女6080| 欧美伊久线香蕉线新在线| 中国极品少妇xxxx| 亚洲综合影视| 国产一区二区免费看| 久久激情视频久久| 中文字幕12页| 日韩新的三级电影| 久久精品一区蜜桃臀影院| 欧美亚洲国产成人精品| √天堂中文官网8在线| 四虎国产精品免费久久| 欧美日韩国产一区二区三区| 久久66热这里只有精品| 香蕉免费毛片视频| 久久99国内| 日韩免费一区二区| 韩日视频在线观看| 亚洲欧美一区二区三| 久久久噜噜噜| 欧美精品久久久久久久| 在线观看国产免费视频| 成人免费直播| 亚洲国产高清aⅴ视频| 国产综合福利在线| 久久精品www人人爽人人| 国产一区二区三区不卡av| 亚洲3atv精品一区二区三区| 久久精品国产精品青草色艺 | 日韩精品一区二区三区三区免费| 69久久久久久| 免费大片在线观看www| 国产精品资源站在线| 久久久久久午夜| 老熟妻内射精品一区| 日韩成人精品一区| 欧美成人性福生活免费看| 91视频 -- 69xx| 亚洲搞黄视频| 国产精品99久久久| 欧美性受xxx| 黄色大片网站在线观看| 波多野结衣在线播放一区| 欧美影片第一页| 成年丰满熟妇午夜免费视频| 青青草观看免费视频在线| 久久精品国产秦先生| 久久久免费电影| 国产真人做爰视频免费| 久久爱www.| 欧美日韩亚洲系列| 日韩电影在线一区二区| 亚洲午夜av在线| 精品一区久久| 一级成人免费视频| 欧美日韩三级电影在线| 日韩精品在线免费观看| 奇米视频7777| 免费成人在线电影| 亚洲人成亚洲人成在线观看图片| 久久综合一区| 国产黄色片av| 麻豆精品视频在线| 成人国产精品一区二区| 69国产精品视频免费观看| 欧美一区影院| 中日韩午夜理伦电影免费 | 精品久久久久久最新网址| 亚洲午夜久久久久久久久| 午夜a一级毛片亚洲欧洲| 欧美精品三级日韩久久| 国产成人久久婷婷精品流白浆| 91精选在线| 精品国产91久久久久久老师| 樱空桃在线播放| 动漫一区二区| 亚洲卡通欧美制服中文| 亚洲黄色成人久久久| 四虎精品成人影院观看地址| 国产精品一区三区| 激情视频一区二区| 成年人在线免费观看| 99综合电影在线视频| 91精品久久香蕉国产线看观看| 凹凸精品一区二区三区| 狠狠狠色丁香婷婷综合久久五月| 国产成人精品一区| 亚洲永久精品在线观看| 亚洲国产一区二区三区a毛片| 久久不射热爱视频精品| 黑人狂躁日本娇小| 欧美mv日韩| 色系列之999| 亚洲天堂精品一区| 久久亚洲影视| …久久精品99久久香蕉国产| 亚洲天堂2021av| 99精品久久免费看蜜臀剧情介绍| 国产精品国产一区二区| 国产三级三级在线观看| 国内精品免费**视频| 久久久www免费人成黑人精品| 美女隐私在线观看| 欧美性精品220| 北条麻妃av高潮尖叫在线观看| 男人天堂视频在线观看| 欧美夫妻性生活| 亚洲成人网在线播放| 日韩美女精品| 久久av.com| 中文字幕+乱码+中文字幕明步| 日韩电影在线免费看| 国产日韩一区欧美| 日本黄在线观看| 一区二区三区小说| 自拍日韩亚洲一区在线| 亚洲电影观看| 欧美性大战久久久久久久蜜臀| 在线xxxxx| 午夜欧美视频| 91精品免费视频| 亚洲精品无amm毛片| jvid福利写真一区二区三区| 激情五月五月婷婷| 欧美一级做a| 永久555www成人免费| 日韩欧美综合视频| 亚洲二区视频| 国产成人欧美在线观看| 欧洲综合视频| 欧美性猛交xxxx乱大交| 国产精品揄拍100视频| 久久裸体网站| 国产剧情日韩欧美| 91精彩在线视频| 亚洲一区av在线| 日本泡妞xxxx免费视频软件| 一区二区三区日本久久久| 91国语精品自产拍在线观看性色| 神马久久久久久久久久| 国产精品情趣视频| 青青草精品视频在线| 91精品国产66| 精品国产乱码久久久久久浪潮| 一本加勒比北条麻妃| 午夜在线视频一区二区区别 | 自拍偷拍校园春色| 中文字幕精品三区| 男生操女生视频在线观看 | 日韩在线你懂的| 日本精品一区二区三区在线| 99在线小视频| 亚洲主播在线观看| 一级片视频免费观看| 久久婷婷蜜乳一本欲蜜臀| 国产精品极品美女粉嫩高清在线| 曰批又黄又爽免费视频| 国产精品女同一区二区三区| 国产第一页视频| 国产美女视频一区二区| 日韩欧美综合一区| 成人午夜精品无码区| 亚洲人成777| 亚洲香蕉在线观看| 青青草视频在线观看免费| 中文字幕av一区二区三区高 | 大片免费在线看视频| 一区二区三区四区av| 国产xxx在线观看| 久久精品一区二区三区中文字幕| 任我爽在线视频精品一| 日本国产亚洲| 日韩在线中文字幕| 日本xxxxwww| 欧美国产日韩亚洲一区| 久久亚洲国产成人精品无码区 | 一区二区三区色| 天堂av在线8| 亚洲国产电影| 日韩av一级大片| 精精国产xxxx视频在线| 91精品啪在线观看国产60岁| 亚洲av片不卡无码久久| 午夜激情久久| 欧美尺度大的性做爰视频| 久久精品99北条麻妃| 国产日韩欧美一区二区三区综合| 国产又黄又爽免费视频| 91p九色成人| 美日韩精品视频免费看| 日本午夜在线视频| 欧美一区二区精品| 日本在线视频免费观看| 国产精品区一区二区三区| 韩国三级视频在线观看| 亚洲国产精品一区| 五月天亚洲综合情| 天堂电影一区| 日韩视频免费大全中文字幕| 精品国产乱子伦| 91污片在线观看| 911福利视频| 亚洲欧美日本日韩| 一区二区三区不卡在线| 欧美xnxx| 在线成人中文字幕| 午夜免费福利视频| 亚洲欧美国产毛片在线| aaaa黄色片| 国产成人久久精品77777最新版本| 免费在线观看日韩视频| 在线日本成人| 9999在线观看| 精品国产一区二区三区久久久蜜臀| 国产精品国产三级国产专播精品人 | 国产在线观看一区二区| 国产在线观看福利| 亚洲性视频h| 亚洲综合欧美日韩| 日韩黄色大片网站| 免费在线观看91| 福利欧美精品在线| 91在线网站视频| 自拍视频在线免费观看| 亚洲国产免费av| 国产色在线视频| 91精品国产欧美一区二区18| 中文字幕在线播放av| 色综合色综合色综合色综合色综合| 一区视频免费观看| 一区二区三区毛片| 手机在线免费看片| 国产精品夫妻自拍| 国产福利视频网站| 亚洲欧洲韩国日本视频| 中文字幕 日韩 欧美| 日韩电影网1区2区| 亚洲最大综合网| 日韩黄色在线观看| 91av俱乐部| 久久午夜激情| 亚洲欧美另类动漫| 日本一区中文字幕 | 欧美激情日韩| 91精品国产91久久久久麻豆 主演| 中文字幕午夜精品一区二区三区| 自拍偷拍一区二区三区| 国产一区二区三区黄网站| 91理论片午午论夜理片久久| 日日夜夜亚洲| 亚洲一区二区少妇| 91丨精品丨国产| 日本精品久久中文字幕佐佐木| 伊人色综合一区二区三区影院视频| 2019亚洲男人天堂| 国产私拍福利精品视频二区| 国产成人综合av| 亚洲成人短视频| 欧美成人第一页| 超碰在线中文字幕| 韩剧1988在线观看免费完整版| 大片免费播放在线视频| 最好看的2019年中文视频| а√资源新版在线天堂| 欧美黑人视频一区| 三妻四妾的电影电视剧在线观看| 国产精品久久97| 高清一区二区三区av| 9a蜜桃久久久久久免费| 精品淫伦v久久水蜜桃| 日本一区不卡| 国产精品97| 国产一区二区三区在线免费| 国产日韩亚洲欧美精品| 九九视频精品在线观看| 精品中文字幕一区二区小辣椒| 给我免费播放片在线观看| 亚洲女人av| 一本一道久久a久久综合蜜桃| 国产精品资源网站| 欧美深性狂猛ⅹxxx深喉 | 中文字幕免费视频| 一区二区在线观看不卡| 久久久久亚洲av成人毛片韩| 欧美性色综合网| 中文字幕在线视频免费| 在线91免费看| 亚洲 欧美 激情 另类| 亚洲图中文字幕| 青青青草视频在线| 国产精品日韩精品| 欧美aaaaa性bbbbb小妇| 国产精品久久久久9999| 美女精品久久| 日韩精品久久久毛片一区二区| 中文字幕一区二区av| 免费观看成人网| 国产精品一二三四| 性の欲びの女javhd| 亚洲美女视频一区| 中文字幕码精品视频网站| 精品国产免费一区二区三区四区| 台湾av在线二三区观看| 久久久之久亚州精品露出| 欧美黄色a视频| 国产99久久精品一区二区永久免费 | 91精品国产91综合久久蜜臀| 青青青草原在线| 韩国国内大量揄拍精品视频| 青草综合视频| 欧美一区二区三区在线播放| 一本久久综合| 91九色丨porny丨国产jk| 奇米色777欧美一区二区| 深夜视频在线观看| 亚洲欧美日韩精品久久久久| 亚洲va在线观看| 精品国产污网站| 日本大片在线播放| 成人免费视频a| 成人免费在线观看av| 波多野结衣家庭教师视频| 国产iv一区二区三区| 成熟人妻av无码专区| 一本色道久久综合亚洲精品按摩| 色婷婷中文字幕| 欧美裸体xxxx极品少妇| 色婷婷av在线| 91亚洲人电影| 97精品国产福利一区二区三区| 无需播放器的av| 国产午夜精品理论片a级大结局| 久久久久99精品| 亚洲精品在线免费播放| 中文字幕有码在线观看| 成人国产在线视频| 亚洲人体av| 日韩精品――色哟哟| 亚洲精品成人精品456| 国产成人毛毛毛片| 欧美精品免费看| 香蕉成人app| 人人妻人人澡人人爽欧美一区双 | 丰满少妇在线观看| 日本一区二区视频在线| 特级西西444www高清大视频| 日韩av一卡二卡| 在线成人av观看| 欧美精品一区二区三区在线看午夜| 在线亚洲a色| 成人免费视频久久| 国产偷v国产偷v亚洲高清| 在线观看污污网站| 伊人精品在线观看| 99精品视频在线免费播放| 真人做人试看60分钟免费| 国产美女视频91| 日操夜操天天操| 精品五月天久久| 欧美舌奴丨vk视频| 曰韩不卡视频| 国产成人精品一区二区三区四区 | 久久6免费高清热精品| 一区视频网站| 鲁一鲁一鲁一鲁一色| 国产欧美一区二区精品秋霞影院| 亚洲 小说区 图片区| 久久久精品视频成人| 国产香蕉精品| 老头吃奶性行交视频| 国产精品日韩精品欧美在线| 性做久久久久久久久久| 91精品国产91久久久久久不卡| 国产精品一区二区av日韩在线| 永久免费看av| 成人av先锋影音| 制服丨自拍丨欧美丨动漫丨| 日韩欧美一区二区免费| 国产传媒在线| 日韩精品久久久免费观看| 国产91高潮流白浆在线麻豆| 久久久久99精品成人片三人毛片| 中文字幕av一区二区三区谷原希美 | 亚洲国产美女搞黄色|