top / index / prev / next / target / source
日記形式でつづる いがぴょんコラム ウェブページです。
RMIは SOAP(Axis 1.2)に比べて 実行時動作が高速であるということを体感しました。 , Java Servletの仕組みそのものに時間的なフットプリントが存在するようです。
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)
上記の単純な呼び出しプログラムに繰り返し型実行+所要時間サンプリング機能をつけて実行を行います。 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サービスと所要時間のオーダーが違います。なんてことだ… (苦笑)
関連する日記
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との間の所要時間の溝の仕組みが分かり、ちょっと安心しました (苦笑)
Axis2で試そうと思ったのですが、私には理解不能だったので断念しました。
高速なSOAP実装ということで、ふと ActiveSOAPというプロダクトを連想しました。
なぜ ActiveSOAPを思い出したのだろう、と考えたら 下記のニュースで用語が登場していたからでした
Webサービスは 下記の存在上の宿命をしょっています。
異機種・異プログラミング言語間の接続
(原則としては) HTTP接続
このために、Java RMIのような実装に比べると、どうしても動作速度は遅くなりがち、、、なのかも知れません...…とりあえず DOMベースではなくって SAXベースで実装されたSOAP実装でないと、どうしても速度が出ないような気がします…
、、、ああ、Axisは 処理の途中で DOMを利用しているから、データ件数が増えた際に指数的な速度低下を発生する可能性があります。気づいて、がっくりしました。
どどどっと調べ物をしていたら、がくっと疲れました。おまけに事務所がとても寒いです (T_T)今日は(も) 早めに帰社します。