在SpringBoot2.7.10版本中內(nèi)置Tomcat版本是9.0.73,SpringBoot內(nèi)置Tomcat的默認設(shè)置如下:
-
Tomcat的連接等待隊列長度,默認是100
-
Tomcat的最大連接數(shù),默認是8192
-
Tomcat的最小工作線程數(shù),默認是10
-
Tomcat的最大線程數(shù),默認是200
-
Tomcat的連接超時時間,默認是20s
相關(guān)配置及默認值如下
server:
??tomcat:
????#?當所有可能的請求處理線程都在使用中時,傳入連接請求的最大隊列長度
????accept-count:?100
????#?服務(wù)器在任何給定時間接受和處理的最大連接數(shù)。一旦達到限制,操作系統(tǒng)仍然可以接受基于“acceptCount”屬性的連接。
????max-connections:?8192
????threads:
??????#?工作線程的最小數(shù)量,初始化時創(chuàng)建的線程數(shù)
??????min-spare:?10
??????#?工作線程的最大數(shù)量?io密集型建議10倍的cpu數(shù),cpu密集型建議cpu數(shù)+1,絕大部分應(yīng)用都是io密集型
??????max:?200
????#?連接器在接受連接后等待顯示請求 URI 行的時間。
????connection-timeout:?20000
????#?在關(guān)閉連接之前等待另一個 HTTP 請求的時間。如果未設(shè)置,則使用 connectionTimeout。設(shè)置為?-1 時不會超時。
????keep-alive-timeout:?20000
????#?在連接關(guān)閉之前可以進行流水線處理的最大HTTP請求數(shù)量。當設(shè)置為0或1時,禁用keep-alive和流水線處理。當設(shè)置為-1時,允許無限數(shù)量的流水線處理或keep-alive請求。?
????max-keep-alive-requests:?100
2架構(gòu)圖
當連接數(shù)大于maxConnections+acceptCount + 1時,新來的請求不會收到服務(wù)器拒絕連接響應(yīng),而是不會和新的請求進行3次握手建立連接,一段時間后(客戶端的超時時間或者Tomcat的20s后)會出現(xiàn)請求連接超時。
3TCP的3次握手4次揮手
4時序圖
5核心參數(shù)
AcceptCount
全連接隊列容量,等同于backlog參數(shù),與Linux中的系統(tǒng)參數(shù)somaxconn取較小值,Windows中沒有系統(tǒng)參數(shù)。
NioEndpoint.java
serverSock?=?ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress?addr?=?new?InetSocketAddress(getAddress(),?getPortWithOffset());
//?這里
serverSock.socket().bind(addr,getAcceptCount());
MaxConnections
Acccptor.java
//?線程的run方法。
public?void?run()?{????
??while?(!stopCalled)?{?
??????//?如果我們已達到最大連接數(shù),等待
?????????connectionLimitLatch.countUpOrAwait();
????????????//?接受來自服務(wù)器套接字的下一個傳入連接
????????????socket?=?endpoint.serverSocketAccept()
????????????//?socket.close?釋放的時候?調(diào)用?connectionLimitLatch.countDown();???????
MinSpareThread/MaxThread
AbstractEndpoint.java
//?tomcat?啟動時
public?void?createExecutor()?{
????????internalExecutor?=?true;
?????//?容量為Integer.MAX_VALUE
????????TaskQueue?taskqueue?=?new?TaskQueue();
????????TaskThreadFactory?tf?=?new?TaskThreadFactory(getName()?+?"-exec-",?daemon,?getThreadPriority());
?????//?Tomcat擴展的線程池
????????executor?=?new?ThreadPoolExecutor(getMinSpareThreads(),?getMaxThreads(),?60,?TimeUnit.SECONDS,taskqueue,?tf);
????????taskqueue.setParent(?(ThreadPoolExecutor)?executor);
}
重點重點重點
Tomcat擴展了線程池增強了功能。
-
JDK線程池流程:
minThreads --> queue --> maxThreads --> Exception
-
Tomcat增強后:?
minThreads --> maxThreads --> queue --> Exception
MaxKeepAliveRequests
長連接,在發(fā)送了maxKeepAliveRequests
個請求后就會被服務(wù)器端主動斷開連接。
在連接關(guān)閉之前可以進行流水線處理的最大HTTP請求數(shù)量。當設(shè)置為0或1時,禁用keep-alive和流水線處理。當設(shè)置為-1時,允許無限數(shù)量的流水線處理或keep-alive請求。
較大的?MaxKeepAliveRequests
?值可能會導(dǎo)致服務(wù)器上的連接資源被長時間占用。根據(jù)您的具體需求,您可以根據(jù)服務(wù)器的負載和資源配置來調(diào)整?MaxKeepAliveRequests
?的值,以平衡并發(fā)連接和服務(wù)器資源的利用率。
NioEndpoint.setSocketOptions?
?socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
Http11Processor.service(SocketWrapperBase<?>?socketWrapper)
??keepAlive?=?true;
??while(!getErrorState().isError()?&&?keepAlive?&&?!isAsync()?&&?upgradeToken?==?null?&&
????????????????sendfileState?==?SendfileState.DONE?&&?!protocol.isPaused())?{
????//?默認100??
?int?maxKeepAliveRequests?=?protocol.getMaxKeepAliveRequests();
?if?(maxKeepAliveRequests?==?1)?{
?????keepAlive?=?false;
?}?else?if?(maxKeepAliveRequests?>?0?&&
????????????//????
?????????socketWrapper.decrementKeepAlive()?<=?0)?{
?????keepAlive?=?false;
?}
ConnectionTimeout
連接的生存周期,當已經(jīng)建立的連接,在connectionTimeout
時間內(nèi),如果沒有請求到來,服務(wù)端程序?qū)鲃雨P(guān)閉該連接。
-
在Tomcat 9中,
ConnectionTimeout
的默認值是20000毫秒,也就是20秒。 -
如果該時間過長,服務(wù)器將要等待很長時間才會收到客戶端的請求結(jié)果,從而導(dǎo)致服務(wù)效率低下。如果該時間過短,則可能會出現(xiàn)客戶端在請求過程中網(wǎng)絡(luò)慢等問題,而被服務(wù)器取消連接的情況。
-
由于某個交換機或者路由器出現(xiàn)了問題,導(dǎo)致某些post大文件的請求堆積在交換機或者路由器上,tomcat的工作線程一直拿不到完整的文件數(shù)據(jù)。
NioEndpoint.Poller#run()
?//?Check?for?read?timeout
?if?((socketWrapper.interestOps()?&?SelectionKey.OP_READ)?==?SelectionKey.OP_READ)?{
?????long?delta?=?now?-?socketWrapper.getLastRead();
?????long?timeout?=?socketWrapper.getReadTimeout();
?????if?(timeout?>?0?&&?delta?>?timeout)?{
?????????readTimeout?=?true;
?????}
?}
?//?Check?for?write?timeout
?if?(!readTimeout?&&?(socketWrapper.interestOps()?&?SelectionKey.OP_WRITE)?==?SelectionKey.OP_WRITE)?{
?????long?delta?=?now?-?socketWrapper.getLastWrite();
?????long?timeout?=?socketWrapper.getWriteTimeout();
?????if?(timeout?>?0?&&?delta?>?timeout)?{
?????????writeTimeout?=?true;
?????}
?}
KeepAliveTimeout
等待另一個 HTTP 請求的時間,然后關(guān)閉連接。當未設(shè)置時,將使用?connectionTimeout
。當設(shè)置為 -1 時,將沒有超時。
Http11InputBuffer.parseRequestLine
//?Read?new?bytes?if?needed
if?(byteBuffer.position()?>=?byteBuffer.limit())?{
????if?(keptAlive)?{
????????//?還沒有讀取任何請求數(shù)據(jù),所以使用保持活動超時
????????wrapper.setReadTimeout(keepAliveTimeout);
????}
????if?(!fill(false))?{
????????//?A?read?is?pending,?so?no?longer?in?initial?state
????????parsingRequestLinePhase?=?1;
????????return?false;
????}
????//??至少已收到請求的一個字節(jié)?切換到套接字超時。
?????wrapper.setReadTimeout(connectionTimeout);
}
6內(nèi)部線程
Acceptor
Acceptor:接收器,作用是接受scoket網(wǎng)絡(luò)請求,并調(diào)用setSocketOptions()
封裝成為NioSocketWrapper
,并注冊到Poller的events中。注意查看run方法org.apache.tomcat.util.net.Acceptor#run
public?void?run()?{
???while?(!stopCalled)?{
???????//?等待下一個請求進來
???????socket?=?endpoint.serverSocketAccept();
????????//?注冊socket到Poller,生成PollerEvent事件
???????endpoint.setSocketOptions(socket);
??????????//?向輪詢器注冊新創(chuàng)建的套接字
????????????????-?poller.register(socketWrapper);
????????????????????-?(SynchronizedQueue(128))events.add(new?PollerEvent(socketWrapper))??
Poller
Poller:輪詢器,輪詢是否有事件達到,有請求事件到達后,以NIO的處理方式,查詢Selector取出所有請求,遍歷每個請求的需求,分配給Executor線程池執(zhí)行。查看org.apache.tomcat.util.net.NioEndpoint.Poller#run()
public?void?run()?{
???while?(true)?{
???????????//查詢selector取出所有請求事件
???????????Iterator<SelectionKey>?iterator?=
???????????????keyCount?>?0???selector.selectedKeys().iterator()?:?null;
???????????//?遍歷就緒鍵的集合并調(diào)度任何活動事件。
???????????while?(iterator?!=?null?&&?iterator.hasNext())?{
???????????????SelectionKey?sk?=?iterator.next();
???????????????iterator.remove();
???????????????NioSocketWrapper?socketWrapper?=?(NioSocketWrapper)?sk.attachment();
???????????????//?分配給Executor線程池執(zhí)行處理請求key
???????????????if?(socketWrapper?!=?null)?{
???????????????????processKey(sk,?socketWrapper);
???????????????????-?processSocket(socketWrapper,?SocketEvent.OPEN_READ/SocketEvent.OPEN_WRITE)
???????????????????????-?executor.execute((Runnable)new?SocketProcessor(socketWrapper,SocketEvent))
???????????????}
???????????}
TomcatThreadPoolExecutor
真正執(zhí)行連接讀寫操作的線程池,在JDK線程池的基礎(chǔ)上進行了擴展優(yōu)化。
AbstractEndpoint.java
public?void?createExecutor()?{
????internalExecutor?=?true;
????TaskQueue?taskqueue?=?new?TaskQueue();
????TaskThreadFactory?tf?=?new?TaskThreadFactory(getName()?+?"-exec-",?daemon,?getThreadPriority());
?//?tomcat自定義線程池
????executor?=?new?ThreadPoolExecutor(getMinSpareThreads(),?getMaxThreads(),?60,?TimeUnit.SECONDS,taskqueue,?tf);
????taskqueue.setParent(?(ThreadPoolExecutor)?executor);
}
TomcatThreadPoolExecutor.java
//?與 java.util.concurrent.ThreadPoolExecutor 相同,但實現(xiàn)了更高效的getSubmittedCount()方法,用于正確處理工作隊列。
//?如果未指定?RejectedExecutionHandler,將配置一個默認的,并且該處理程序?qū)⑹冀K拋出?RejectedExecutionException
public?class?ThreadPoolExecutor?extends?java.util.concurrent.ThreadPoolExecutor?{
?//?已提交但尚未完成的任務(wù)數(shù)。這包括隊列中的任務(wù)和已交給工作線程但后者尚未開始執(zhí)行任務(wù)的任務(wù)。
????//?這個數(shù)字總是大于或等于getActiveCount()?。
????private?final?AtomicInteger?submittedCount?=?new?AtomicInteger(0);
????
????@Override
????protected?void?afterExecute(Runnable?r,?Throwable?t)?{
????????if?(!(t?instanceof?StopPooledThreadException))?{
????????????submittedCount.decrementAndGet();
????????}
????@Override
????public?void?execute(Runnable?command){
????????//?提交任務(wù)的數(shù)量+1
????????submittedCount.incrementAndGet();
????????try?{
????????????//??線程池內(nèi)部方法,真正執(zhí)行的方法。就是JDK線程池原生的方法。
????????????super.execute(command);
????????}?catch?(RejectedExecutionException?rx)?{
????????????//?再次把被拒絕的任務(wù)放入到隊列中。
????????????if?(super.getQueue()?instanceof?TaskQueue)?{
????????????????final?TaskQueue?queue?=?(TaskQueue)super.getQueue();
????????????????try?{
??????????????????????//強制的將任務(wù)放入到阻塞隊列中
????????????????????if?(!queue.force(command,?timeout,?unit))?{
????????????????????????//放入失敗,則繼續(xù)拋出異常
????????????????????????submittedCount.decrementAndGet();
????????????????????????throw?new?RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));
????????????????????}
????????????????}?catch?(InterruptedException?x)?{
?????????????????????//被中斷也拋出異常
????????????????????submittedCount.decrementAndGet();
????????????????????throw?new?RejectedExecutionException(x);
????????????????}
????????????}?else?{
?????????????????//不是這種隊列,那么當任務(wù)滿了之后,直接拋出去。
????????????????submittedCount.decrementAndGet();
????????????????throw?rx;
????????????}
????????}
????}
/**
?*?實現(xiàn)Tomcat特有邏輯的自定義隊列
?*/
public?class?TaskQueue?extends?LinkedBlockingQueue<Runnable>?{
????private?static?final?long?serialVersionUID?=?1L;
????private?transient?volatile?ThreadPoolExecutor?parent?=?null;
????private?static?final?int?DEFAULT_FORCED_REMAINING_CAPACITY?=?-1;
????/**
?????*?強制遺留的容量
?????*/
????private?int?forcedRemainingCapacity?=?-1;
????/**
?????*?隊列的構(gòu)建方法
?????*/
????public?TaskQueue()?{
????}
????public?TaskQueue(int?capacity)?{
????????super(capacity);
????}
????public?TaskQueue(Collection<??extends?Runnable>?c)?{
????????super(c);
????}
????/**
?????*?設(shè)置核心變量
?????*/
????public?void?setParent(ThreadPoolExecutor?parent)?{
????????this.parent?=?parent;
????}
????/**
?????* put:向阻塞隊列填充元素,當阻塞隊列滿了之后,put時會被阻塞。
?????* offer:向阻塞隊列填充元素,當阻塞隊列滿了之后,offer會返回false。
?????*
?????*?@param?o?當任務(wù)被拒絕后,繼續(xù)強制的放入到線程池中
?????*?@return?向阻塞隊列塞任務(wù),當阻塞隊列滿了之后,offer會返回false。
?????*/
????public?boolean?force(Runnable?o)?{
????????if?(parent?==?null?||?parent.isShutdown())?{
????????????throw?new?RejectedExecutionException("taskQueue.notRunning");
????????}
????????return?super.offer(o);
????}
????/**
?????*?帶有阻塞時間的塞任務(wù)
?????*/
????@Deprecated
????public?boolean?force(Runnable?o,?long?timeout,?TimeUnit?unit)?throws?InterruptedException?{
????????if?(parent?==?null?||?parent.isShutdown())?{
????????????throw?new?RejectedExecutionException("taskQueue.notRunning");
????????}
????????return?super.offer(o,?timeout,?unit);?//forces?the?item?onto?the?queue,?to?be?used?if?the?task?is?rejected
????}
????/**
?????*?當線程真正不夠用時,優(yōu)先是開啟線程(直至最大線程),其次才是向隊列填充任務(wù)。
?????*
?????*?@param?runnable?任務(wù)
?????*?@return?false?表示向隊列中添加任務(wù)失敗,
?????*/
????@Override
????public?boolean?offer(Runnable?runnable)?{
????????if?(parent?==?null)?{
????????????return?super.offer(runnable);
????????}
????????//若是達到最大線程數(shù),進隊列。
????????if?(parent.getPoolSize()?==?parent.getMaximumPoolSize())?{
????????????return?super.offer(runnable);
????????}
????????//當前活躍線程為10個,但是只有8個任務(wù)在執(zhí)行,于是,直接進隊列。
????????if?(parent.getSubmittedCount()?<?(parent.getPoolSize()))?{
????????????return?super.offer(runnable);
????????}
????????//當前線程數(shù)小于最大線程數(shù),那么直接返回false,去創(chuàng)建最大線程
????????if?(parent.getPoolSize()?<?parent.getMaximumPoolSize())?{
????????????return?false;
????????}
????????//否則的話,將任務(wù)放入到隊列中
????????return?super.offer(runnable);
????}
????/**
?????*?獲取任務(wù)
?????*/
????@Override
????public?Runnable?poll(long?timeout,?TimeUnit?unit)?throws?InterruptedException?{
????????Runnable?runnable?=?super.poll(timeout,?unit);
????????//取任務(wù)超時,會停止當前線程,來避免內(nèi)存泄露
????????if?(runnable?==?null?&&?parent?!=?null)?{
????????????parent.stopCurrentThreadIfNeeded();
????????}
????????return?runnable;
????}
????/**
?????*?阻塞式的獲取任務(wù),可能返回null。
?????*/
????@Override
????public?Runnable?take()?throws?InterruptedException?{
????????//當前線程應(yīng)當被終止的情況下:
????????if?(parent?!=?null?&&?parent.currentThreadShouldBeStopped())?{
????????????long?keepAliveTime?=?parent.getKeepAliveTime(TimeUnit.MILLISECONDS);
????????????return?poll(keepAliveTime,?TimeUnit.MILLISECONDS);
????????}
????????return?super.take();
????}
????/**
?????*?返回隊列的剩余容量
?????*/
????@Override
????public?int?remainingCapacity()?{
????????if?(forcedRemainingCapacity?>?DEFAULT_FORCED_REMAINING_CAPACITY)?{
????????????return?forcedRemainingCapacity;
????????}
????????return?super.remainingCapacity();
????}
????/**
?????*?強制設(shè)置剩余容量
?????*/
????public?void?setForcedRemainingCapacity(int?forcedRemainingCapacity)?{
????????this.forcedRemainingCapacity?=?forcedRemainingCapacity;
????}
????/**
?????*?重置剩余容量
?????*/
????void?resetForcedRemainingCapacity()?{
????????this.forcedRemainingCapacity?=?DEFAULT_FORCED_REMAINING_CAPACITY;
????}
}?
/**
?*?實現(xiàn)Tomcat特有邏輯的自定義隊列
?*/
public?class?TaskQueue?extends?LinkedBlockingQueue<Runnable>?{
????private?static?final?long?serialVersionUID?=?1L;
????private?transient?volatile?ThreadPoolExecutor?parent?=?null;
????private?static?final?int?DEFAULT_FORCED_REMAINING_CAPACITY?=?-1;
????/**
?????*?強制遺留的容量
?????*/
????private?int?forcedRemainingCapacity?=?-1;
????/**
?????*?隊列的構(gòu)建方法
?????*/
????public?TaskQueue()?{
????}
????public?TaskQueue(int?capacity)?{
????????super(capacity);
????}
????public?TaskQueue(Collection<??extends?Runnable>?c)?{
????????super(c);
????}
????/**
?????*?設(shè)置核心變量
?????*/
????public?void?setParent(ThreadPoolExecutor?parent)?{
????????this.parent?=?parent;
????}
????/**
?????* put:向阻塞隊列填充元素,當阻塞隊列滿了之后,put時會被阻塞。
?????* offer:向阻塞隊列填充元素,當阻塞隊列滿了之后,offer會返回false。
?????*
?????*?@param?o?當任務(wù)被拒絕后,繼續(xù)強制的放入到線程池中
?????*?@return?向阻塞隊列塞任務(wù),當阻塞隊列滿了之后,offer會返回false。
?????*/
????public?boolean?force(Runnable?o)?{
????????if?(parent?==?null?||?parent.isShutdown())?{
????????????throw?new?RejectedExecutionException("taskQueue.notRunning");
????????}
????????return?super.offer(o);
????}
????/**
?????*?帶有阻塞時間的塞任務(wù)
?????*/
????@Deprecated
????public?boolean?force(Runnable?o,?long?timeout,?TimeUnit?unit)?throws?InterruptedException?{
????????if?(parent?==?null?||?parent.isShutdown())?{
????????????throw?new?RejectedExecutionException("taskQueue.notRunning");
????????}
????????return?super.offer(o,?timeout,?unit);?//forces?the?item?onto?the?queue,?to?be?used?if?the?task?is?rejected
????}
????/**
?????*?當線程真正不夠用時,優(yōu)先是開啟線程(直至最大線程),其次才是向隊列填充任務(wù)。
?????*
?????*?@param?runnable?任務(wù)
?????*?@return?false?表示向隊列中添加任務(wù)失敗,
?????*/
????@Override
????public?boolean?offer(Runnable?runnable)?{
????????if?(parent?==?null)?{
????????????return?super.offer(runnable);
????????}
????????//若是達到最大線程數(shù),進隊列。
????????if?(parent.getPoolSize()?==?parent.getMaximumPoolSize())?{
????????????return?super.offer(runnable);
????????}
????????//當前活躍線程為10個,但是只有8個任務(wù)在執(zhí)行,于是,直接進隊列。
????????if?(parent.getSubmittedCount()?<?(parent.getPoolSize()))?{
????????????return?super.offer(runnable);
????????}
????????//當前線程數(shù)小于最大線程數(shù),那么直接返回false,去創(chuàng)建最大線程
????????if?(parent.getPoolSize()?<?parent.getMaximumPoolSize())?{
????????????return?false;
????????}
????????//否則的話,將任務(wù)放入到隊列中
????????return?super.offer(runnable);
????}
????/**
?????*?獲取任務(wù)
?????*/
????@Override
????public?Runnable?poll(long?timeout,?TimeUnit?unit)?throws?InterruptedException?{
????????Runnable?runnable?=?super.poll(timeout,?unit);
????????//取任務(wù)超時,會停止當前線程,來避免內(nèi)存泄露
????????if?(runnable?==?null?&&?parent?!=?null)?{
????????????parent.stopCurrentThreadIfNeeded();
????????}
????????return?runnable;
????}
????/**
?????*?阻塞式的獲取任務(wù),可能返回null。
?????*/
????@Override
????public?Runnable?take()?throws?InterruptedException?{
????????//當前線程應(yīng)當被終止的情況下:
????????if?(parent?!=?null?&&?parent.currentThreadShouldBeStopped())?{
????????????long?keepAliveTime?=?parent.getKeepAliveTime(TimeUnit.MILLISECONDS);
????????????return?poll(keepAliveTime,?TimeUnit.MILLISECONDS);
????????}
????????return?super.take();
????}
????/**
?????*?返回隊列的剩余容量
?????*/
????@Override
????public?int?remainingCapacity()?{
????????if?(forcedRemainingCapacity?>?DEFAULT_FORCED_REMAINING_CAPACITY)?{
????????????return?forcedRemainingCapacity;
????????}
????????return?super.remainingCapacity();
????}
????/**
?????*?強制設(shè)置剩余容量
?????*/
????public?void?setForcedRemainingCapacity(int?forcedRemainingCapacity)?{
????????this.forcedRemainingCapacity?=?forcedRemainingCapacity;
????}
????/**
?????*?重置剩余容量
?????*/
????void?resetForcedRemainingCapacity()?{
????????this.forcedRemainingCapacity?=?DEFAULT_FORCED_REMAINING_CAPACITY;
????}
}?
JDK線程池架構(gòu)圖
Tomcat線程架構(gòu)
7測試
如下配置舉例
server:
??port:?8080
??tomcat:
????accept-count:?3
????max-connections:?6
????threads:
??????min-spare:?2
??????max:?3
使用ss -nlt
查看全連接隊列容量。
ss?-nltp
ss?-nlt|grep?8080
-?Recv-Q表示(acceptCount)全連接隊列目前長度
-?Send-Q表示(acceptCount)全連接隊列的容量。
靜默狀態(tài)
6個并發(fā)連接
結(jié)果同上
9個并發(fā)連接
10個并發(fā)連接
11個并發(fā)連接
結(jié)果同上
使用ss -nt
查看連接狀態(tài)。
ss?-ntp
ss?-nt|grep?8080
-?Recv-Q表示客戶端有多少個字節(jié)發(fā)送但還沒有被服務(wù)端接收
-?Send-Q就表示為有多少個字節(jié)未被客戶端接收。
靜默狀態(tài)
6個并發(fā)連接
9個并發(fā)連接
補充個netstat
10個并發(fā)連接
結(jié)果同上,隊列中多加了個
11個并發(fā)連接
超出連接后,會有個連接一直停留在SYN_RECV狀態(tài),不會完成3次握手了。
超出連接后客戶端一直就停留在SYN-SENT狀態(tài),服務(wù)端不會再發(fā)送SYN+ACK,直到客戶端超時(20s內(nèi)核控制)斷開。
客戶端請求超時(需要等待一定時間(20s))。
這里如果客戶端設(shè)置了超時時間,要和服務(wù)端3次握手超時時間對比小的為準。
12個并發(fā)連接
最后說一句(求關(guān)注!別白嫖!)
如果這篇文章對您有所幫助,或者有所啟發(fā)的話,求一鍵三連:點贊、轉(zhuǎn)發(fā)、在看。文章來源:http://www.zghlxwxcb.cn/news/detail-854856.html
關(guān)注公眾號:woniuxgg,在公眾號中回復(fù):筆記??就可以獲得蝸牛為你精心準備的java實戰(zhàn)語雀筆記,回復(fù)面試、開發(fā)手冊、有超贊的粉絲福利!文章來源地址http://www.zghlxwxcb.cn/news/detail-854856.html
到了這里,關(guān)于SpringBoot 最大連接數(shù)及最大并發(fā)數(shù)是多少?圖解就看到了!的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!