2006年10月10日

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

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

          問題分析

          不過在實(shí)際使用中, 還是發(fā)現(xiàn)按照最基本的方式調(diào)用 HttpClient 時(shí), 并不支持 UTF-8 編碼, 在網(wǎng)絡(luò)上找過一些文章, 也不得要領(lǐng), 于是查看了 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 請(qǐng)求的參數(shù)最終都會(huì)轉(zhuǎn)化為 RequestEntity 提交到 HTTP 服務(wù)器, 接著在 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 是如何依據(jù) "Content-Type" 獲得請(qǐng)求的編碼(字符集), 而這個(gè)編碼又是如何應(yīng)用到提交內(nèi)容的編碼過程中去的. 按照這個(gè)原來, 其實(shí)我們只需要重載 getRequestCharSet() 方法, 返回我們需要的編碼(字符集)名稱, 就可以解決 UTF-8 或者其它非默認(rèn)編碼提交 POST 請(qǐng)求時(shí)的亂碼問題了.

          測(cè)試

          首先在 Tomcat 的 ROOT WebApp 下部署一個(gè)頁面 test.jsp, 作為測(cè)試頁面, 主要代碼片段如下:
          <%@?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);
          %>


          接著寫一個(gè)測(cè)試類, 主要代碼如下:
          ????public?static?void?main(String[]?args)?throws?Exception,?IOException?{
          ????????String?url?=?"http://localhost:8080/test.jsp";
          ????????PostMethod?postMethod?=?new?UTF8PostMethod(url);
          ????????//填入各個(gè)表單域的值
          ????????NameValuePair[]?data?=?{
          ????????????????new?NameValuePair("TEXT",?"中文"),
          ????????};
          ????????//將表單的值放入postMethod中
          ????????postMethod.setRequestBody(data);
          ????????//執(zhí)行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";
          ????????}
          ????}


          運(yùn)行這個(gè)測(cè)試程序, 在 Tomcat 的后臺(tái)輸出中可以正確打印出 ">>>> The result is 中文" .

          代碼下載

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

          END


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


          初步設(shè)想

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

          環(huán)境準(zhǔn)備

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

          測(cè)試 jsp 文件的說明

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

          Apache 的配置

          編輯 Apache 的 httpd.conf 文件(如果使用 xampp-linux 的話, 應(yīng)該在 /opt/lampp/etc 目錄下), 在文件的最后加上如下內(nèi)容:
          ###############################################################################
          # Reverse Proxy and Load Balance ##############################################
          ###############################################################################
          # 1)簡(jiǎn)單的反向代理
          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>
          這個(gè)配置分為3個(gè)部分, 包括了 1)簡(jiǎn)單的反向代理, 2)非 session sticky 的 load balance, 以及 3)session sticky 的 load balance 三種方式的配置(這里假設(shè)兩個(gè) Tomcat 服務(wù)器的 HTTP 服務(wù)被配置在 8080 和 18080 端口), 其中第 2) 和 3) 的配置中 "nofailover=On" 適合于沒有 session 復(fù)制的情況下, 這種情況下, 如果其中一臺(tái) HTTP 服務(wù)器出錯(cuò), 那么原來分配在這個(gè)出錯(cuò)機(jī)器上的瀏覽器客戶端不會(huì)被自動(dòng)轉(zhuǎn)移到另外的服務(wù)器上, 必須重新啟動(dòng)瀏覽器才能將請(qǐng)求分配到另外一臺(tái)服務(wù)器上去.

          使用 JMeter 測(cè)試結(jié)果

          使用 JMeter 對(duì) "3)session sticky 的 load balance" 的效果進(jìn)行測(cè)試, 通過壓力測(cè)試的方式, 檢查兩臺(tái) Tomcat 服務(wù)器被分配到的請(qǐng)求數(shù)量, 相關(guān)的測(cè)試腳本參見文章最后的附件.

          注意如果重復(fù)測(cè)試, 在下一次測(cè)試開始之前請(qǐng)對(duì)每個(gè) Tomcat 服務(wù)器執(zhí)行 .../test.jsp?clear=1 的請(qǐng)求, 清除上一次的計(jì)數(shù)結(jié)果.

          從下圖的測(cè)試結(jié)果可見: 50個(gè)線程中有21個(gè)被分配在 8080 端口的服務(wù)器上, 29個(gè)則被分配到 18080 端口的服務(wù)器; 另外, 所有的 session 請(qǐng)求次數(shù)都是 20 次, 說明 session sticky 達(dá)到了預(yù)期的效果.

          附件


          后記

          如何禁用 XAMPP 自帶的內(nèi)容, 使之成為一個(gè)單純的轉(zhuǎn)發(fā)服務(wù)器:
          • 1)注釋掉 /opt/lampp/etc/httpd.conf 中 "Include etc/extra/httpd-xampp.conf" 這一行;
          • 2)刪除或者移走 /opt/lampp/htdocs 目錄下的內(nèi)容(但是此目錄需要保留).
          posted @ 2006-10-10 13:12 thinkbase.net 閱讀(7069) | 評(píng)論 (7)編輯 收藏
           
          主站蜘蛛池模板: 三江| 长兴县| 桑植县| 滨海县| 饶阳县| 类乌齐县| 沙洋县| 舟山市| 焦作市| 潼关县| 平凉市| 乌鲁木齐市| 惠州市| 绵阳市| 昭苏县| 瓮安县| 双流县| 大余县| 东港市| 兴安盟| 固阳县| 佛坪县| 红河县| 利辛县| 淳化县| 永顺县| 正蓝旗| 贵港市| 泰宁县| 千阳县| 丹阳市| 宜良县| 临西县| 广宁县| 女性| 偃师市| 安西县| 读书| 长葛市| 长寿区| 贵港市|