記一次tomcat報(bào)錯(cuò)Too many open files的排查歷程

本文的技術(shù)細(xì)節(jié)并不具通用性簿寂,但是排查的思路過程值得自己mark下歉铝,上次遇到這樣的刺激線上故障已經(jīng)是今年3月了~~~這次排查花了3個(gè)小時(shí),還算快啦碘裕,也算最近的一個(gè)小彩蛋携取。

昨天在公司coding,測(cè)試組的同事突然來找我帮孔,告知預(yù)發(fā)環(huán)境的系統(tǒng)掛了雷滋,請(qǐng)求都是異常不撑,打開業(yè)務(wù)日志和tomcat的日志排查,滿屏的Too many open files晤斩,果然 lsof -p pid 預(yù)發(fā)機(jī)器焕檬,已經(jīng)打滿到了65535個(gè),很多都是anon_inode pipe FIFO REG澳泵,真的不知道這是什么鬼:


image.png

內(nèi)心一緊实愚,趕緊用lsof -p pid 查看了下線上所有生產(chǎn)機(jī)器的文件描述符個(gè)數(shù),所幸都在正常范圍兔辅,這才放下心對(duì)預(yù)發(fā)環(huán)境的故障進(jìn)行排查腊敲。

首先保存事故現(xiàn)場(chǎng),用新端口給qa緊急部署了一個(gè)新的環(huán)境维苔,nginx轉(zhuǎn)發(fā)到新的端口碰辅。

接下來就是jstack對(duì)堆棧進(jìn)行檢查。jstack的結(jié)果是有10個(gè)左右的線程處于innative的狀態(tài)介时,其他線程都是處于blocked的狀態(tài)没宾。處于innative狀態(tài)的線程,有一個(gè)明顯是tomcat的主線程潮尝,有6榕吼,7個(gè)處于epoll的epoll_wait狀態(tài)饿序,還有一個(gè)是我們開啟的內(nèi)部端口監(jiān)聽勉失,也是wait狀態(tài),剩下的數(shù)百個(gè)blocked線程都在tomcat的quene take的wait狀態(tài)原探。我想了想乱凿,又谷歌了下,會(huì)不會(huì)是epoll_wait的bug呢咽弦,記得jdk1.6有一個(gè)epoll_wait的bug一直沒解決徒蟆。于是定位的天平往jdk bug的方向傾斜,應(yīng)該是這樣想會(huì)輕松一點(diǎn)吧型型。


image.png

image.png

轉(zhuǎn)念一想段审,如果是epoll_wait的bug,不會(huì)導(dǎo)致文件描述符被打滿呀闹蒜。又陷入了焦灼狀態(tài)寺枉。。

接下來開始嘗試翻日志绷落,業(yè)務(wù)日志是看不出什么問題了姥闪,但是想起來tomcat報(bào)錯(cuò)的時(shí)間是下午2點(diǎn),2點(diǎn)有什么操作么砌烁?查看業(yè)務(wù)日志筐喳,2點(diǎn)有刷新緩存的操作,看起來有了一條靠譜的線!

快速搭建了一個(gè)測(cè)試環(huán)境避归。先lsof一下荣月,大概是400個(gè),刷了一下緩存梳毙,變成600個(gè)喉童,再刷一下,800個(gè)......65535個(gè)顿天,問題到這里已經(jīng)能夠復(fù)現(xiàn)了堂氯,能復(fù)現(xiàn)就好辦。查到了這里牌废,我給自己泡了一杯普洱茶咽白。
測(cè)試了刷新緩存的各個(gè)接口,問題基本上已經(jīng)定位到了刷新通訊緩存鸟缕,我們作為server端采用mina代碼部分的問題晶框,這個(gè)時(shí)候遠(yuǎn)程debug就派上了用場(chǎng)《樱基本上是在懷疑的代碼塊附近授段,每debug一步,我就lsof一下文件描述符的個(gè)數(shù)番甩,終于找到一句可疑的話:

acceptor = new NioSocketAcceptor(CommunicationThreadPool.getSeperatorPool(networkConfig),

                new SimpleIoProcessorPool(NioProcessor.class, availableProcessors +1));

