今どきのJavaRMI

とあるシステムのプロトタイプを去年作りました。
システム構成はAPサーバ(Weblogic)が複数台構成で、ある特定の処理を同期処理で1台のサーバだけで行わないといけないというもので、言語と実行環境はオールJAVAとの指定です。
その特定の処理を行うサーバとAPサーバ間の通信をどうしようかなと考えましたが、プロトタイプとはいえ、かなりミッションクリティカルかつ応答性能を求めるシステムで、また、拡張性や他の言語からの呼び出しなどは無視してよかったので、シンプルかつ、ミドルウェアレベルで昔から使われているRMIを採用してみました。実は最近の方式をよく知らないだけだったりしますが・・

こんな構成です。

RMI
ソース的には3つに分かれます

①インターフェースをクライアントとなるAPサーバと、「とある処理」サーバで動作させるモジュールのソースに含みます。ここでは処理要求・応答はByte配列を引数・戻りとして、クライアントからサーバへ処理要求を行います。

/**
* とある処理のRMIインターフェース
* */
public interface RequestReciever extends Remote {
  /** 処理要求
  * @param b 送信バイト配列
  * @return 応答結果バイト配列
  * */
  public byte[] req(byte[] b) throws RemoteException;

以下必要な処理をつらつら書いてますが省略

②APサーバ側の呼び出し部分抜粋です。

Registry registry;
registry = LocateRegistry.getRegistry({とある処理サーバのIPアドレス}, {待受ポート番号});
RequestReciever stub = (RequestReciever) registry.lookup("{とある処理を示す名前}");
{結果バイト配列} = stub.req({渡すバイト配列});

必要なのはこれだけ

③「とある処理」サーバ側のソースです

public class toaruPrcServer extends UnicastRemoteObject implements RequestReciever {
・・・中略
 public toaruPrcServer() throws RemoteException {
  public static void main(String[] args) {
   ・・・中略
   toaruPrcServer server = new toaruPrcServer();
   //Start RMI server
   Registry registry = LocateRegistry.createRegistry({待受ポート番号});
   registry.rebind("{とある処理を示す名前}", server);
  }
  @Override
  public byte[] req(byte[] b) throws RemoteException{
   byte[] res;
   ここに処理を書きます
   return res;
  }
以下、省略
 }
}

これだけでホントにシンプルです。
昔はリモートオブジェクトのエクスポートとかRMIレジストリ起動とか訳が解らないいろいろな前提があったみたいなんですが、今(使っているのはjdk1.8)ではずいぶんすっきりしているみたいです。
が、古い情報が混在していていまいち何が必要かやってみないとわかりません。

なお、「とある処理」のJVM起動時に
-Djava.security.policy=ポリシーファイル
-Djava.rmi.server.hostname=待受対象となるIP(NICが複数ある場合)
を指定しないと動きませんでした。(ポリシーは要らないかもしれない・・)

ポリシーファイルは
java.net.SocketPermission
について許可すればよかったはず。

後で負荷テストしたら秒間千件を超えるRMI呼び出しを行うと「Too many open files」エラーが多発しました。
調べたところ、Linuxのファイルディスクリプタ制限が影響していて、open filesの値を大きくしなきゃならないみたい。