這個(gè)問(wèn)題之前在項(xiàng)目中就困擾了我很多次,只不過(guò)重新部署就好了而且再也沒(méi)有出現(xiàn)過(guò)了,所以也就沒(méi)注意。但是今天這個(gè)問(wèn)題一直出現(xiàn),重新部署運(yùn)行一會(huì)兒就出現(xiàn),運(yùn)行一會(huì)就出現(xiàn)。不知道怎么弄,沒(méi)辦法就硬著頭皮上網(wǎng)搜,花了一天時(shí)間,也算是把這奇葩的BUG解決了,這里記錄一下解決的幾個(gè)過(guò)程。
首先講一下場(chǎng)景,項(xiàng)目是一個(gè)物聯(lián)網(wǎng)項(xiàng)目,會(huì)有硬件向服務(wù)器上報(bào)信息,硬件數(shù)量多,上傳頻率高,上來(lái)的數(shù)據(jù)需要做處理,在這種高并發(fā)的環(huán)境下,我先是盡可能不去數(shù)據(jù)庫(kù)取數(shù)據(jù),但是無(wú)可避免的還是會(huì)需要訪問(wèn)到數(shù)據(jù)庫(kù),退無(wú)可退了,只能開(kāi)始找原因了。
故障的意思很簡(jiǎn)單直接,就是說(shuō)數(shù)據(jù)庫(kù)的連接池空了,在超時(shí)時(shí)限內(nèi)沒(méi)有獲取到連接,則拋出異常,于是去數(shù)據(jù)庫(kù)查看連接
Mysql -> show full processlist;
通過(guò)這句話在MySQL中去獲取所有進(jìn)程,看看自己這個(gè)項(xiàng)目的進(jìn)程,確實(shí)有很多,而且在Command這一欄還都是sleep,想來(lái)可能跟這個(gè)有關(guān)系,于是乎上網(wǎng)搜相關(guān)內(nèi)容,搜來(lái)的大致意思是連接沒(méi)有正確關(guān)閉。不過(guò)我代碼里面用到的SQL是關(guān)閉了的,雖然體感是這個(gè)問(wèn)題,但是從這入手應(yīng)該是解決不了問(wèn)題。
但是關(guān)于這個(gè)sleep的問(wèn)題,又引申到了MySQL的wait_timeout和interactive_timeout的這兩個(gè)屬性,這兩個(gè)都是空閑連接的超時(shí)時(shí)間,只不過(guò)前者是非交互式連接的(通過(guò)jdbc連接),后者是交互式連接的(通過(guò)MySQL客戶端連接)。于是嘗試設(shè)置一下wait_timeout的時(shí)間,這里默認(rèn)設(shè)置的是8h
Mysql -> set global wait_timeout=30;
設(shè)置成30s之后,重新部署,跑起來(lái)之后確實(shí)是有效的,那些sleep的進(jìn)程都消失了,感覺(jué)好像這樣就OK了,但是接下來(lái)又報(bào)了另外一個(gè)異常,具體異常忘記記錄了,大致意思是連接丟失,上網(wǎng)搜一下發(fā)現(xiàn)時(shí)wait_timeout時(shí)間設(shè)置太短造成的。所以感覺(jué)修改wait_timeout的時(shí)間不是正確的解,于是又重新設(shè)置成了8H,再?gòu)膭e的地方入手。
這條異常記錄上有提到size是100,于是乎想到提高最大連接量試試(其實(shí)這是個(gè)很笨的主意,因?yàn)?00能超,1000估計(jì)都一樣超,時(shí)間問(wèn)題而已)。這就又涉及到了Tomcat-jdbc連接池的配置,但是之前我試著去配置連接池,想讓最大連接量能大一些,只不過(guò)沒(méi)有用,先貼一下原先的配置,由于是spring-boot項(xiàng)目,所以配置是寫在properties里面
#連接池最大連接數(shù)
spring.datasource.tomcat.max-active=200
#空閑池中最大連接數(shù)
spring.datasource.tomcat.max-idle=50
#空閑池中最小連接數(shù)
spring.datasource.tomcat.min-idle=10
spring.datasource.tomcat.initial-size=10
本意是想讓連接池的連接數(shù)變成200的,但是不起作用,stackoverflow上看是說(shuō)spring-boot的1.4.1版本有一個(gè)更改,本來(lái)是spring.datasource.max-active=200的,現(xiàn)在得要根據(jù)具體情況來(lái)調(diào)整,比如用Tomact就是spring.datasource.tomcat.max-active=200。只不過(guò)我是1.4.3版本,我以為是向上兼容,沒(méi)想到居然是向下兼容,改為不帶Tomcat的就可以了,加上又看了一下連接池的配置,最終連接池的配置版本為
#連接池最大連接數(shù)
spring.datasource.max-active=200
#空閑池中最大連接數(shù)
spring.datasource.max-idle=50
#空閑池中最小連接數(shù)
spring.datasource.min-idle=10
spring.datasource.initial-size=10
#連接在池中空閑最小時(shí)間后被清除
spring.datasource.min-evictable-idle-time-millis=60000
#隔多久時(shí)間清回收廢棄連接
spring.datasource.time-between-eviction-runs-millis=30000
#每次調(diào)用檢測(cè)池里連接的可用性,假如連接池中的連接被數(shù)據(jù)庫(kù)關(guān)閉了,應(yīng)用通過(guò)連接池getConnection時(shí)會(huì)重新創(chuàng)建
spring.datasource.testOnBorrow=true
spring.datasource.validation-query=SELECT 1
#移除被遺棄的連接
spring.datasource.remove-abandoned=true
#設(shè)置超時(shí)時(shí)間
spring.datasource.tomcat.remove-abandoned-timeout=60
不得不說(shuō),在高并發(fā)的應(yīng)用環(huán)境下,連接池的配置是真的重要,關(guān)于連接池的配置我感覺(jué)可以另開(kāi)一篇來(lái)介紹了。最下面兩個(gè)就是確實(shí)解決掉這個(gè)BUG所需要的配置,注釋也稍微寫了一下,是移除被遺棄的連接,超過(guò)60秒就被判斷為遺棄的連接。這里的遺棄的連接就是在代碼過(guò)程中寫的比如沒(méi)有及時(shí)關(guān)閉的連接之類的糟糕的寫法。其實(shí)針對(duì)這個(gè)問(wèn)題,體感上來(lái)說(shuō)也確實(shí)是這么回事,連接用完之后,超過(guò)一個(gè)設(shè)定的時(shí)間就自動(dòng)刪掉。這點(diǎn)有點(diǎn)像Java線程池框架中的Executors.newCachedThreadPool(),設(shè)定的是線程存活60秒就自動(dòng)刪掉,之前試過(guò)在60秒之內(nèi)瘋狂增加線程數(shù),并將線程數(shù)設(shè)定的比較小,一段時(shí)間后就報(bào)錯(cuò)了,跟今天連接池的這種情況確實(shí)是很像。
大概就是這么個(gè)情況,僅僅提供一個(gè)結(jié)題思路,當(dāng)中還有不少繞路,也怪自己對(duì)連接池的配置還并不了解,如果一開(kāi)始就修改對(duì)連接池的配置的話,也不至于被這個(gè)BUG糾纏一整天了。
長(zhǎng)路漫漫。。