ActiveMQ由于Session is closed錯(cuò)誤導(dǎo)致發(fā)送消息失敗的問(wèn)題回顧

天黑請(qǐng)閉眼 : 問(wèn)題背景

在這樣一個(gè)難得陽(yáng)光燦爛的上午,大家都沐浴在清晨的清爽之中的時(shí)候……
好吧,我編不下去了,其實(shí)就是大清早被通知說(shuō)我們的消息顯示的不正確颅围,然后開(kāi)始排查問(wèn)題,經(jīng)由定位之后發(fā)現(xiàn)問(wèn)題在于MQ消息沒(méi)有發(fā)送成功恨搓,導(dǎo)致生成的信息有缺失,然而悲劇的是禍不單行筏养,由于之前配置日志的問(wèn)題導(dǎo)致了歷史日志的丟失斧抱。唯一的線索就是當(dāng)時(shí)的一個(gè)同事給另一個(gè)同事的錯(cuò)誤信息截圖,如下:

警察叔叔渐溶,就是這個(gè)人辉浦!

于是就此,我們展開(kāi)了調(diào)查……


狼人請(qǐng)殺人 : 錯(cuò)誤位置

首先我們開(kāi)始對(duì)可疑的問(wèn)題點(diǎn)開(kāi)始反推茎辐,鑒于存在錯(cuò)誤信息的線索宪郊,我們分析了MQ Client的源碼,發(fā)現(xiàn)了拋出這個(gè)異常的位置:

    /**
     * Check if the session is closed. It is used for ensuring that the session
     * is open before performing various operations.
     *
     * @throws IllegalStateException if the Session is closed
     */
    protected void checkClosed() throws IllegalStateException {
        if (closed) {
            throw new IllegalStateException("The Session is closed");
        }
    }

結(jié)合我們具體的業(yè)務(wù)日志代碼拖陆,以及數(shù)據(jù)庫(kù)記錄弛槐,反推具體的問(wèn)題發(fā)生點(diǎn)應(yīng)該在于我們對(duì)MQ發(fā)送消息的功能封裝類位置:

        /**
         * 注意提交事務(wù) 方法commit()
         * 
         * queueName此處用于初始化,防止static對(duì)象先初始化時(shí)候出現(xiàn)空指針的問(wèn)題
         * 
         * @param queueName
         * @param msg
         * @throws JMSException
         */
        public void sendMsg(String queueName, JSONObject messageJson) {
            try {
                if (null == producer) {
                    init(queueName);// 每一個(gè)queue分別實(shí)例化自己的參數(shù)
                }
                if (null != producer) {
                    //統(tǒng)一增加queueName
                    messageJson.put("queueName", queueName);
                    String msg = messageJson.toString();


                    // **警察叔叔,就是這里**
                    message = session.createTextMessage(msg);


                    if (null != message) {
                        producer.send(message);
                        mqLog.info("MQProducer, sendMsg succ, queueName:" + queueName + ", msg:" + msg);
                    } else {
                        throw new JMSException(" 消息未成功發(fā)送: " + msg);
                    }
                } else {
                    throw new JMSException(" 消息未成功發(fā)送: " + messageJson.toString());
                }
            } catch (Exception exp) {
                exp.printStackTrace();
                Log4jUtil.exception(exp);
                mqLog.error(messageJson.toString());
            }

        }

預(yù)言家請(qǐng)驗(yàn)人 : 錯(cuò)誤原因

鑒于這里就明確了是Session的調(diào)用時(shí)依啰,由于內(nèi)部的closed值為TRUE導(dǎo)致了這個(gè)異常信息的打印乎串,因此我們繼續(xù)通過(guò)源碼排查了這個(gè)值會(huì)被寫為TRUE的情況,發(fā)現(xiàn)是在于如下釋放(類似于析構(gòu)的情況)的時(shí)候才會(huì)導(dǎo)致這個(gè)值的變化:

    public synchronized void dispose() throws JMSException {
        if (!closed) {

            try {
                executor.close();

                for (Iterator<ActiveMQMessageConsumer> iter = consumers.iterator(); iter.hasNext();) {
                    ActiveMQMessageConsumer consumer = iter.next();
                    consumer.setFailureError(connection.getFirstFailureError());
                    consumer.dispose();
                    lastDeliveredSequenceId = Math.max(lastDeliveredSequenceId, consumer.getLastDeliveredSequenceId());
                }
                consumers.clear();

                for (Iterator<ActiveMQMessageProducer> iter = producers.iterator(); iter.hasNext();) {
                    ActiveMQMessageProducer producer = iter.next();
                    producer.dispose();
                }
                producers.clear();

                try {
                    if (getTransactionContext().isInLocalTransaction()) {
                        rollback();
                    }
                } catch (JMSException e) {
                }

            } finally {
                connection.removeSession(this);
                this.transactionContext = null;

                // ** 警察叔叔速警,就是這個(gè)人叹誉! **
                closed = true;

            }
        }
    }