調(diào)試的時(shí)候發(fā)現(xiàn)侵贵,每次執(zhí)行到 new SimpleIoProcessorPool(NioProcessor.class, availableProcessors +1)) ,文件描述符會(huì)增加20缘薛,在刷新緩存的時(shí)候窍育,會(huì)有10次這樣的動(dòng)作,加起來就是180個(gè)描述符的增長宴胧,基本符合我們觀察到的現(xiàn)象.接著谷歌了一下這個(gè)類漱抓,果然我們的代碼里面對(duì)mina的使用是不正確的,我們?cè)谒⑿戮彺娴臅r(shí)候恕齐,對(duì)上一次的SimpleIoProcessorPool需要進(jìn)行卸載乞娄。也即,我們需要將SimpleIoProcessorPool記錄下來显歧,再調(diào)用sessionSimpleIoProcessorPool.dispose();進(jìn)行卸載仪或。

最后的代碼長成這樣:

/**

* 停止監(jiān)聽服務(wù)

*/

    public void dispose() {

if (acceptor !=null) {

acceptor.unbind();

            acceptor.dispose();

            CommunicationConfig networkConfig =configs.get(0);

            LoggerUtil.info(logger, "銷毀channelCode:" + networkConfig.getChannelCode() +"端口:"

                                    + networkConfig.getPort() +"成功!");

        }

if (sessionSimpleIoProcessorPool !=null) {

sessionSimpleIoProcessorPool.dispose();

        }

}

/**

*  啟動(dòng)服務(wù)端監(jiān)聽

*

    * @param listener

    * @throws IOException

*/

    public void startup()throws IOException {

CommunicationConfig networkConfig =configs.get(0);

        /* 設(shè)置內(nèi)存獲取方式 */

        IoBuffer.allocate(1024, false).setAutoExpand(true);

        int availableProcessors = Runtime.getRuntime().availableProcessors();

        sessionSimpleIoProcessorPool =new SimpleIoProcessorPool(NioProcessor.class, availableProcessors +1);

        acceptor =new NioSocketAcceptor(CommunicationThreadPool.getSeperatorPool(networkConfig),

                sessionSimpleIoProcessorPool);

        // SSL連接屬性設(shè)置,責(zé)任鏈模式追迟,必須在解碼器鏈前面

        initSSLConfig(networkConfig);

        // 初始化Socket接收連接

        initSocketAcceptorConfig(networkConfig);

        acceptor.setReuseAddress(true);

        acceptor.setHandler(TcpServerListenerContext.getListener(ConfigUtil

.getTcpKey(networkConfig)));

        acceptor.bind(new InetSocketAddress(networkConfig.getPort()));

        LoggerUtil.info(logger, "啟動(dòng)channelCode:" + networkConfig.getChannelCode() +"端口:"

                                + networkConfig.getPort() +"成功溶其!");

    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市敦间,隨后出現(xiàn)的幾起案子瓶逃,更是在濱河造成了極大的恐慌束铭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厢绝,死亡現(xiàn)場(chǎng)離奇詭異契沫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昔汉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門懈万,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靶病,你說我怎么就攤上這事会通。” “怎么了娄周?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵涕侈,是天一觀的道長。 經(jīng)常有香客問我煤辨,道長裳涛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任众辨,我火速辦了婚禮端三,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鹃彻。我一直安慰自己郊闯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布浮声。 她就那樣靜靜地躺著虚婿,像睡著了一般旋奢。 火紅的嫁衣襯著肌膚如雪泳挥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天至朗,我揣著相機(jī)與錄音屉符,去河邊找鬼。 笑死锹引,一個(gè)胖子當(dāng)著我的面吹牛矗钟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫌变,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼吨艇,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了腾啥?” 一聲冷哼從身側(cè)響起东涡,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤冯吓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后疮跑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體组贺,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年祖娘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了失尖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渐苏,死狀恐怖掀潮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琼富,我是刑警寧澤胧辽,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站公黑,受9級(jí)特大地震影響邑商,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凡蚜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一人断、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧朝蜘,春花似錦恶迈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至副渴,卻和暖如春奈附,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煮剧。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工斥滤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人勉盅。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓佑颇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親草娜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挑胸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容