posts - 42,comments - 83,trackbacks - 0
                  目前Apache + Weblogic Cluster是很多企業(yè)應(yīng)用的架構(gòu)模式,前端Apache負(fù)責(zé)請求分發(fā),后端利用Weblogic Cluster的session replication提供failover。絕大多數(shù)情況下,這種模式工作的很好。這篇文章我將以標(biāo)題中的兩個名詞為中心,探討一下Apache plugin的分發(fā)及failover機(jī)制。首先看一下這兩個名詞:

          No backend server available:沒有可用的后端服務(wù)器。最常見最可能的原因就是:后端server全部沒有啟動或者Apache無法連接到其中的任何一個。復(fù)現(xiàn)方式很簡單,只啟動Apache,然后做后端weblogic server的業(yè)務(wù)請求,或者中斷Apache到后端服務(wù)器的網(wǎng)絡(luò)。這種原因很好檢查,也很好解決。本文中我們將探討另外一種情況,網(wǎng)絡(luò)沒有問題,而且至少有一個服務(wù)器存活,但客戶端仍然碰到“No backend server available”。

          HALF_OPEN_SOCKET_RETRY:Weblogic內(nèi)部自定的異常,這種異常通常由于Apache使用了某個已經(jīng)中斷的連接向后端服務(wù)器發(fā)送請求,這種請求肯定會失敗。連接中斷的原因也很多,比如我們關(guān)閉后端的服務(wù)器,那么Apache手里到這個服務(wù)器的連接再次被使用時,Apache將會收到HALF_OPEN_SOCKET_RETRY。

                  首先,我們看一下plugin的分發(fā)機(jī)制。通常每個非初始請求都會在自己的cookie中有一個JSESSIONID,JSESSIONID由weblogic產(chǎn)生,用戶唯一標(biāo)識和該用戶相關(guān)的Session。JSessionID常見格式為:
          SessionHash ! primaryHash ! SecondaryHash,比如:
          s4wYKyvDKG2ZDHwQclchD50PYjvCsN9nLSL9hPRMQ2ZzM3pVdbL1!1198345223!-977705075
          primary:該Session的主服務(wù)器
          secondary:該Session的備份服務(wù)器,如果沒做SessionReplication,則沒有該列。如果SessionReplication失敗,那么該列會是none或null。

                  有了JSESSIONID,在load_utils.getPreferredServersFromCookie()中,plugin會解析這個JSESSIONID,如果我們打開plugin的debug,我們會看到類似如下的信息,

          Thu Sep 10 08:56:36 2009 <1899912525369961> Found cookie from cookie header: JSESSIONID=s4wYKyvDKG2ZDHwQclchD50PYjvCsN9nLSL9hPRMQ2ZzM3pVdbL1!1198345223!-977705075
          Thu Sep 10 08:56:36 2009 <1899912525369961> Parsing cookie JSESSIONID=s4wYKyvDKG2ZDHwQclchD50PYjvCsN9nLSL9hPRMQ2ZzM3pVdbL1!1198345223!-977705075
          Thu Sep 10 08:56:36 2009 <1899912525369961> getpreferredServersFromCookie: [1198345223!-977705075]
          Thu Sep 10 08:56:36 2009 <1899912525369961> primaryJVMID: [1198345223]
          secondaryJVMID: [-977705075]

                解析出了primaryJVMID、secondaryJVMID,這是plugin需要檢查這兩個id是否為空,如果兩個都為空,那么說明這個請求沒有prefer server,plugin只要按照最基本的load balance策略作請求分發(fā)即可。如果至少有一個不為空,我們繼續(xù)找該session所prefer的server,debug 信息如下:

          Thu Sep 10 08:56:36 2009 <1899912525369961>Primary or Secondary JVMID *not* found in cookie, ignore preferred servers //說明沒有從cookie中找到primay或secondary。
          Thu Sep 10 08:56:36 2009 <1899912525369961> No of JVMIDs found in cookie: 2 //說明找到了兩個jvmID

                  Apache plugin運(yùn)行過程中會維護(hù)一個server list,這個server list就是我們在httpd.conf中指定的server列表,如果我們使用了dynamic server list,那么這個list是隨著后端cluster的變化而動態(tài)更新的。server list中每個server有自己的JVMID。plugin從cookie中解析出primaryJVMID、secondaryJVMID后,需要primaryJVMID、secondary JVMID做校驗(yàn),防止cookie中的信息過于陳舊(如果后端服務(wù)器發(fā)生過重起,就會出現(xiàn)JVMID不一致的情況)。校驗(yàn)過程其實(shí)很簡單,plugin會遍歷手里的server list。如果發(fā)現(xiàn)某個server被標(biāo)志為bad,它會檢查skip-time,如果skip-time(默認(rèn)10秒)已到,即從這個server被mark bad到現(xiàn)在已經(jīng)超過10秒,那么plugin會把這個server重新mark good(這樣如果我們接下來創(chuàng)建到這個server的連接成功,那這個server就真的可用了,plugin通過這種機(jī)制來恢復(fù)后端server)。

           1     time_t t = lists[i].startMarkBad;
           2     if (t > 0) {
           3       time_t now = 0;
           4       time(&now);
           5       int delta = now - t;
           6       if (delta >= MAX_SKIP_TIME) {
           7         lists[i].startMarkBad = 0;
           8         lists[i].isGood = 1;
           9       }
          10     }

                  接著plugin會將解析出來的primaryJVMID、secondaryJVMID后自己server list中server的JVMID做比對。如果primary匹配,就把對應(yīng)的serverInfo設(shè)入請求的preferred[0],同樣如果seconday匹配,對應(yīng)的serverInfo會被設(shè)入請求的preferred[1]。注意:接下來plugin將以preferred為基礎(chǔ)進(jìn)行分發(fā)、failover。debug信息如下:

          Wed Sep 02 14:33:12 2009 <553612518731924> getPreferredFromCookie: Found Primary 172.23.4.53:8001:0
          Wed Sep 02 14:33:12 2009 <553612518731924> getPreferredFromCookie: Found Secondary 172.23.4.52:8001:0
          如果匹配的server數(shù)目和cookie中jvmid數(shù)一致,則還能看到如下輸出:
          Wed Sep 02 14:33:12 2009 <553612518731924> getPreferredFromCookie: Found 2 servers
          如果不匹配,那么輸出信息將是下面這樣:
          Wed Sep 02 14:33:12 2009 <553612518731924> getPreferredFromCookie: Found atleast 1 server

                  如果匹配結(jié)果發(fā)現(xiàn)沒有一致的JVMID,如下:
          Thu Sep 10 08:56:36 2009 <1899912525369961> getPreferredFromCookie: Either JVMIDs not set or they are stale. Will try to get JVMIDs from WLS
          那么這時候plugin就需要強(qiáng)制更新自己server list中server的JVMID,這個更新通過proxy.initJVMID()實(shí)現(xiàn)。initJVMID中,plugin遍歷整個server list,嘗試創(chuàng)建到每個server的連接,如果連接創(chuàng)建失敗,這個server會被mark bad。連接創(chuàng)建成功,plugin向后端server發(fā)送internal request,如下:
          Thu Sep 10 08:56:36 2009 <1899912525369961> ======internal request /bea_wls_internal/ WLDummyInitJVMIDs======
          如果連接能夠成功創(chuàng)建,但發(fā)送請求的時候發(fā)生HALF_OPEN_SOCKET_RETRY(這里為什么會發(fā)送HALF_OPEN_SOCKET_RETRY,后面我們會討論到),這個server也會被mark bad(這里對HALF_OPEN_SOCKET_RETRY的處理不同于應(yīng)用請求),如下:

          Thu Sep 10 08:56:36 2009 <1899912525369961> readStatus: Local port of the socket 53149, connected to Remote Host/Port 172.23.4.53/8001
          Thu Sep 10 08:56:36 2009 <1899912525369961> readStatus: Response contains no data - isRecycled: 0
          Thu Sep 10 08:56:36 2009 <1899912525369961> *******Exception type [HALF_OPEN_SOCKET_RETRY] (Unexpected EOF reading HTTP status - request retry) raised at line 854 of ../nsapi/URL.cpp
          Thu Sep 10 08:56:36 2009 <1899912525369961> initJVMID: Failed to retrieved JVMID for 172.23.4.53:8001:8001
          Thu Sep 10 08:56:36 2009 <1899912525369961> initJVMID: Marked server as BAD

          如果請求處理成功,將會看到類似于以下的信息:

          Thu Sep 10 08:56:36 2009 <1899912525369961> ServerInfo struct for JVMID '-977705075' populated
           Server Details are:
           OrigHostInfo [172.23.4.52]
           isOrigHostInfoDNS [0]
           Host [172.23.4.52]
           Port [8001]
           SecurePort [8201]
          Thu Sep 10 08:56:36 2009 <1899912525369961> Initializing lastIndex=0 for a list of length=1

          initJVMID()除了更新jvmID,同時還給我們這個請求分配了preferred server list。

                  獲取到請求的preferred后,程序的控制權(quán)將回到proxy.wl_proxy(),這個方法是所有http請求的入口。回到wl_proxy后,進(jìn)入while循環(huán),循環(huán)的終止條件就是我們的retry次數(shù)(默認(rèn)5次)。進(jìn)入循環(huán)后,debug信息如下:
          Thu Sep 10 08:56:36 2009 <1899912525369961> attempt #0 out of a max of 5 

                  plugin要成功發(fā)送一個http請求,首先需要拿到connection,如果我們設(shè)置了keep-alive,首先會從connection pool中獲取,如果沒有,則創(chuàng)建新的物理連接。debug信息分別如下:
          Wed Sep 02 14:35:45 2009 <553612518733456> got a pooled connection to preferred server '172.23.4.53/8001for '/test_sleep/testsleep.jsp', Local port:3651
          Wed Sep 02 14:33:12 2009 <553612518731924> created a new connection to preferred server '172.23.4.53/8001' for '/test_sleep/testsleep.jsp', Local port:3636

                   拿到connection后,plugin會將request的header信息發(fā)送到server端,并從連接上讀取response。此時上面兩種連接都可能出現(xiàn)HALF_OPEN_SOCKET_RETRY。我們分別看看這兩種情況:

          1:如果pool connection對應(yīng)的后端服務(wù)器已經(jīng)被shutdown,那么我們那pool connection做操作的時候,肯定無法完成,因此很容易碰到HALF_OPEN_SOCKET_RETRY。碰到HALF_OPEN_SOCKET_RETRY,plugin會做retry,而不是直接failover。重新去getConnection,如果pool中corrupted connection足夠多,那么五次失敗后,客戶端最終會看到“No backend server available”,debug中會有如下信息:

          Tue Sep  1 10:43:41 2009 <10088125176582121> request [/socs/favicon.ico] did NOT process successfully..................

          2:pool中的connection不是很多,比如2個,那么我們做完兩次retry后,這是就要創(chuàng)建物理的連接,如果連接創(chuàng)建失敗,這個server會立刻mark bad,請求將被failover到secondary,debug信息如下:
          Wed Sep 02 14:36:06 2009 <553612518733456> *******Exception type [CONNECTION_REFUSED] (Error connecting to host 10.182.216.198:8002) raised at line 1732 of ../nsapi/URL.cpp
          如果創(chuàng)建連接成功,但sendRequestHeader的時候發(fā)生HALF_OPEN_SOCKET_RETRY,那么plugin同樣會繼續(xù)作retry,而不是failover。如果剩余幾次retry結(jié)果都一樣(創(chuàng)建連接成功,但sendRequestHeader失?。?,客戶最終也會看到“No backend server available。日志通常如下:
          Tue Sep  1 10:43:41 2009 <10088125176582121> attempt #5 out of a max of 5
          Tue Sep  1 10:43:41 2009 <10088125176582121> created a new connection to preferred server '172.23.4.52/8001' for '/socs/favicon.ico', Local port:49416
          ......
          Tue Sep  1 10:43:41 2009 <10088125176582121> URL::sendHeaders(): meth='GET' file='/socs/favicon.ico' protocol='HTTP/1.1'
          ......
          Tue Sep  1 10:43:41 2009 <10088125176582121> readStatus: Local port of the socket 49416, connected to Remote Host/Port 172.23.4.52/8001
          Tue Sep  1 10:43:41 2009 <10088125176582121> readStatus: Response contains no data - isRecycled: 0
          Tue Sep  1 10:43:41 2009 <10088125176582121> *******Exception type [HALF_OPEN_SOCKET_RETRY] (Unexpected EOF reading HTTP status - request retry) raised at line 854 of ../nsapi/URL.cpp
          Tue Sep  1 10:43:41 2009 <10088125176582121> got exception in sendRequest phase: HALF_OPEN_SOCKET_RETRY: [os error=0, line 854 of ../nsapi/URL.cpp]: Unexpected EOF reading HTTP status - request retry at line 3080

                  對于1,這個可以理解,如果pool中connection被關(guān)閉(keep-alive超時)完了,客戶就不會碰到這樣的問題問題了。而對于2,更多的應(yīng)該說是OS的問題,OS沒有正確釋放端口。如果端口釋放成功,plugin做url.connect()的時候,應(yīng)該是收到connection refuse,而不是正常創(chuàng)建連接(實(shí)際上是無用連接,使用時出現(xiàn)Socket_Half_Open)。對于2,我們可以通過以下的方法檢查:
          1:ps -ef | grep java,檢查對應(yīng)的managed server時候shutdown
          2:netstat -a | grep listening port,如果server已經(jīng)shutdown了,卻還能看到listening port,那么就是OS的問題了。 

                寫了這么多,覺得比較繁瑣,不知道大家能否看得明白,建議最好拿一個wl_proxy的debug log,對照著看。
                 
          posted on 2009-09-14 08:54 走走停停又三年 閱讀(6652) 評論(5)  編輯  收藏 所屬分類: Weblogic

          FeedBack:
          # re: Weblogic Apache Plugin由HALF_OPEN_SOCKET_RETRY引起的“No backend server available”
          2009-09-14 21:16 | sorcerer
          一直沒搞明白,apache如何進(jìn)行心跳檢測?后端的appServer主動報告?hung的情況可以被識別為not available嗎?  回復(fù)  更多評論
            
          # re: Weblogic Apache Plugin由HALF_OPEN_SOCKET_RETRY引起的“No backend server available”
          2009-09-15 08:55 | 走走停停又三年
          對于無法連接的server,apache會做mark bad。過了max skip time后,這個server會被重新加入server list(無論這個server是否存活),然后嘗試創(chuàng)建到該server的連接,如果失敗,那么繼續(xù)將其mark bad。至于,hanging server的問題,參考WLIOTimeoutSecs。  回復(fù)  更多評論
            
          # re: Weblogic Apache Plugin由HALF_OPEN_SOCKET_RETRY引起的“No backend server available”
          2009-09-15 22:05 | sorcerer
          搜索WLIOTimeoutSecs居然可以搜到本帖了....
          既然Apache + Weblogic Cluster可以做load balance和failover那么是不是不需要lvs了?  回復(fù)  更多評論
            
          # re: Weblogic Apache Plugin由HALF_OPEN_SOCKET_RETRY引起的“No backend server available”
          2009-09-15 22:11 | 走走停停又三年
          @sorcerer

          你可以這么認(rèn)為,如果費(fèi)用不吃緊可以考慮lvs。failover是由weblogic的session replication實(shí)現(xiàn)的,這個是硬件層面無法做到的。  回復(fù)  更多評論
            
          # re: Weblogic Apache Plugin由HALF_OPEN_SOCKET_RETRY引起的“No backend server available”
          2009-09-15 23:32 | sorcerer
          還是有點(diǎn)迷惑,我們用了f5,session replication沒做,怕影響性能,但是系統(tǒng)都是sso的,應(yīng)該也不用重登錄.那么f5的主要功能似乎就是load balance了,apache既然可以做了,干嘛還要花這個錢呢.難道,只是因?yàn)閺S商吹噓的硬件更穩(wěn)定,性能更高?當(dāng)然配置起來確實(shí)方便.  回復(fù)  更多評論
            
          主站蜘蛛池模板: 田阳县| 奈曼旗| 富裕县| 陇川县| 孟州市| 临安市| 凤冈县| 怀宁县| 枣阳市| 天镇县| 泽州县| 建始县| 阳山县| 凌云县| 普安县| 疏附县| 嘉峪关市| 苗栗市| 澎湖县| 毕节市| 武汉市| 宿松县| 通州区| 雷波县| 蓬莱市| 枣强县| 贵州省| 敖汉旗| 灵川县| 克东县| 罗江县| 乌兰察布市| 玛纳斯县| 蓬安县| 深水埗区| 商丘市| 四川省| 策勒县| 射阳县| 会昌县| 襄樊市|