連接池要這么配,干貨收藏!
哈嘍,大家好,最近周邊的同事,鄰居逐漸都羊了。
而指北君還在堅挺碼字。等待陽的到來。
相信羊過之后,必是一片彩虹!
如果很多資源的使用如果不從共享資源池中獲取,極容易造成內存泄漏和內存溢出。要想實現高并發并且合理利用資源,大部分設計方案都會用到各種連接池,線程池等等。所有的可重復利用資源均從一組資源池中進行調用。也類似于近幾年火爆的共享經濟,然而共享經濟就和軟件設計中的共享資源池類似。不單獨持有某個資源,在需要使用的時候再去資源池中進行申請。
下面我們盤一盤各種資源共享池的一些配置,以及優化策略!
1、Tomcat中的各種connection
廢話少說,我們看一下一個簡單的SpringBoot tomcat配置
HTTP Connector
其工作流程如下:
- 每個非異步請求都需要一個線程來處理,如果并發請求大于當前可處理的線程數量,則會創建額外的線程來處理,至多創建到maxThreads 的數量。
- 此時仍然接收到更多的并發請求,Tomcat會接受新的connection,直到connection數到達最大數maxConnections。此時這些connection會在Connector中創建的 server socket中排隊,直到有線程可以來處理這些connection。
- 一旦上面的排隊數量達到maxC onnections,然后還有新的請求進來,那么新進來的connection會在OS中排隊,操作系統提供的排隊數量為acceptCount。如果這個隊列滿了的話,后面進來的請求有可能被拒絕或者超時timeout
關于這個咱們講一個食堂干飯的例子:
- 某學校有一個食堂,大廳里面日常至少擺100把椅子(min-spare)供學生們吃飯。
- 然而當同時吃飯的同學大于100人的時候,食堂會增加一些椅子(創建線程),并且這些椅子也不會立馬收回去,一段時間沒有人使用才會收回。
- 但是食堂里面最多可以擺500把椅子(maxThreads)。然后超過500人吃飯同時吃飯的話,其他人就只能在大廳里面排隊等別人吃完。食堂大廳里面可以容納1000人進行排隊等候(maxConnections)。
- 當食堂大廳1000人都排滿了,那么就只能到食堂外面排隊了,外面排隊最多一直能排200人(acceptCount)。這個時候如果再有人過來要吃飯,而且還排不上隊,就會等到不耐煩(time out),也會有人來告訴后來的同學,別來了人都滿了,上其他地方吃飯去吧。(reject)
通過上面的例子,我相信大家都能清楚tomcat的一些基本參數配置作用,并且針對不同的情況進行調優了。
2 ThreadPool
關于Java線程池,大家都比較熟悉了吧。下面是基本參數
線程池基本運行原理介紹
- 提交任務給線程池后,線程池會檢查線程池中正在運行的線程數量,如果線程數量小于核心線程,則創建一個新的線程來處理任務。
- 如果線程池中線程數量達到和corePoolSize的大小,則將線程放入等待隊列BlockingQueue中。
- 如果提交任務時連等待隊列都已經滿了的話,線程池會繼續創建新的線程來處理任務,直到線程池數量達到maximumPoolSize。
- 如果線程數量達到了最大容量,則會執行拒絕策略。
這里線程池的方案和tomcat Connector 的方案稍微有點不同。前者是先排隊然后再把池子容量擴大代最大,后者是先擴大池子,然后再排2個隊。
我覺得對于ThreadPoolExecutor線程池的理解,用工廠工人的例子比較好理解。
- 有一家工廠建立,開始的時候只有10個工人,然后工廠的活越來越多,招聘新的工人肯定不是最好的策略,所以多出來的活暫時只能等著,進行排隊。(這個例子中工廠的活多了,立馬去招人肯定是不可能,只能先排單)
- 后面工廠的業務越來越多,任務擠壓過多,原來的工人干活已經不能滿足業務需求了。為了最大化效益,招聘新的工人勢在必行,于是就招聘了新的工人,所有的工人一起來干活,加快效率。
- 當工廠的工人數量達到飽和之后,仍然不停的新增業務,此時工廠已經飽和,沒有辦法再繼續接單。那么只能采取別的方案(拒絕策略),找別的工廠干,或者新建工廠。
- 當后面業務量比較小的時候,新招的工人就會慢慢的裁剪(線程一段時間不使用就會關掉!)。
對線程池的優化思路:
- 如果線程需要執行的任務耗時比較少,是High CPU類型,則核心線程數量可以根據CPU的核數來進行設置。最大線程數量也不應該設置的太大。線程隊列可以根據使用場景設置大一點,提高線程池效率。
- 如果線程需要執行的任務耗時比較長,是High IO型,依賴其他系統,CPU需要等待的時間比較長,則核心線程數可以大一點,相應的線程隊列長度也應該針對不同的使用場景進行調整。
- 線程數量也不宜設置過大,不然會導致頻繁的GC。
3、RestTemplate的坑與優化
SpringBoot微服務與其他Restful的資源進行交互的時候會使用到RestTemplate。如果你直接new RestTemplate,那么就需要特別注意了。使用不慎就會造成內存泄漏,引發GC等。
RestTemplate底層依舊是使用org.apache.http包下的HttpClient。
SpringBoot中可以通過PoolingHttpClientConnectionManager設置一些connection pool 的參數
通過HttpRequestFactory可以設置connectTimeOut,connectionRequestTimeout,SocketTimeout
小結一下比較重要的幾個參數如下:
maxTotal : 連接池里面的最大連接數
defaultMaxPerRoute : 每個路由默認接收的最大連接數
socketTimeout :它是指客戶端和服務器建立連接后,客戶端從服務器讀取數據的超時時間,超出后會拋出SocketTimeOutException。
connectionRequestTimout:指從連接池獲取連接的timeout
connetionTimeout:指客戶端和服務器建立連接的timeout。
可以通過如下方式構建RestTemplate,其中的參數也可以自定以從配置文件中引入。
對于RestTemplate的一些建議
- 應該從資源池中獲取RestTemplate(PoolingHttpClientConnectionManager)
- 使用RestTemplateBuilder來創建RestTemplate
- 針對maxTotal ,defaultMaxPerRoute ,可以增大maxTotal以增大并發量,同時也需要調整每個路由的最大并發連接數,此時也可以提高某條路由的并發量。
- connectionRequestTimeout和connectTimeout設置不要太長,socketTimeout根據需求可以設置相應的時間。
當然還有其他的一些優化的地方,比如使用不同的ConnectionKeepAliveStrategy等,設置maxIdleTime最大空閑時間等。
總結
本篇總結了Tomcat,線程池,RestTemplate 的一些日常優化策略。平時應該多注意總結,在不同的情況下,優化參數均有不同。所以就要多一些測試,才能得到最好的配置。看完這些不妨在項目中試一下,增強記憶。