2006年10月10日

          原文來自: HttpClient POST 的 UTF-8 編碼問題

          Apache HttpClient ( http://jakarta.apache.org/commons/httpclient/ ) 是一個純 Java 的HTTP 協議的客戶端編程工具包, 對 HTTP 協議的支持相當全面, 更多細節也可以參考IBM 網站上的這篇文章 HttpClient入門 ( http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/ ).

          問題分析

          不過在實際使用中, 還是發現按照最基本的方式調用 HttpClient 時, 并不支持 UTF-8 編碼, 在網絡上找過一些文章, 也不得要領, 于是查看了 commons-httpclient-3.0.1 的一些代碼, 首先在 PostMethod 中找到了 generateRequestEntity() 方法:
          ????/**
          ?????*?Generates?a?request?entity?from?the?post?parameters,?if?present.??Calls
          ?????*?{@link?EntityEnclosingMethod#generateRequestBody()}?if?parameters?have?not?been?set.
          ?????*?
          ?????*?@since?3.0
          ?????*/
          ????protected?RequestEntity?generateRequestEntity()?{
          ????????if?(!this.params.isEmpty())?{
          ????????????//?Use?a?ByteArrayRequestEntity?instead?of?a?StringRequestEntity.
          ????????????//?This?is?to?avoid?potential?encoding?issues.??Form?url?encoded?strings
          ????????????//?are?ASCII?by?definition?but?the?content?type?may?not?be.??Treating?the?content
          ????????????//?as?bytes?allows?us?to?keep?the?current?charset?without?worrying?about?how
          ????????????//?this?charset?will?effect?the?encoding?of?the?form?url?encoded?string.
          ????????????String?content?=?EncodingUtil.formUrlEncode(getParameters(),?getRequestCharSet());
          ????????????ByteArrayRequestEntity?entity?=?new?ByteArrayRequestEntity(
          ????????????????EncodingUtil.getAsciiBytes(content),
          ????????????????FORM_URL_ENCODED_CONTENT_TYPE
          ????????????);
          ????????????return?entity;
          ????????}?else?{
          ????????????return?super.generateRequestEntity();
          ????????}
          ????}

          原來使用 NameValuePair 加入的 HTTP 請求的參數最終都會轉化為 RequestEntity 提交到 HTTP 服務器, 接著在 PostMethod 的父類 EntityEnclosingMethod 中找到了如下的代碼:
          ????/**
          ?????*?Returns?the?request's?charset.??The?charset?is?parsed?from?the?request?entity's?
          ?????*?content?type,?unless?the?content?type?header?has?been?set?manually.?
          ?????*?
          ?????*?@see?RequestEntity#getContentType()
          ?????*?
          ?????*?@since?3.0
          ?????*/
          ????public?String?getRequestCharSet()?{
          ????????if?(getRequestHeader("Content-Type")?==?null)?{
          ????????????//?check?the?content?type?from?request?entity
          ????????????//?We?can't?call?getRequestEntity()?since?it?will?probably?call
          ????????????//?this?method.
          ????????????if?(this.requestEntity?!=?null)?{
          ????????????????return?getContentCharSet(
          ????????????????????new?Header("Content-Type",?requestEntity.getContentType()));
          ????????????}?else?{
          ????????????????return?super.getRequestCharSet();
          ????????????}
          ????????}?else?{
          ????????????return?super.getRequestCharSet();
          ????????}
          ????}


          解決方案

          從上面兩段代碼可以看出是 HttpClient 是如何依據 "Content-Type" 獲得請求的編碼(字符集), 而這個編碼又是如何應用到提交內容的編碼過程中去的. 按照這個原來, 其實我們只需要重載 getRequestCharSet() 方法, 返回我們需要的編碼(字符集)名稱, 就可以解決 UTF-8 或者其它非默認編碼提交 POST 請求時的亂碼問題了.

          測試

          首先在 Tomcat 的 ROOT WebApp 下部署一個頁面 test.jsp, 作為測試頁面, 主要代碼片段如下:
          <%@?page?contentType="text/html;charset=UTF-8"%>
          <%@?page?session="false"?%>
          <%
          request.setCharacterEncoding("UTF-8");
          String?val?=?request.getParameter("TEXT");
          System.out.println(">>>>?The?result?is?"?+?val);
          %>


          接著寫一個測試類, 主要代碼如下:
          ????public?static?void?main(String[]?args)?throws?Exception,?IOException?{
          ????????String?url?=?"http://localhost:8080/test.jsp";
          ????????PostMethod?postMethod?=?new?UTF8PostMethod(url);
          ????????//填入各個表單域的值
          ????????NameValuePair[]?data?=?{
          ????????????????new?NameValuePair("TEXT",?"中文"),
          ????????};
          ????????//將表單的值放入postMethod中
          ????????postMethod.setRequestBody(data);
          ????????//執行postMethod
          ????????HttpClient?httpClient?=?new?HttpClient();
          ????????httpClient.executeMethod(postMethod);
          ????}
          ????
          ????//Inner?class?for?UTF-8?support
          ????public?static?class?UTF8PostMethod?extends?PostMethod{
          ????????public?UTF8PostMethod(String?url){
          ????????????super(url);
          ????????}
          ????????@Override
          ????????public?String?getRequestCharSet()?{
          ????????????//return?super.getRequestCharSet();
          ????????????return?"UTF-8";
          ????????}
          ????}


          運行這個測試程序, 在 Tomcat 的后臺輸出中可以正確打印出 ">>>> The result is 中文" .

          代碼下載

          本文所提到的所有代碼, 以及測試程序(可直接導入 eclipse)提供打包下載: att:HttpClient POST 的 UTF-8 編碼問題.httpClientUTF8.tar.bz2

          END


          posted @ 2006-10-31 23:00 thinkbase.net 閱讀(7722) | 評論 (8)編輯 收藏
           
          原文來自:? 使用 Apache 反向代理實現負載均衡及熱備


          初步設想

          • 早些時候在 JavaEye 上看到過一些使用 lighttpd 或者 apache 作前端, 通過負載均衡, 實現高性能的 Web 系統的討論, 于是留意了一下這方面的技術;
          • 考慮到對不同的 App Server 而言, 實現 Session 復制的配置各不相同(通常是需要配置集群), 因此從通用的角度, 覺得使用 session sticky 方式實現的負載均衡比較方便;
          • 由于沒有看到有資料說 lighttpd 能夠實現 session sticky, 所以決定先使用 Apache 試試.
          參考資料:

          環境準備

          • 下載安裝 Apache, 測試時使用的是 XAMPP ( http://www.apachefriends.org/en/xampp.html ) 的 Linux 版本 (xampp-linux-1.5.4.tar.gz), 按照安裝說明, 解壓到 /opt/lampp 目錄下就可以使用了;
            • 啟動 Apache: sudo /opt/lampp/lampp startapache
            • 重新加載 Apache: sudo /opt/lampp/lampp reloadapache (在 httpd.conf 文件被修改后可以不重啟, 而是直接 reload 就可以了)
            • 停止服務: sudo /opt/lampp/lampp stop
          • 準備兩個運行同樣程序的 Web 服務器, 這里使用的是 Tomcat 5.5, 并使用一個 jsp 文件作為測試文件(相關源代碼參見文章最后的附件);
            • 這兩個 Tomcat 服務器需要將 HTTP 服務配置在不同的端口上, 同時由于測試時運行在同一臺機器上, 其它端口也需要避免沖突;
          • 下載安裝 JMeter ( jakarta-jmeter-2.2), 用于壓力測試, 驗證負載均衡的效果;

          測試 jsp 文件的說明

          測試用的 jsp 文件 (test.jsp) 具有如下功能:
          • 顯示當前運行的服務器的 IP 地址及端口號, 這樣從返回的頁面就能夠知道是運行在哪一個 Web 服務器上的了;
          • 統計每個客戶端(不同的 session)向同一臺服務器發出請求的次數, 通過這個計數可以驗證是否實現了 session sticky;
          • 通過 clear 請求參數(即 .../test.jsp?clear=1)清除請求次數的計數結果, 以便進行下一次測試;
          • 模擬 JSESSIONID +jvmRoute 的機制, 自行實現了一個 STICK_PORT_TOKEN 的 Cookie, 直接使用不同服務器的 HTTP 端口號作為 route;
            • 說明1: 考慮到方案的通用性, 這里沒有直接使用 JSESSIONID +jvmRoute 的機制;
            • 說明2: 雖然作為一個例子, 相關代碼是寫死在 jsp 文件中的, 但是這個機制可以很方便的用一個 Filter 統一實現;

          Apache 的配置

          編輯 Apache 的 httpd.conf 文件(如果使用 xampp-linux 的話, 應該在 /opt/lampp/etc 目錄下), 在文件的最后加上如下內容:
          ###############################################################################
          # Reverse Proxy and Load Balance ##############################################
          ###############################################################################
          # 1)簡單的反向代理
          ProxyRequests Off
          <Proxy *>
          Order deny,allow
          Allow from all
          </Proxy>
          ProxyPass /1 http://localhost:8080/test
          #ProxyPassReverse /1 http://localhost:8080/test
          ProxyPass /2 http://localhost:18080/test
          #ProxyPassReverse /2 http://localhost:18080/test
          # 2)非 stickysession 的 balance
          ProxyPass /3 balancer://non-sticky-cluster nofailover=On
          <Proxy balancer://non-sticky-cluster>
          BalancerMember http://localhost:8080/test
          BalancerMember http://localhost:18080/test smax=10
          </Proxy>
          # 3)stickysession 的 balance
          ProxyPass /4 balancer://sticky-cluster stickysession=STICK_PORT_TOKEN nofailover=On
          <Proxy balancer://sticky-cluster>
          BalancerMember http://localhost:8080/test route=8080
          BalancerMember http://localhost:18080/test route=18080 loadfactor=2
          </Proxy>
          這個配置分為3個部分, 包括了 1)簡單的反向代理, 2)非 session sticky 的 load balance, 以及 3)session sticky 的 load balance 三種方式的配置(這里假設兩個 Tomcat 服務器的 HTTP 服務被配置在 8080 和 18080 端口), 其中第 2) 和 3) 的配置中 "nofailover=On" 適合于沒有 session 復制的情況下, 這種情況下, 如果其中一臺 HTTP 服務器出錯, 那么原來分配在這個出錯機器上的瀏覽器客戶端不會被自動轉移到另外的服務器上, 必須重新啟動瀏覽器才能將請求分配到另外一臺服務器上去.

          使用 JMeter 測試結果

          使用 JMeter 對 "3)session sticky 的 load balance" 的效果進行測試, 通過壓力測試的方式, 檢查兩臺 Tomcat 服務器被分配到的請求數量, 相關的測試腳本參見文章最后的附件.

          注意如果重復測試, 在下一次測試開始之前請對每個 Tomcat 服務器執行 .../test.jsp?clear=1 的請求, 清除上一次的計數結果.

          從下圖的測試結果可見: 50個線程中有21個被分配在 8080 端口的服務器上, 29個則被分配到 18080 端口的服務器; 另外, 所有的 session 請求次數都是 20 次, 說明 session sticky 達到了預期的效果.

          附件


          后記

          如何禁用 XAMPP 自帶的內容, 使之成為一個單純的轉發服務器:
          • 1)注釋掉 /opt/lampp/etc/httpd.conf 中 "Include etc/extra/httpd-xampp.conf" 這一行;
          • 2)刪除或者移走 /opt/lampp/htdocs 目錄下的內容(但是此目錄需要保留).
          posted @ 2006-10-10 13:12 thinkbase.net 閱讀(7062) | 評論 (7)編輯 收藏
           
          主站蜘蛛池模板: 邯郸县| 延庆县| 来安县| 台州市| 遂川县| 林周县| 东丰县| 罗甸县| 平泉县| 沂水县| 肥城市| 马关县| 翁牛特旗| 辽源市| 镇平县| 攀枝花市| 凤翔县| 荆门市| 临清市| 新余市| 太仓市| 陕西省| 射洪县| 渝中区| 正阳县| 桃园县| 龙门县| 海丰县| 法库县| 遂溪县| 台中市| 含山县| 弥勒县| SHOW| 黎川县| 宁津县| 竹山县| 恩施市| 新田县| 陆河县| 上高县|