本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

SOAPを搾る

GZIP対応のApache Axis

Brian Goodman (bgoodman@us.ibm.com), Software Engineer, IBM Advanced Internet Technology
Brian D. Goodman氏は、必要なときに必要なものを手に入れることを重視し、そのための努力を惜しまないタイプの人間です。Brianの連絡先は、bgoodman@us.ibm.com です。いつでも質問、コメント、提案などをお寄せください。

概要: HTTP上でのGZIPエンコーディングは使い古された方法です。ほとんどのユーザーにとって、それは「うんざりするほど経験した」方法に思えるでしょう。しかし、現在のSOAP実装をいくつか経験したことのあるユーザーなら、これらの実装でGZIPが有効に活用されていないことに気づくはずです。最終的にはSOAPを実装することを認識しながらWebサービス・ソリューションを構築中であり、パフォーマンスの大幅な向上を求めているユーザーにとって、GZIPはまさに最適な選択肢です。

日付:  2003年 3月 01日
レベル:  初級 この記事の原文:  英語
アクティビティー: 1850 ビュー
お気軽にご意見・ご感想をお寄せください: 


HTTPトランスポート上でのGZIPエンコーディングは、Webアプリケーションのパフォーマンスを向上させるよく知られた手法です。トラフィックの多いWebサイトでは、ユーザーへの応答を高速化するためにGZIPが使用されています。また圧縮は、ファイルのダウンロードや交換のためにファイルのサイズをより小さくする目的で広く使用されています。実際、XMLに関して言えば、GZIPの使用は最新の素晴らしい手法というわけでもありません。ATTのXMillなどの新しいテクノロジーでは、ほぼ同じ時間でZGIPの2倍の圧縮が可能だとされています。しかし、GZIPはJavaプラットフォームの中核的なコンポーネントであり、多くのWebサーバーは自身が提供するファイルやアプリケーションに関係なくコンテンツを圧縮することができます。このような理由から、この記事では、GZIPをAxis SOAPの実装と組み合わせて使用する方法について考察します。Axis SOAPとGZIPの組み合わせは、次のようなプロジェクトやソリューションに役立つと証明されています。すなわち、現在さらに高いパフォーマンスを必要とし、将来的に今後リリースされるSOAP実装と時間をかけて統合する予定のあるものです。さらに、この記事では、サーブレット・レベルでのエンコーディングについても考察します。これにより、別のコンテンツのエンコーディング・スキーム (符号化手法) を実装することも可能になります。

はじめに

これから説明する変更を実行するには、Apache Axisのソース・コードが必要です。しかし、ここで紹介する変更は、これ以外のSOAP実装に対してもほぼ同じように実行できるはずです。ここでは、基本となるHTTPSenderおよびAxisServletのコードを変更します。使用するサーバーが自動的に圧縮を処理する場合、サーブレットは不要ですが、サーブレットを変更することで、別のエンコーディング・ソリューションを組み込むためのフレームワークが用意されます。

ここでの目標は、基本となるSOAP実装を機能面で変更することなく、GZIPによる圧縮機能を追加することです。java.util.zipはストリームを基にしていますので、これは比較的簡単です。また、Axisやその他のソリューションでHTTP接続を作成する基本的なコードもストリームを基にしています。


JavaテクノロジーとGZIPの概要

この記事では、java.util.zipパッケージの使用について詳細に説明することはしません。このパッケージの内容に関しては、Sunを始めとして、その他の多くの場所で提供されています (参考文献を参照してください)。

java.util.zipパッケージには、他のストリームをエンコードするために必要なすべてのものが用意されています。具体的には、GZIPInputStream およびGZIPOutputStream オブジェクトを使用します。どちらのオブジェクトもストリームを引数に取ります。コンテンツのエンコーディングを確認できれば、ストリームを適切にラップすることができます。圧縮と解凍の複雑な処理自体は、これらのオブジェクトが実行します。もちろん別のフォーマットでエンコードとデコードを実行する場合は、入出力ストリームを展開してください。

サーバー側では、2つの方法でコンテンツのエンコーディングを処理できます。第1の、そして望ましい方法は、アプリケーション・サーバーの前面にあるWebサーバーで圧縮を処理することです。Webサーバーを構築し、圧縮されたコンテンツのエンコーディングをサポートするクライアントを識別し、要求された出力コンテンツをそのフォーマットにエンコードすることができます。Webサーバーでは、この動作のほとんどが動的に実行されるため、現在あるハードウェアでこれを実行すると、わずかながらオーバーヘッドが生じます。第2の方法は、アプリケーション自身でコンテンツをエンコードすることです。この方法は、パフォーマンス、必要なコード量、および必要なメンテナンスにおいて、最初の方法よりも高くつきます。しかしCGIやサーブレットを使用して、クライアントが受け付けるエンコーディングの種類を簡単に判別し、適切にエンコードされた応答を返すことができます。この方法は、Webサーバーで一般に使用されていない種類のエンコーディングを使用している場合に便利です。最終的には、負荷はネットワークからサーバーおよびクライアントへ移っていきます。しかし、約半分の量にしたデータをネットワークで転送することによって得られるパフォーマンスがこの負荷を補うほどのものであるかどうかについては、まだ議論の余地があります。

