<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
            前段時間,有同事跟我說客戶那邊有很多狀態為receive的message,這些message只有在JMS Server或weblogic Server充啟之后才能被消費。經過調查后,這個問題可能是weblogic的一個bug,當然也不排除跟具體環境有關的可能。下面我們來看看問題的根本原因是什么,這種分析有助我們更進一步理解weblogic JMS的實現。

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

      初看這個問題,感覺是ack沒有收到,那么什么情況下會出現ack丟失呢?網絡問題? 那么客戶端或服務器端的server log應該能夠看到異常,客戶堅持說沒有任何異常。有點不可思議,要了客戶的代碼,他們沒有代碼,實際上他們的應用是基于Spring Framework的,通過簡單的配置來實現他們的業務需要,看了下Spring的相關代碼,客戶之所以說沒有異常,因為Spring catch了服務器端返回的JMSException,并吃掉了這個異常(即異常沒有打印出來),這個異常輸出是可以通過Spring的配置來實現。客戶配置后,給了我具體的異常,如下:
    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)

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

     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中刪除,那么如果過一會有message sender發送一條和我們上述請求match的message到這個queue。weblogic收到這個message后,它會檢查readerList,如果這個message match某個reader,我們會把message狀態改成receive,當由于IllegalArgumentException,客戶端收到它的時候,客戶端會close JMSSession,也就是說這個消息雖然有reader,但無法deliver到客戶端。

      我們再來看看Weblogic JMS sender的相關流程,
      QueueImpl.messageSendComplete(),消息發送過程結束后(比如涉及store的話,消息此時已經被存儲),到這一步的話,我們會調整系統接受的消息數,然后通過makeMessageAvailable()把消息標成visiable或deliver給正在等待的reader。
            QueueImpl.makeMessageAvailable(),它會直接調用match()去檢查readerList中是否存在正在等待它的reader。
            QueueImpl.match(),它通過finderReader()從readerList中檢查reader,如果有符合條件的reader,它會把這個message標志為receive,同時把這個message挪到pending list中去。

      前面我們說了,雖然reader還在,但與之對應的JMSConsumer已經被close,所以這個消息根本就無法deliver出去,自然就不會有ack從客戶端返回了,這個消息也只能一直pending了。

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

      雖然這個問題能引發message pending,但并不是所有的message pending問題都是由它應起的。網絡問題也能引發類似問題,具體問題具體分析,主要的參考客戶端的JMSException。位于receive后的狀態是transation,也就是如果發現狀態為transaction的message的話,一般而言,是這個消息要么就是發送還沒結束,要么就是消息正處于一個delete的事務單元中,這里就不再一一羅列了。

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

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

    另外一個客戶端回避的方法就是客戶端使用receiveNoWait()來代替receive()或receive(long timeout)。  回復  更多評論
      
    # re: 關于JMS Message Pending的問題
    2009-06-17 18:26 | sunnycare
    3x....  回復  更多評論
      
    # re: 關于JMS Message Pending的問題
    2009-06-19 12:33 | 找個美女做老婆
    JBOSS 和 SPRING 的JMS 用過,WEBLOGIC的還沒有用過

    Java樂園 技術交流社區:http://www.javaly.cn
    Java樂園 群號:15651281
    驗證消息 : Java樂園  回復  更多評論
      
    # re: 關于JMS Message Pending的問題
    2009-08-03 11:22 | ybb
    我這里碰到一個奇詭的問題:

    環境 jdk1.6, weblogic 10.3

    配置了一個 TestQueue。

    當我們使用 MDB方式接收Queue消息時,如果拋出運行時異常,這個消息會自動重發。

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

    難道messageListener形式不能像MDB一樣的自動重發?

    找了很多資料,一點思路都沒了。


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

    但在ejb MDB中,如果出現運行時異常,這消息會等待10、20秒后重發。

    如果我自行處理 acknowledege,如何讓message自動重發呢?

      回復  更多評論
      
    # re: 關于JMS Message Pending的問題
    2009-08-03 13:30 | ybb
    能否 交流一下?

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

    期待  回復  更多評論
      
    # re: 關于JMS Message Pending的問題
    2009-09-02 10:00 | 走走停停又三年
    對于系統時間問題,我們可以用下面的小程序測試一下,

    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){}
    }
    }
    }
      回復  更多評論
      
    主站蜘蛛池模板: 嫖丰满老熟妇AAAA片免费看| 无码免费又爽又高潮喷水的视频| 两性色午夜视频免费播放| 亚洲人成国产精品无码| 尤物视频在线免费观看| 亚洲国产精品一区二区第一页免| 337P日本欧洲亚洲大胆艺术图| 大学生美女毛片免费视频| 久久国产精品免费网站| 亚洲老妈激情一区二区三区| 国产免费区在线观看十分钟| 亚洲国产精品VA在线看黑人 | 久久er国产精品免费观看2| 亚洲ⅴ国产v天堂a无码二区| 91大神免费观看| 亚洲第一综合天堂另类专| mm1313亚洲精品无码又大又粗| 国产精品无码免费专区午夜| 亚洲AV无码一区二区二三区软件| 中文字幕乱码免费视频| 国产精品亚洲lv粉色| 亚洲乱码国产一区三区| 四虎成年永久免费网站| 国产亚洲综合一区二区三区| 亚洲成A人片在线观看WWW| 日韩毛片免费无码无毒视频观看| 香蕉视频在线观看免费| 亚洲AV无码日韩AV无码导航| 永久中文字幕免费视频网站| 久久精品成人免费看| 久久精品国产亚洲AV未满十八| 亚洲av日韩av激情亚洲| 日韩亚洲精品福利| 好男人www免费高清视频在线| 国产男女爽爽爽免费视频| 亚洲图片校园春色| 亚洲国产精品免费视频| 免费大香伊蕉在人线国产| 99久久国产热无码精品免费 | 亚洲精品综合一二三区在线| 亚洲精品第一国产综合精品99 |