1、參數(shù)設(shè)置
Springboot集成的tomcat默認(rèn)采從的是NIO(非阻塞隊(duì)列)方式創(chuàng)建http鏈接,NIO相比普通BIO(阻塞隊(duì)列)來說,參數(shù)設(shè)置上有很大不同:
NIO方式下并發(fā)訪問取決于maxConnections參數(shù),BIO模式下取決于maxThreads(一個鏈接對應(yīng)一個線程)
關(guān)鍵參數(shù)說明
NIO下默認(rèn)設(shè)置值:
maxConnections=10000
maxKeepAliveRequests=100 //支持的長鏈接個數(shù),1表示禁用長鏈接,-1表示不限制個數(shù)
keepAliveTimeout=20000ms //長鏈接持續(xù)時(shí)長,表示這個鏈接在執(zhí)行完一個請求后,可以繼續(xù)存活20s, 如果這個參數(shù)不設(shè)置,就取connecttimeout做為長鏈接支持時(shí)長,
這個值引發(fā)的安全問題,比如這個值設(shè)置為20s, 惡意用戶可以用多臺電腦向該服務(wù)器發(fā)請求,每18s(小于20s就可以)發(fā)一次,導(dǎo)致這鏈接一直都被占用,從而無法釋放,耗盡鏈接資源。
目前測試瀏覽器與esm微服務(wù) 訪問是存在這樣問題的。
(在開發(fā)環(huán)境很好模擬,把maxConnections設(shè)置為2,只要兩個用戶在不同電腦上用瀏覽器訪問,間隔刷新,會發(fā)現(xiàn)第三個用戶訪問時(shí)訪問不了)
Tips:將附件中 GetSysInfoServlet.java放入到springboot項(xiàng)目的任意目錄下,然后訪問http://xxx/上下文/monitor/thread 動態(tài)監(jiān)控所有web容器中的線程。
例子: http://api.uds.zte.com.cn/zte-sec-terminal-base/monitor/thread
對于長鏈接的監(jiān)控,用netstat |findstr ip命令 (或采用wireshark可以抓包分析更多明細(xì)),會發(fā)現(xiàn)TCP鏈接的端口號未變化,即表示采用的是長鏈接,目前我們微服務(wù)因?yàn)橥ㄟ^ng/haproxy代理,實(shí)際上長鏈接是建立在ng/haproxy與客戶端之間,這時(shí)候keepAliveTimeout的時(shí)長其實(shí)就取決于haproxy的鏈接超時(shí)實(shí)際,就是一分鐘,而不是我們tomcat里面的設(shè)置。
2、長鏈接訪問
通過wireshark抓包我們發(fā)現(xiàn)瀏覽器都能復(fù)用tcp鏈接,說明長鏈接是生效的,但在httpclient調(diào)用過程中,抓包發(fā)現(xiàn)tcp鏈接其實(shí)是沒有復(fù)用的,可以采用如下方式做到鏈接的復(fù)用,減少創(chuàng)建鏈接的損耗:
public class HttpClientUtil
{
static PoolingHttpClientConnectionManager cm = null;
static
{
cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(500);
cm.setDefaultMaxPerRoute(500);
SocketConfig socketConfig = SocketConfig.custom()
.setSoKeepAlive(true)
.build();
cm.setDefaultSocketConfig(socketConfig);
}
public static CloseableHttpClient getClient()
{
CloseableHttpClient httpClient=HttpClients.custom().setConnectionManager(cm).build();
return httpClient;
}
}
在開發(fā)環(huán)境中做壓力測試,這個性能提升非常明顯,但是在生成環(huán)境,建立的鏈路比較多,只能確保在用戶請求到ng/haproxy這個鏈路是長鏈接,而ng/haproxy到微服務(wù)這一層我們就沒法控制了,而對于公用資源haproxy,如果使用長鏈接過多,有鏈接耗盡的風(fēng)險(xiǎn),但微服務(wù)之間的調(diào)用,如果用長鏈接方式這個風(fēng)險(xiǎn)應(yīng)該不存在。