とにかく、目標は透過的な処理の実現、すなわち、入出力ストリームをインターセプトして、適切なGZIPストリームに送り込むことです。いたって単純な目標です。それではさっそく始めることにしましょう。


サーバー側の改造: AxisServletの変更

このセクションでは、AxisServletを変更して圧縮を可能にする方法を説明します。HTTPサーバーの設定にアクセスできない場合や、サーバーが圧縮をサポートしない場合には、この方法を使用する必要があります。この技術は、異なる方式のエンコーディングを可能にする最初のステップにもなります。

Webサーバー層ではなく、アプリケーション層でエンコーディングを実行するのは非常に簡単です。この方法は、一般に使用されているサーバーやモジュールではサポートされていないエンコーディング方式を使用する計画がある場合に特に便利です。この記事で紹介するすべてのコード変更と同様に、これらの変更もいくつかの方法で実装できます。たとえば、AxisServletを継承してHTTPServletResponse を実装することにより、出力ストリームをインターセプトできます。

まず最初にAccept-Encoding ヘッダーを調べるため、doPostメソッドを変更します。クライアントがGZIPエンコーディングを受け付けると示していれば、それを信頼することにします。ユーザー・エージェント・ヘッダーを確認し、圧縮を処理できるクライアントに対してのみ圧縮が実行されるようにしてください。ただし、ほとんどのブラウザーはRPCRouterを使用しないため、これを気にする必要は極めて低いはずです。クライアントがGZIPエンコーディングを受け付ける場合は、GZIPサポートを示すBooleanをtrueに設定します。リスト1 のコードを確認してください。


リスト1. AxisServlet(doPost)
                
String encoding = req.getHeader("Accept-Encoding"); 
if (encoding != null) {
if (encoding.toLowerCase().indexOf("gzip") > -1){supportsGzip = true;}
}

次にsendResponseメソッドへ移ります。現在の接続がGZIPをサポートしているかどうかを確認するためのコードをいくつか追加します。GZIPがサポートされている場合は、応答ヘッダーContent-Encoding をgzipに設定し、応答の出力ストリームをGZIP出力ストリームでラップします。これ以降は、出力ストリームをフラッシュする必要があることや、ストリーム・バッファーの最後のビットが送信されない可能性があることを除けば、すべて元と同じようなコーディングになります。この修正により、呼び出している根本的なWebサービスが何であるかを問わず、GZIPエンコーディングが可能になります。このことから考えて、特殊なコンテンツ・エンコーディングを有効にすることも非常に簡単なはずです。リスト2 のコードを参照してください。


リスト2. AxisServlet(sendResponse)
                
if (supportsGzip == true) {

// Set the response header.
// Note that response is an HttpResponse instance.

        res.setHeader("Content-Encoding", "gzip");
    GZIPOutputStream gzos = new GZIPOutputStream(res.getOutputStream());
            responseMsg.writeTo(gzos);
        gzos.flush();
        gzos.close();
            //gzip code end             
} else {
            	
        res.setContentType(contentType);
        /* My understand of Content-Length
         * HTTP 1.0
         *   -Required for requests, but optional for responses.
         * HTTP 1.1
         *  - Either Content-Length or HTTP Chunking is required.
         *   Most servlet engines will do chunking if content-length is not specified.
         */
                responseMsg.writeTo(res.getOutputStream());         
}


クライアント側の改造: HTTPSenderの変更

Axisのソース・コードを見慣れていないユーザーがコードを見て気付くことは、単に他のHTTPクライアント・オブジェクトを継承しているだけではないということだと思います。コードでは、実際にはHTTPクライアントがソケット・レベルで独自に実装されています。このようにする理由はさまざまですが、明白な理由の1つは、私がこれから説明するような変更を行うためです。ここではwriteToSocketとreadFromSocketの2つのメソッドに注目します。

GZIP対応の通知

最初にすべきことは、クライアントを変更して、そのクライアントが圧縮を処理できることをサーバーに示すことです。ここで注意しなければならないのは、すべてのWebクライアントが、自身が宣言したとおりにエンコーディングを処理できるとは限らないということです。サーバーが使用するエンコーディング規則を検証できない場合は、アプリケーション・レベルでエンコーディングを行うサーブレットを用いて、エンコーディング規則を検査することをお勧めします。サーバーが特定のユーザー・クライアントに対応するよう設定されている可能性は十分にあります。その場合、別途エンコーディングを用意すると重複した作業になってしまします。アクセスが可能であれば、サーバーが目的のユーザー・エージェントに対してエンコーディングを提供しているかどうかを確認してください。ログやSOAPのトレース情報を簡単に参照することによって、ユーザー・エージェント・ヘッダーが設定されているかどうかを確認できると思います。問題を簡単にするためにも、そうであってほしいところです。とにかく、この件については読者におまかせするしかありません。

