一次未使用單例模式導(dǎo)致的Too many open files 上線事故

一. 前言

(1)事件的描述:在新項(xiàng)目上線后,部分基礎(chǔ)數(shù)據(jù)需要兩個(gè)系統(tǒng)進(jìn)行同步。這些基礎(chǔ)數(shù)據(jù)涉及大量的歷史數(shù)據(jù),需要從新上線的項(xiàng)目中,同步到其他有關(guān)聯(lián)的系統(tǒng)中。同步的是方式采用RocketMq進(jìn)行消息推送和消費(fèi)。

(2)事故的反思:為避免今后在犯同樣的錯(cuò)誤,反思bug為何未在測試環(huán)節(jié)中被暴露出來,最主要的原因還是并未進(jìn)行充分的測試,測試的場景并不能覆蓋生產(chǎn)中所能涉及到的所有場景,導(dǎo)致事故的發(fā)生。

二. 事故回顧排查經(jīng)過

(1)在基礎(chǔ)數(shù)據(jù)同步過程中,一共有四部分基礎(chǔ)數(shù)據(jù)需要同步。按照數(shù)據(jù)依賴關(guān)系,有先后順序的進(jìn)行同步,當(dāng)在第四部分?jǐn)?shù)據(jù)也是數(shù)據(jù)量最大的一部分?jǐn)?shù)據(jù)同步時(shí),bug被暴露出來。數(shù)據(jù)量大概是13000多條,同步的方式是批量同步,由于雙方系統(tǒng)中都有限制,所以批量同步時(shí),每條報(bào)文中的數(shù)據(jù)量是30條。計(jì)算下來也就是需要推送434次,才可以將人員信息 全部推送完成。

至此就是本次事故的起始情況,下面來看一下異常信息。

(2)too many open files 異常

圖片

分析:從異常的直觀含以上來看是開起了太多的文件數(shù)據(jù)量,導(dǎo)致 consumer 再創(chuàng)建socket鏈接時(shí)失敗。通過百度查閱后學(xué)習(xí)到,在linux系統(tǒng)一切都是文件,socket也是以文件的形式存在,那么由此就可以將異常鏈接起來了,每創(chuàng)建一個(gè)socket鏈接或每開啟一個(gè)線程或者進(jìn)程,都是在創(chuàng)建并開啟一個(gè)文件,這個(gè)文件在linux系統(tǒng)中的學(xué)名叫做“文件句柄”。

并且每臺(tái)Linux系統(tǒng)中都有默認(rèn)的 “文件句柄”數(shù)量的限制,默認(rèn)是1024個(gè)(通過 ulimit -a 可以查看)通常只有在高并發(fā)的場景下會(huì)出現(xiàn)該情況,但以當(dāng)前的配置來看,足以支撐現(xiàn)有的并發(fā)數(shù)。

圖片

(3)繼續(xù)往下看, 正常情況下,socket使用后close掉是會(huì)被正常關(guān)閉回收掉的,且在新上線的系統(tǒng)并不會(huì)有什么并發(fā)量,所以socket持續(xù)存在是并不正常的,通過 ps -ef | grep TIME_WAIT 可以查看到,項(xiàng)目所在的服務(wù)器中有大量的 socket 處在等待狀態(tài),這也就是為生socket 不能在被實(shí)例化和 too many open files 的原因。只要批量個(gè)幾次,一定會(huì)超過1024這個(gè)open file的數(shù)量。

接下來,查看 err log 中異常拋出的具體類 ,從代碼中找到了這樣一段代碼

圖片

這段代碼的功能是用來創(chuàng)建 MQ 中 Producer 與 Broker(這里的broker是阿里的ONS)的鏈接方法,會(huì)實(shí)例化一個(gè)Producer出來供后續(xù)方法調(diào)用,實(shí)例化的本質(zhì)也就是創(chuàng)建一個(gè)和Broker 的RPC鏈接,至此too many open files 的原因就基本說的通了。

(4)再來看另外一段代碼

圖片

創(chuàng)建完鏈接后,緊接這就是方法調(diào)用,將數(shù)據(jù)推送出去。但是在推送后并未將資源關(guān)閉,這也就是為什么有那么多處在 TIME_WAIT 狀態(tài)下的 socket 的連接。

至此整個(gè)事故的回顧到此就告一段落了。

三. 方法的改進(jìn)

針對(duì)本次事故產(chǎn)生問題的位置,有兩種解決辦法。

(1)方法一:將創(chuàng)建 Product 實(shí)例化的方法改為單例模式。

圖片

此處我們使用 (Double-Check)模式優(yōu)化該方法中的單例模式以提高效率。

(2)方法二:資源調(diào)用完后,關(guān)閉釋放掉資源。

圖片

以上兩種方法任選其一即可。但是對(duì)于不同的使用場景,兩種改進(jìn)方法各有優(yōu)勢,具體情況還需具體分析。

四. 總結(jié)

在項(xiàng)目開發(fā)完成后,開發(fā)應(yīng)先將開發(fā)好的功能,盡可能的充分測試,以暴露可能存在的bug。因?yàn)橛行゜ug并不是簡單的黑盒測試可以暴露出來的。代碼書寫要嚴(yán)謹(jǐn),如果時(shí)間允許,每一個(gè)環(huán)節(jié)其實(shí)都值得認(rèn)真反復(fù)的推敲。

@Author : zhankai

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容