top / index / prev / next / target / source

2005-07-28 diary: Java RMI 体験。RMIは速かった… , Java Servletの仕組み上のフットプリント

いがぴょんの日記 日記形式でつづる いがぴょんコラム ウェブページです。

old-v2

Java RMI 体験。RMIは速かった… , Java Servletの仕組み上のフットプリント

RMIは SOAP(Axis 1.2)に比べて 実行時動作が高速であるということを体感しました。 , Java Servletの仕組みそのものに時間的なフットプリントが存在するようです。

Java RMI 体験中

Webサービスと Java RMIとの速度比較を実施したいので、まずは Java RMIを体験しました。

サーバプログラムのインタフェースは下記になります。 KantanHello.java

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface KantanHello extends Remote {
    String hello(String arg) throws RemoteException;
}

サーバプログラムの実装は下記のようになります。 KantanHelloImpl.java

import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class KantanHelloImpl extends UnicastRemoteObject implements KantanHello {
    public static void main(String[] args) {
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }
        try {
            KantanHelloImpl impl = new KantanHelloImpl();
            Naming.rebind("KantanHello", impl);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    protected KantanHelloImpl() throws RemoteException {
        super();
    }

    public String hello(String arg) throws RemoteException {
        return "こんにちは[" + arg + "]";
    }
}

KantanHelloImpl.classファイルが存在するディレクトリで、下記コマンドを実行します。 rmic KantanHelloImpl この結果 KantanHelloImpl_Stub.class ファイルが生成されます。 RMIサーバを起動するために、rmiregistry を実行します。

rmiregistry

ネットワーク接続を許容させるために、下記のようにセキュリティjava.policyファイルを整備します。 java.policy

grant {
    permission java.security.AllPermission;
};

サーバを実行します。

java -Djava.security.policy=java.policy -Djava.rmi.server.codebase=file:///C:\eclipse\eclipse\workspace\testRmi\bin\ KantanHelloImpl

※codebase指定のディレクトリに 最後にディレクトリのデリミタをつけるというのがとても重要です。付けないと動作しません。 単純に呼び出すだけのクライアントプログラムは下記のようになります。 TestCaller.java

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class TestCaller {
    public static void main(String[] args) {
        KantanHello hello = null;
        try {
            System.setSecurityManager(new RMISecurityManager());
            hello = (KantanHello) Naming.lookup("rmi://localhost/KantanHello");
            System.out.println(hello.hello("こんにちは"));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

クライアントを実行します。

java -Djava.security.policy=java.policy TestCaller

※セキュリティポリシをセットしなおさないと通信できない点に注意!。Eclipse 3.0上からは単純には設定&実行できませんでした。 * セキュリティが適切に設定できず、適切に実行できなかった場合には、下記のような例外が発生します。 java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1099 connect,resolve)

Java RMI の速度感…

上記の単純な呼び出しプログラムに繰り返し型実行+所要時間サンプリング機能をつけて実行を行います。 TestCaller.java

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class TestCaller {
    private static final int COUNT = 1000;

    public static void main(String[] args) {
        KantanHello hello = null;
        try {
            long start = System.currentTimeMillis();
            System.setSecurityManager(new RMISecurityManager());
            hello = (KantanHello) Naming.lookup("rmi://localhost/KantanHello");
            System.out.println(hello.hello("こんにちは"));

            long end = System.currentTimeMillis();
            System.out.println("初回呼出の所用時間:" + (end - start) + "ミリ秒");

            start = System.currentTimeMillis();
            for (int index = 0; index < COUNT; index++) {
                String result = hello.hello("疎通試験");
            }
            end = System.currentTimeMillis();
            System.out.println("" + COUNT + "件処理するための所用時間:" + (end - start)
                    + "ミリ秒");
            System.out.println("1件あたり:" + (end - start) / COUNT + "ミリ秒");

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

呼出回数初回呼出の処理時間1件あたり処理時間1 270ミリ秒 0ミリ秒 10 290ミリ秒 1ミリ秒 100 270ミリ秒 0.9ミリ秒 1000 260ミリ秒 0.5ミリ秒

Webサービスと所要時間のオーダーが違います。なんてことだ… (苦笑)

関連する日記

生Servletの実行コスト

Webサービスとの比較に際して、Java Servletという仕組みそのものの実行時コストが存在するはずなので、それを簡単に計測しました。 KantanHelloServlet.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class KantanHelloServlet extends HttpServlet {

    public static final String ENCODING = "ISO-2022-JP";

    public final void service(final HttpServletRequest request,
            final HttpServletResponse response) throws ServletException,
            IOException {

        BufferedReader reader = null;
        PrintWriter writer = null;
        setResponseDefault(response);

        try {
            reader = request.getReader();
            writer = response.getWriter();

            for (;;) {
                String line = reader.readLine();
                if (line == null) {
                    break;
                }
                System.out.println("REQ:" + line);
            }
            writer
                    .write("<HEAD>\n"
                            + "<META http-equiv=\"Content-Type\" content=\"text/html; charset="
                            + "\""
                            + ENCODING
                            + "\">\n"
                            + "<META http-equiv=\"Content-Style-Type\" content=\"text/css\">\n"
                            + "<META http-equiv=\"Cache-Control\" content=\"no-cache\">\n"
                            + "<META http-equiv=\"Pragma\" content=\"no-cache\">\n"
                            + "<META http-equiv=\"Expires\" content=\"0\">\n"
                            + "<TITLE>簡単な こんにちはサーブレットです</TITLE>\n</HEAD>\n");
            writer.write("<BODY>\n");
            writer.write("<P>こんにちはサーブレット</P>\n");

            for (Enumeration enum = request.getParameterNames(); enum
                    .hasMoreElements();) {
                String key = (String) enum.nextElement();
                String value = new String(((String) request.getParameter(key))
                        .getBytes("ISO8859-1"), ENCODING);
                writer.write("<LI>P:" + key + "=" + value + "</LI>\n");
            }

            writer.write("<FORM action=\"KantanHelloServlet\">\n");
            writer
                    .write("<INPUT type=\"text\" name=\"INPUTVALUE\" size=\"20\">\n");
            writer
                    .write("<INPUT type=\"submit\" value=\"実行\" name=\"execute\">\n");
            writer.write("</FORM>\n");
            writer.write("</BODY>\n");

        } finally {
        }
    }

    /**
     * デフォルトとなるレスポンスヘッダーの付与
     * 
     * @param response
     *            レスポンスオブジェクトを与えます。
     */
    private void setResponseDefault(final HttpServletResponse response) {
        response.setContentType("text/html; charset=" + ENCODING);
        response.addHeader("Content-Type", "text/html");
        response.addHeader("charset", ENCODING);
        response.addHeader("Cache-Control", "no-cache");
        response.addHeader("Pragma", "no-cache");
        response.addHeader("Expires", "0");
    }
}

このサーブレットを HttpClientを用いて接続します。※このサンプルでは レスポンス電文を解析していません。他のサンプルとは異なり、単に 標準出力に表示しているだけである点には注目する必要があります。 TestCaller.java

import java.io.IOException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

public class TestCaller {
    private static final int COUNT = 100;

    private static void callMethod(boolean isSysout) {
        HttpClient client = new HttpClient();
        client.getHostConfiguration().setHost("localhost", 8080, "http");

        PostMethod method = new PostMethod("/examples/KantanHelloServlet");
        NameValuePair[] parameters = new NameValuePair[2];
        parameters[0] = new NameValuePair("execute", "実行");
        parameters[1] = new NameValuePair("INPUTVALUE", "こんにちは");
        method.setRequestBody(parameters);
        try {
            client.executeMethod(method);
            String result = method.getResponseBodyAsString();
            if (isSysout) {
                System.out.println(result);
            }
        } catch (HttpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        method.releaseConnection();
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        callMethod(true);
        long end = System.currentTimeMillis();
        System.out.println("初回呼出の所用時間:" + (end - start) + "ミリ秒");

        start = System.currentTimeMillis();
        for (int index = 0; index < COUNT; index++) {
            callMethod(false);
        }
        end = System.currentTimeMillis();
        System.out
                .println("" + COUNT + "件処理するための所用時間:" + (end - start) + "ミリ秒");
        System.out.println("1件あたり:" + (end - start) / COUNT + "ミリ秒");
    }
}

※必要なライブラリ * commons-httpclient-2.0.2.jar

数ミリ秒のレベルにおいて、Java Servletという仕組みそのものによるフットプリントが存在しているようです。Java RMIとの間の所要時間の溝の仕組みが分かり、ちょっと安心しました (苦笑)

番外編: Apache Axis2 0.9

Axis2で試そうと思ったのですが、私には理解不能だったので断念しました。

番外編: ActiveSOAP

高速なSOAP実装ということで、ふと ActiveSOAPというプロダクトを連想しました。

なぜ ActiveSOAPを思い出したのだろう、と考えたら 下記のニュースで用語が登場していたからでした

Webサービスの存在上の宿命

Webサービスは 下記の存在上の宿命をしょっています。

このために、Java RMIのような実装に比べると、どうしても動作速度は遅くなりがち、、、なのかも知れません...…とりあえず DOMベースではなくって SAXベースで実装されたSOAP実装でないと、どうしても速度が出ないような気がします…

、、、ああ、Axisは 処理の途中で DOMを利用しているから、データ件数が増えた際に指数的な速度低下を発生する可能性があります。気づいて、がっくりしました。

疲れました…

どどどっと調べ物をしていたら、がくっと疲れました。おまけに事務所がとても寒いです (T_T)今日は(も) 早めに帰社します。

世間のニュースから


この日記について