ここでは、ヘッダーをAccept-Encoding に設定します。このHTTPヘッダーは、文字列値 (多くの場合値のリスト) を格納します。リスト3 をご覧ください。ここでは値gzipを使用しています。ヘッダーを追加する場合は、HTTPSenderのコードを変更する必要があります。ここでヘッダーが処理されます。ざっと見ただけで、ヘッダーを追加するための枠組みがあることがわかると思います。ただし、この時点で処理されるヘッダーはAccept-Encoding ではありません。この記事では、エンコーディングをハード・コーディングしておきます。HTTPヘッダーの組み込みと設定の方法は、読者が決定してください。これには、多くの方法があります。ここでは、リスト4 のようなわずかなコードを挿入します。要求ヘッダーを取り出して、ハッシュ・テーブルにキャストしています。ここでは、サーバー側での圧縮を開始させるヘッダーを追加します。圧縮は、Webサーバーとアプリケーション・サーバーのどちらかで行われるものとします。


リスト3. インターネット・エクスプローラーからwww.ibm.comへのHTTP GETのサンプル
                
GET / HTTP/1.1
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)
Host: www.ibm.com:80
Connection: Keep-Alive


リスト4. HTTPSender.java (writeToSocket) のコードの一部
                
//process user defined headers for information.
Hashtable userHeaderTable = 
(Hashtable) msgContext.getProperty(HTTPConstants.REQUEST_HEADERS);
                
//Adding Accept-Encoding header to the hashtable.
if(userHeaderTable == null){userHeaderTable = 
new Hashtable();} userHeaderTable.put("Accept-Encoding", "gzip");

要求したものを取得する

gzipという値を持つAccept-Encoding ヘッダーを組み込むようにHTTP要求を変更したら、次はそのコンテンツの読み取りに使用するメソッドを変更する必要があります。また、HTTPヘッダーのコンテンツ・エンコーディングを確認して、サーバーがGZIPで応答したかどうかを確認する必要もあります。サーバーがGZIPで応答していれば、入力ストリームをGZIP 入力ストリームでラップします。GZIPエンコーディングが検出されない場合は、入力ストリームはそのままにします。Axis開発者が使用している現在のプロセスを妨げないようにするためにも、このコードを配置しておくことは重要です。入力ストリームは、HTTPメッセージの本文がデコードされる直前、かつヘッダーが解析された直後に変更する必要があります。ソケットをラップする前にコンテンツのエンコーディングを知る必要があることを忘れないでください。リスト5 のコードは、readFromSocketメソッドのどこにGZIPのコードが挿入されているかを示しています。このコードでInputStream が宣言されていることに注目してください。この変数は、GZIPInputStream か、パラメーターで渡されるInputStream のいずれかの型になります。この時点から、Axisは、圧縮されたストリームから読み取りを行っているのかどうか、それを読み取るべきかどうかを認識しなくなります。これで、根底にあるコードを意識することなくGZIPエンコーディングの要求と処理を実行するAxisクライアントができました。


リスト5. HTTPSender.java (readFromSocket) のコードの一部
                
if (null != transferEncoding && 
transferEncoding.trim().equals(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED)) {
	inp = new ChunkedInputStream(inp);
}

//Check the content encoding. If it is gzip then
//wrapper the input stream as a GZIPInputStream

   
String zip = (String)headers.get("content-encoding");
InputStream is = null;
if(zip.indexOf("gzip") != -1){
	GZIPInputStream zipIn = new GZIPInputStream(inp);
	is = zipIn;
} else {
      	is = inp;
}
//end gzip code
outMsg = 
new Message( new SocketInputStream(is, sock), false, contentType, contentLocation);


まとめ

HTTP上のGZIPエンコーディングは、ご存知のとおりWebテクノロジーの一部です。次のステップとしてGZIPエンコーディングを既存のWebサービス・フレームワークで使用することは、理にかなった努力です。しかし多くのソリューションが日々SOAPの実装上で設計、構築、および開発されています。SOAPエンベロープでGZIPエンコードを可能にすることにより、結果的には比較的少ないオーバーヘッドでトランザクションを高速化できるというケースがたくさんあります。このパフォーマンスの向上は、いくつかの簡単なコードの変更によって、今すぐ実現することができます。現在のSOAP環境でGZIPエンコーディングを有効にすることにより、望ましい実装形態への統合を待つこの瞬間にも、圧縮のメリットを享受することができるのです。


参考文献

著者について

Brian D. Goodman氏は、必要なときに必要なものを手に入れることを重視し、そのための努力を惜しまないタイプの人間です。Brianの連絡先は、bgoodman@us.ibm.com です。いつでも質問、コメント、提案などをお寄せください。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and Web services
ArticleID=244785
ArticleTitle=SOAPを搾る
publish-date=03012003
author1-email=bgoodman@us.ibm.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。