因此想到可能是由于鏈接被釋放了才導(dǎo)致了這個(gè)問(wèn)題,開(kāi)始在MQ Server端找對(duì)應(yīng)日志闷旧,于是發(fā)現(xiàn)了如下錯(cuò)誤信息长豁,由于心跳(或其它)的超時(shí),導(dǎo)致服務(wù)器端主動(dòng)釋放了相關(guān)的鏈接:

由于超時(shí)釋放了鏈接

天亮了 : 根因分析

由此我們得到大致的結(jié)論忙灼,由于我們本身的代碼里面采用的是單例模式匠襟,造成了Session值只會(huì)在一開(kāi)始創(chuàng)建Producer之后就保持住。因此雖然服務(wù)器端釋放后缀棍,我們Client端的Session值還是使用了之前的值宅此,導(dǎo)致了這個(gè)異常被拋出。
也就是說(shuō)MQ本身的程序在發(fā)現(xiàn)異常之后開(kāi)始清理相關(guān)的數(shù)據(jù)爬范,并設(shè)置相關(guān)的狀態(tài)(把Closed變量置為TRUE)父腕。而我們的程序由于還是引用到了之前的對(duì)象上,因此在訪問(wèn)Closed變量時(shí)發(fā)現(xiàn)出錯(cuò)了青瀑,因此拋出異常璧亮,導(dǎo)致我們之后的發(fā)送失敗

沒(méi)錯(cuò)萧诫,我就是犯人

Session在Producer中的聲明如下:

    // 這里的Producer是單例模式的
    // Session一旦創(chuàng)建之后是不會(huì)更新的
    protected class MQProducer {
        private ActiveMQConnectionFactory factory = null;
        private Connection connection = null;
        private Session session = null;     
        // 以下省略若干行
        ...

請(qǐng)投票 : 問(wèn)題解決

那么問(wèn)題清楚了之后,還是不明確為什么會(huì)導(dǎo)致這種異常釋放枝嘶,突然小伙伴們想起了之前收到的一封郵件帘饶,于是成功發(fā)現(xiàn)了兇手(核對(duì)日志時(shí)間和機(jī)器,完全一致)……
這個(gè)故事告訴我們一定要注意看郵件啊群扶,以及阿里的監(jiān)控系統(tǒng)好犀利凹翱獭!


兇手

當(dāng)然我們自身的代碼中竞阐,對(duì)這方面理解也不夠深入缴饭,主要在于:

  1. 捕獲這種異常之后,僅僅是簡(jiǎn)單的記錄了相關(guān)的log骆莹,其實(shí)更優(yōu)雅的方式是針對(duì)這種錯(cuò)誤作出健壯性的反饋(當(dāng)如具體的方式和策略還得再深入理解相關(guān)的機(jī)制和相關(guān)實(shí)現(xiàn))
  2. 單例模式颗搂,有自身的好處,但是Session固定之后是否合適幕垦,還需要結(jié)合第1點(diǎn)來(lái)具體給出策略

結(jié)局 : 寫在最后

僅以此記錄一下的問(wèn)題丢氢,防止之后忘記了。
也希望能給有類似問(wèn)題發(fā)生的小伙伴們一些線索先改。
have fun~ :)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疚察,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仇奶,更是在濱河造成了極大的恐慌稍浆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猜嘱,死亡現(xiàn)場(chǎng)離奇詭異衅枫,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)朗伶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門弦撩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人论皆,你說(shuō)我怎么就攤上這事益楼。” “怎么了点晴?”我有些...
    開(kāi)封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵感凤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我粒督,道長(zhǎng)陪竿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任屠橄,我火速辦了婚禮族跛,結(jié)果婚禮上闰挡,老公的妹妹穿的比我還像新娘。我一直安慰自己礁哄,他們只是感情好长酗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著桐绒,像睡著了一般夺脾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茉继,一...
    開(kāi)封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天劳翰,我揣著相機(jī)與錄音,去河邊找鬼馒疹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乙墙,可吹牛的內(nèi)容都是我干的颖变。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼听想,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腥刹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起汉买,我...
    開(kāi)封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衔峰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蛙粘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體垫卤,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年出牧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穴肘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舔痕,死狀恐怖评抚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伯复,我是刑警寧澤慨代,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站啸如,受9級(jí)特大地震影響侍匙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叮雳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一丈积、第九天 我趴在偏房一處隱蔽的房頂上張望筐骇。 院中可真熱鬧,春花似錦江滨、人聲如沸铛纬。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)告唆。三九已至,卻和暖如春晶密,著一層夾襖步出監(jiān)牢的瞬間擒悬,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工稻艰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懂牧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓尊勿,卻偏偏與公主長(zhǎng)得像僧凤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子元扔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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