<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 42,comments - 83,trackbacks - 0
            前段時(shí)間,有同事跟我說(shuō)客戶那邊有很多狀態(tài)為receive的message,這些message只有在JMS Server或weblogic Server充啟之后才能被消費(fèi)。經(jīng)過(guò)調(diào)查后,這個(gè)問(wèn)題可能是weblogic的一個(gè)bug,當(dāng)然也不排除跟具體環(huán)境有關(guān)的可能。下面我們來(lái)看看問(wèn)題的根本原因是什么,這種分析有助我們更進(jìn)一步理解weblogic JMS的實(shí)現(xiàn)。

            首先我們看一下什么是receive,receive表示一個(gè)message已經(jīng)被consumer消費(fèi),但服務(wù)端還沒(méi)有關(guān)于這個(gè)message的ack,所以消息不能從queue中刪除, 由于queue中的消息是point-2-point的,所以某個(gè)消息被標(biāo)為receive后,這個(gè)消息自然不能被其他consumer消費(fèi)。那么這個(gè)ack由誰(shuí)負(fù)責(zé)發(fā)送給Server呢,什么時(shí)候發(fā)送呢?這些都由我們創(chuàng)建JMS Session時(shí)使用的Ack_mode決定,典型的ack-mode有如下兩種:
      auto-ack: 自動(dòng)響應(yīng)模式,consumer.receive()調(diào)用后,如果服務(wù)器端發(fā)現(xiàn)有可用的message,消息返回到客戶端JMS實(shí)現(xiàn)層,在消息返回給客戶前,由weblogic client(JMSSession.getAsyncMessageForConsumer(),異步接受,比如MessageListener,或JMSSession.receiveMessage(),同步接受)層實(shí)現(xiàn)直接調(diào)用acknowledge()通知服務(wù)器端,服務(wù)器端收到ack后,它會(huì)負(fù)責(zé)負(fù)責(zé)將處于receive的message從物理queue中刪除。
            client-ack: 客戶響應(yīng)模式,consumer.receive()調(diào)用后,客戶端收到消息后,客戶端程序決定什么時(shí)候發(fā)送ack,可以在消息后立即發(fā)送,也可以在消息處理成功后發(fā)送,ack的發(fā)送通過(guò)message.acknowledge()實(shí)現(xiàn)。后面的過(guò)程和auto-ack相同。

      初看這個(gè)問(wèn)題,感覺(jué)是ack沒(méi)有收到,那么什么情況下會(huì)出現(xiàn)ack丟失呢?網(wǎng)絡(luò)問(wèn)題? 那么客戶端或服務(wù)器端的server log應(yīng)該能夠看到異常,客戶堅(jiān)持說(shuō)沒(méi)有任何異常。有點(diǎn)不可思議,要了客戶的代碼,他們沒(méi)有代碼,實(shí)際上他們的應(yīng)用是基于Spring Framework的,通過(guò)簡(jiǎn)單的配置來(lái)實(shí)現(xiàn)他們的業(yè)務(wù)需要,看了下Spring的相關(guān)代碼,客戶之所以說(shuō)沒(méi)有異常,因?yàn)镾pring catch了服務(wù)器端返回的JMSException,并吃掉了這個(gè)異常(即異常沒(méi)有打印出來(lái)),這個(gè)異常輸出是可以通過(guò)Spring的配置來(lái)實(shí)現(xiàn)。客戶配置后,給了我具體的異常,如下:
    java.lang.IllegalArgumentException: Delay is negative.
            at weblogic.timers.internal.TimerManagerImpl.schedule(TimerManagerImpl.java:388)
            at weblogic.timers.internal.TimerManagerImpl.schedule(TimerManagerImpl.java:340)
            at weblogic.messaging.kernel.internal.ReceiveRequestImpl.<init>(ReceiveRequestImp l.java:98)
            at weblogic.messaging.kernel.internal.QueueImpl.receive(QueueImpl.java:820)
            at weblogic.jms.backend.BEConsumerImpl.blockingReceiveStart(BEConsumerImpl.java:1 172)
            at weblogic.jms.backend.BEConsumerImpl.receive(BEConsumerImpl.java:1383)
            at weblogic.jms.backend.BEConsumerImpl.invoke(BEConsumerImpl.java:1088)
            at weblogic.messaging.dispatcher.Request.wrappedFiniteStateMachine(Request.java:7 59)
            at weblogic.messaging.dispatcher.DispatcherImpl.dispatchAsyncInternal(DispatcherI mpl.java:129)
            at weblogic.messaging.dispatcher.DispatcherImpl.dispatchAsync(DispatcherImpl.java :112)
            at weblogic.messaging.dispatcher.Request.dispatchAsync(Request.java:1046)
            at weblogic.jms.dispatcher.Request.dispatchAsync(Request.java:72)
            at weblogic.jms.frontend.FEConsumer.receive(FEConsumer.java:557)
            at weblogic.jms.frontend.FEConsumer.invoke(FEConsumer.java:806)
            at weblogic.messaging.dispatcher.Request.wrappedFiniteStateMachine(Request.java:7 59)
            at weblogic.messaging.dispatcher.DispatcherServerRef.invoke(DispatcherServerRef.j ava:276)
            at weblogic.messaging.dispatcher.DispatcherServerRef.handleRequest(DispatcherServ erRef.java:141)
            at weblogic.messaging.dispatcher.DispatcherServerRef.access$000(DispatcherServerR ef.java:36)
            at weblogic.messaging.dispatcher.DispatcherServerRef$2.run(DispatcherServerRef.ja va:112)
            at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
            at weblogic.work.ExecuteThread.run(ExecuteThread.java:181)

      現(xiàn)在我們看一下Weblogic JMS的receive的基本流程,看看這個(gè)exception為什么會(huì)被拋出來(lái)。
      JMSConsumer.receive(long timewait),客戶端發(fā)起receive請(qǐng)求,其中timewait可有可無(wú),不做指定的話,說(shuō)明沒(méi)有可用消息到達(dá)的話,我們會(huì)一直等下去。如要不作等待的話,可以使用receiveNoWait()。receive()中會(huì)檢查timeout值,如果沒(méi)有指定timeout,那么Long.maxValue會(huì)被設(shè)定成這個(gè)timeout,如果timeout小于0,客戶端將會(huì)收到Invalid Timeout異常,接下來(lái)請(qǐng)求會(huì)被delegate到JMSSession。
            |
            JMSSession.receiveMessage(consumer,timeout),這里timeout會(huì)被重新計(jì)算,然后我們會(huì)創(chuàng)建一個(gè)FEConsumerReceiveRequest對(duì)象。這個(gè)對(duì)象中包含計(jì)算后的timeout,計(jì)算后的timeout應(yīng)該是個(gè)非負(fù)值(上面的異常就是這里的計(jì)算導(dǎo)致的,至于為什么客戶指定的timeout為1,計(jì)算后的timeout變成了負(fù)數(shù),從而導(dǎo)致上面的異常,從代碼層面,看不出有什么問(wèn)題)。FEConsumerReceiveRequest對(duì)象創(chuàng)建后,由JMS FrontEnd Dispatcher負(fù)責(zé)把請(qǐng)求交給后端的JMS Server,Dispatcher是Weblogic JMS中用于負(fù)責(zé)請(qǐng)求傳輸?shù)模蕾囉赗JVM layer,這里不做贅述。
            |     
            RJVM layer, 負(fù)責(zé)RMI socket層的數(shù)據(jù)發(fā)送   
            |        
            FEConsumer.receive(invocableRequest),RJVM層處理完socket數(shù)據(jù)后,請(qǐng)求會(huì)被轉(zhuǎn)給JMSConsumer,JMSConsumer通過(guò)狀態(tài)機(jī)(state machine)來(lái)控制請(qǐng)求處理,沒(méi)有過(guò)多的邏輯,它會(huì)基于收到的receive request創(chuàng)建一個(gè)BEConsumerReceiveRequest對(duì)象,然后把這個(gè)請(qǐng)求通過(guò)JMS BackEnd Dispatcher轉(zhuǎn)發(fā)給BEConsumerImpl。之所以存在FrontEnd /BackEnd Dispatcher,主要考慮到處理請(qǐng)求的server和queue所在的不是同一server。
            |
            BEConsumerImpl.receive(request),request進(jìn)入BEConsumerImpl后,它也通過(guò)state machine來(lái)控制請(qǐng)求處理,下面兩個(gè)方法在調(diào)用過(guò)程中被順序調(diào)用,
            BEConsumerImpl.blockingReceiveStart(request),這里首先檢查timeout值,然后調(diào)用QueueImpl.receive(...)從queue中獲取message,receive()的具體參數(shù)如下,包括timeout, expression(即檢查條件,我們定義的message selector就在其中)。
            BEConsumerImpl.blockingReceiveProcessMessage(request)
            BEConsumerImpl.blockingReceiveComplete(request)
            |
            QueueImpl.receive(expression,count,acknowledge,owner,timeout,started,userBlob),這里除了狀態(tài)檢查,沒(méi)有其他邏輯,它會(huì)根據(jù)傳進(jìn)來(lái)的參數(shù),初始化一個(gè)ReceiveRequestImpl對(duì)象。
            |
            ReceiveRequestImpl.new(),這個(gè)new代表ReceiveRequestImpl的構(gòu)造函數(shù)。
            |
            QueueImpl.get(...),如果timeout = 0,即如果客戶調(diào)用的是receiveNoWait的話,我們直接去通過(guò)QueueImpl.get(...),如果沒(méi)有match的message,那么直接將新建request的result設(shè)定為no result,否則將match的message設(shè)定為result。
            QueueImpl.addReader(receiveRequestImpl),如果timeout != 0,我們會(huì)在ReceiveRequestImpl.start()中調(diào)用QueueImpl.addReader(),addReader()中同樣會(huì)通過(guò)QueueImpl.get()檢查是否有match的message,如果找到相應(yīng)的message,我們會(huì)把message reference狀態(tài)改為receive。
            TimerManagerImpl.schedule(timeout),如果QueueImpl.addReader()中的QueueImpl.get()沒(méi)有找到相應(yīng)的message,我們需要等待(依據(jù)客戶指定的timeout),這個(gè)等待通過(guò)timer去實(shí)現(xiàn),如下:
                    timer = timerManager.schedule(this, timeout);
      指定的timeout到達(dá)后,如果和沒(méi)有可用的message,no result將被返回。從上面的異常堆棧來(lái)看,問(wèn)題就出在這里,如果timeout為負(fù)數(shù),timerMangerImpl在啟動(dòng)trigger的時(shí)候,會(huì)拋出如下的runtimeException,
        java.lang.IllegalArgumentException: Delay is negative.
            
      也許你會(huì)疑問(wèn),這沒(méi)什么問(wèn)題吧,timerTrigger只有在沒(méi)有message的時(shí)候才會(huì)被schedule,既然沒(méi)有message,那有談何狀態(tài)receive message?沒(méi)錯(cuò),起timerTrigger之前我們的確沒(méi)有修改message狀態(tài),但你注意到?jīng)]有,我們?cè)谄餿imerTrigger前,把receiveRequestImpl加入到QueueImpl去了,但我們?cè)谂龅絀llegalArgumentException時(shí)并沒(méi)有把這個(gè)receiveRequestImpl從QueueImpl中刪除,問(wèn)題就在這里。

     1   synchronized void addReader(Reader reader) throws KernelException {
     2             
     3     List list = get(..);
     4     int newCount;
     5     if (list != null) {
     6             
     7     } else {
     8       reader.incrementReserveCount(-reservedCount);
     9       newCount = reader.getCount();
    10     }
    11     if (newCount > 0) {
    12       logger.debug("Adding consumer to reader list");
    13       readerList.add(reader);
    14     }
    15   }

      如果我們不把receiveRequestImpl從QueueImpl的readerList中刪除,那么如果過(guò)一會(huì)有message sender發(fā)送一條和我們上述請(qǐng)求match的message到這個(gè)queue。weblogic收到這個(gè)message后,它會(huì)檢查readerList,如果這個(gè)message match某個(gè)reader,我們會(huì)把message狀態(tài)改成receive,當(dāng)由于IllegalArgumentException,客戶端收到它的時(shí)候,客戶端會(huì)close JMSSession,也就是說(shuō)這個(gè)消息雖然有reader,但無(wú)法deliver到客戶端。

      我們?cè)賮?lái)看看Weblogic JMS sender的相關(guān)流程,
      QueueImpl.messageSendComplete(),消息發(fā)送過(guò)程結(jié)束后(比如涉及store的話,消息此時(shí)已經(jīng)被存儲(chǔ)),到這一步的話,我們會(huì)調(diào)整系統(tǒng)接受的消息數(shù),然后通過(guò)makeMessageAvailable()把消息標(biāo)成visiable或deliver給正在等待的reader。
            QueueImpl.makeMessageAvailable(),它會(huì)直接調(diào)用match()去檢查readerList中是否存在正在等待它的reader。
            QueueImpl.match(),它通過(guò)finderReader()從readerList中檢查reader,如果有符合條件的reader,它會(huì)把這個(gè)message標(biāo)志為receive,同時(shí)把這個(gè)message挪到pending list中去。

      前面我們說(shuō)了,雖然reader還在,但與之對(duì)應(yīng)的JMSConsumer已經(jīng)被close,所以這個(gè)消息根本就無(wú)法deliver出去,自然就不會(huì)有ack從客戶端返回了,這個(gè)消息也只能一直pending了。

      這個(gè)問(wèn)題可能是個(gè)bug,目前還在確認(rèn)之中,但我同時(shí)也在和客戶溝通,可能跟他的環(huán)境有一定關(guān)系(比如NTP時(shí)間同步問(wèn)題)。

      雖然這個(gè)問(wèn)題能引發(fā)message pending,但并不是所有的message pending問(wèn)題都是由它應(yīng)起的。網(wǎng)絡(luò)問(wèn)題也能引發(fā)類似問(wèn)題,具體問(wèn)題具體分析,主要的參考客戶端的JMSException。位于receive后的狀態(tài)是transation,也就是如果發(fā)現(xiàn)狀態(tài)為transaction的message的話,一般而言,是這個(gè)消息要么就是發(fā)送還沒(méi)結(jié)束,要么就是消息正處于一個(gè)delete的事務(wù)單元中,這里就不再一一羅列了。

    posted on 2009-06-17 09:07 走走停停又三年 閱讀(3886) 評(píng)論(9)  編輯  收藏 所屬分類: Weblogic

    FeedBack:
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-06-16 20:46 | sunnycare
    最近項(xiàng)目中正好用到了JMS。
    看了你的blog,有兩個(gè)問(wèn)題問(wèn)下:
    1.如何重現(xiàn)這個(gè)問(wèn)題?
    2.既然沒(méi)有patch出來(lái),那么有什么別的方式避免這個(gè)問(wèn)題?  回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-06-16 21:16 | 走走停停又三年
    這個(gè)問(wèn)題基本很難重現(xiàn),原因很可能跟系統(tǒng)環(huán)境有關(guān)系。weblogic在JMSSession中計(jì)算timeout的時(shí)候,參考了System.currentTimeMills(),如果系統(tǒng)起了NTP client定期做時(shí)間同步的話,可能會(huì)在計(jì)算的時(shí)候引起負(fù)值。如果真跟系統(tǒng)時(shí)間有關(guān),那么最好的做法就是保證客戶端運(yùn)行期間,不要做系統(tǒng)時(shí)間同步。

    另外一個(gè)客戶端回避的方法就是客戶端使用receiveNoWait()來(lái)代替receive()或receive(long timeout)。  回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-06-17 18:26 | sunnycare
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-06-19 12:33 | 找個(gè)美女做老婆
    JBOSS 和 SPRING 的JMS 用過(guò),WEBLOGIC的還沒(méi)有用過(guò)

    Java樂(lè)園 技術(shù)交流社區(qū):http://www.javaly.cn
    Java樂(lè)園 群號(hào):15651281
    驗(yàn)證消息 : Java樂(lè)園  回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-08-03 11:22 | ybb
    我這里碰到一個(gè)奇詭的問(wèn)題:

    環(huán)境 jdk1.6, weblogic 10.3

    配置了一個(gè) TestQueue。

    當(dāng)我們使用 MDB方式接收Queue消息時(shí),如果拋出運(yùn)行時(shí)異常,這個(gè)消息會(huì)自動(dòng)重發(fā)。

    如果客戶端是采用consumer messageListener 的形式接收,當(dāng) listener.onMessage() 拋出運(yùn)行時(shí)異常時(shí),這個(gè)消息的status String 一直是 receive 狀態(tài),只有重啟客戶端才會(huì)重新收到一次。
    代碼如下:
    QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    MessageConsumer receiver = session.createConsumer(sq);
    receiver.setMessageListener(new Receiver());

    難道m(xù)essageListener形式不能像MDB一樣的自動(dòng)重發(fā)?

    找了很多資料,一點(diǎn)思路都沒(méi)了。


      回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-08-03 11:55 | 走走停停又三年
    什么樣的runtime exception呢? 有可能是你的acknowledege可能由于網(wǎng)絡(luò)問(wèn)題沒(méi)有發(fā)過(guò)去,導(dǎo)致message處于receive,所以不會(huì)重新發(fā)送。建議你用client_acknowledge,等你消息成功處理后直接調(diào)用message.acknowledge()。  回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-08-03 13:26 | ybb
    運(yùn)行時(shí)異常,比如拋出空指針異常。
    我的網(wǎng)絡(luò)環(huán)境是局域網(wǎng),acknowledege 肯定能收到的。
    所有在客戶端onMessage中拋出運(yùn)行時(shí)異常的message 都是 receive狀態(tài)。

    但在ejb MDB中,如果出現(xiàn)運(yùn)行時(shí)異常,這消息會(huì)等待10、20秒后重發(fā)。

    如果我自行處理 acknowledege,如何讓message自動(dòng)重發(fā)呢?

      回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-08-03 13:30 | ybb
    能否 交流一下?

    我的msn: ybbkd2@hotmail.com , qq:11898620

    期待  回復(fù)  更多評(píng)論
      
    # re: 關(guān)于JMS Message Pending的問(wèn)題
    2009-09-02 10:00 | 走走停停又三年
    對(duì)于系統(tǒng)時(shí)間問(wèn)題,我們可以用下面的小程序測(cè)試一下,

    public class JVMTimeTest {

    public static void main(String args[]){
    JVMTimeTest test = new JVMTimeTest();
    test.retriveTime();
    }

    private void retriveTime(){
    long previousTime = -1;
    while(true){
    long currentTime = System.currentTimeMillis();
    System.out.println("currentTime is: " + currentTime);
    if(previousTime > currentTime)
    System.out.println("OS time change is detected! and:" +
    "previousTime: " + previousTime + " " +
    "currentTime: " + currentTime);
    previousTime = currentTime;
    try{
    Thread.currentThread().sleep(100);
    }catch(Exception e){}
    }
    }
    }
      回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 国产啪亚洲国产精品无码| 成在人线av无码免费高潮水| 亚洲精品无码专区久久久| 亚洲精品无码久久一线| 五月天婷婷免费视频| 久久久久久一品道精品免费看| 成人黄动漫画免费网站视频 | 亚洲国产精品va在线播放| 亚欧国产一级在线免费| 最好看最新的中文字幕免费| 久久精品蜜芽亚洲国产AV| 亚洲精品无码中文久久字幕| 国产免费MV大全视频网站| 国产亚洲精品福利在线无卡一| 国产一级a毛一级a看免费视频| 久久被窝电影亚洲爽爽爽| 91视频免费网址| 色婷五月综激情亚洲综合| 永久黄网站色视频免费| 亚洲视频一区调教| 一级特黄色毛片免费看| 不卡一卡二卡三亚洲| 一级毛片在线免费看| 亚洲乱码一二三四区国产| 免费网站看v片在线香蕉| 久久久亚洲精华液精华液精华液| 亚洲av午夜精品一区二区三区| 91免费在线视频| 亚洲国产精品综合福利专区| 黄视频在线观看免费| 4444亚洲国产成人精品| 在线视频免费观看www动漫| 一本久久免费视频| 亚洲av日韩av高潮潮喷无码| a毛片基地免费全部视频| 亚洲性天天干天天摸| 成年女人毛片免费播放视频m| 无码免费又爽又高潮喷水的视频| 亚洲AV无码国产精品麻豆天美 | 日本人护士免费xxxx视频| 亚洲av无码久久忘忧草|