Java RMI(Remote Method Invocation)とRPC (Remote Procedure Call)の違いって?

f:id:ts0818:20201212215016j:plain

サンタクロースSanta Claus)は、キリスト教圏における伝説の人物。よく知られている造形では、赤い衣装を着た老人で、トナカイのそりに乗り、クリスマスイブの夜に子供にプレゼントを渡して回るとされるが、時代や地域により異なる。

サンタクロース - Wikipedia

⇧ サンタクロースに会える年頃を過ぎてからいくつの夜を迎えたことでしょう、どうもボクです。

今年もクリスマスがやって来る~、な時期が近づいて参りましたが、 

⇧ 良い映画です。 

いろんな思いよ届け、ということで、今回は「Java RMI(Remote Method Invocation)」についてです。

レッツトライ~。

 

Java RMI(Remote Method Invocation)って?

Wikipediaさんによりますと、

In computing, the Java Remote Method Invocation (Java RMI) is a Java API that performs remote method invocation, the object-oriented equivalent of remote procedure calls (RPC), with support for direct transfer of serialized Java classes and distributed garbage-collection.

https://en.wikipedia.org/wiki/Java_remote_method_invocation

⇧「Java API」ですと。どんな?  

オブジェクト指向な「remote procedure calls (RPC)」を実現する「remote method invocation」ですと。

 

RPC(Remote Procedure Call)って?

というわけで、「remote procedure calls (RPC)」って何ぞ?

In distributed computing, a remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction. That is, the programmer writes essentially the same code whether the subroutine is local to the executing program, or remote. This is a form of client–server interaction (caller is client, executor is server), typically implemented via a request–response message-passing system. In the object-oriented programming paradigm, RPCs are represented by remote method invocation (RMI). The RPC model implies a level of location transparency, namely that calling procedures are largely the same whether they are local or remote, but usually they are not identical, so local calls can be distinguished from remote calls. Remote calls are usually orders of magnitude slower and less reliable than local calls, so distinguishing them is important.

https://en.wikipedia.org/wiki/Remote_procedure_call

⇧ザックリと「when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network)」ってことらしい。

subroutine」 はと言うと、

In computer programming, a subroutine is a sequence of program instructions that performs a specific task, packaged as a unit. This unit can then be used in programs wherever that particular task should be performed.

https://en.wikipedia.org/wiki/Subroutine#:~:text=In_computer_programming,_a_subroutine,particular_task_should_be_performed.

⇧1つのタスクを実行できるプログラミングの単位、ってことみたい、まぁ何らかの処理が実施できるってことでしょうかね。 

 

Distributed object communicationって?

オブジェクト指向な「remote procedure calls (RPC)」を実現する「remote method invocation」が、Java RMI(Remote Method Invocation)ってことなんですが、Wikipediaさんによりますと、

In a distributed computing environment, distributed object communication realizes communication between distributed objects. The main role is to allow objects to access data and invoke methods on remote objects (objects residing in non-local memory space). Invoking a method on a remote object is known as remote method invocation (RMI) or remote invocation, and is the object-oriented programming analog of a remote procedure call (RPC).

https://en.wikipedia.org/wiki/Distributed_object_communication

⇧ 分散システム環境において、分散されたオブジェクトと通信する方法を「Distributed object communication」というらしく、この通信の特徴としてはリモートオブジェクトでメソッドを呼び出すことらしく、その呼び出しのことを、

  • remote method invocation(RMI
  • remote invocation

 っていうらしく、オブジェクト指向が関わってくるので、「RMI(Remote Method Invocation)」は『オブジェクト指向なRPC(Remote Procedure Calls)』と見なされますと。

ちなみに、「RPC(Remote Procedure Calls)」は、

In distributed computing, an object request broker (ORB) is a middleware which allows program calls to be made from one computer to another via a computer network, providing location transparency through remote procedure calls. ORBs promote interoperability of distributed object systems, enabling such systems to be built by piecing together objects from different vendors, while different parts communicate with each other via the ORB.

https://en.wikipedia.org/wiki/Object_request_broker

⇧ 「ORB(Object request broker)」ってものにも利用されてるし、

gRPC (gRPC Remote Procedure Calls) is an open source remote procedure call (RPC) system initially developed at Google in 2015. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming and flow control, blocking or nonblocking bindings, and cancellation and timeouts. It generates cross-platform client and server bindings for many languages. Most common usage scenarios include connecting services in microservices style architecture and connect mobile devices, browser clients to backend services.

https://en.wikipedia.org/wiki/GRPC

⇧「gRPC」ってものにも利用されてるらしい。

全然関係ないけど、個人的に気になるのは、

message broker (also known as an integration broker or interface engine) is an intermediary computer program module that translates a message from the formal messaging protocol of the sender to the formal messaging protocol of the receiver. Message brokers are elements in telecommunication or computer networks where software applications communicate by exchanging formally-defined messages. Message brokers are a building block of message-oriented middleware (MOM) but are typically not a replacement for traditional middleware like MOM and remote procedure call (RPC).

https://en.wikipedia.org/wiki/Message_broker

⇧「message broker」っていう説明で、「Message brokers are a building block of message-oriented middleware (MOM) but are typically not a replacement for traditional middleware like MOM and remote procedure call (RPC).」ってあったのと、

A message broker is an architectural pattern for message validation, transformation, and routing. It mediates communication among applications, minimizing the mutual awareness that applications should have of each other in order to be able to exchange messages, effectively implementing decoupling.

The primary purpose of a broker is to take incoming messages from applications and perform some action on them. Message brokers can decouple end-points, meet specific non-functional requirements, and facilitate reuse of intermediary functions. For example, a message broker may be used to manage a workload queue or message queue for multiple receivers, providing reliable storage, guaranteed message delivery and perhaps transaction management. 

https://en.wikipedia.org/wiki/Message_broker

⇧「For example, a message broker may be used to manage a workload queue or message queue for multiple receivers, providing reliable storage, guaranteed message delivery and perhaps transaction management. 」ってあって、

message queue」はって言うと、

In computer sciencemessage queues and mailboxes are software-engineering components typically used for inter-process communication (IPC), or for inter-thread communication within the same process. They use a queue for messaging – the passing of control or of content. Group communication systems provide similar kinds of functionality.

The message queue paradigm is a sibling of the publisher/subscriber pattern, and is typically one part of a larger message-oriented middleware system. Most messaging systems support both the publisher/subscriber and message queue models in their API, e.g. Java Message Service (JMS).

https://en.wikipedia.org/wiki/Message_queue

⇧ってあって、

  • RMI(Remote Method Invocation)
  • RPC(Remote Procedure Calls)
  • ORB(Object Request Broker)
  • MOM(Message Oriented Middleware)

のあたりの使い分けがよく分からんな~、って思ったら、「Message Oriented Middleware」についての説明で、

Middleware categories

https://en.wikipedia.org/wiki/Message-oriented_middleware

⇧ ってことみたいね。「RMI(Remote Method Invocation)」は「RPC(Remote Procedure Call)」に含まれるってことなのかね。

All these models make it possible for one software component to affect the behavior of another component over a network. They are different in that RPC- and ORB-based middleware create systems of tightly coupled components, whereas MOM-based systems allow for a looser coupling of components. In an RPC- or ORB-based system, when one procedure calls another, it must wait for the called procedure to return before it can do anything else. In these synchronous messaging models, the middleware functions partly as a super-linker, locating the called procedure on a network and using network services to pass function or method parameters to the procedure and then to return results.

https://en.wikipedia.org/wiki/Message-oriented_middleware

⇧ ザックリと「RPC」「ORB」ベースは「密結合」になるのに対し、「MOM」ベースは「疎結合」でいけるってことなんすかね。

若干、気になるのは、「Message Oriented Middleware」の説明で、

Message queuing

See also: Message queuing service

Message queues allow the exchange of information between distributed applications. A message queue can reside in memory or disk storage. Messages stay in the queue until the time they are processed by a service consumer. Through the message queue, the application can be implemented independently - they do not need to know each other's position, or continue to implement procedures to remove the need for waiting to receive this message.

https://en.wikipedia.org/wiki/Message-oriented_middleware

⇧「Message queuing」について「See also: Message queuing service」ってあるんで、見に行ってみると、

message queueing service is a message-oriented middleware or MOM deployed in a compute cloud using software as a service model. Service subscribers access queues and or topics to exchange data using point-to-point or publish and subscribe patterns.

https://en.wikipedia.org/wiki/Message_queuing_service

⇧「A message queueing service is a message-oriented middleware or MOM deployed in a compute cloud using software as a service model.」ってあるんですよ。

でも、「message broker」の説明で、

「For example, a message broker may be used to manage a workload queue or message queue for multiple receivers, providing reliable storage, guaranteed message delivery and perhaps transaction management. 」ってことを言っておきながら、

Message brokers are a building block of message-oriented middleware (MOM) but are typically not a replacement for traditional middleware like MOM and remote procedure call (RPC).」って言ってるんですよね。

 

「MOM(Message Oriented Middleware)」である「message queue」を管理する「message broker」ってのはどういうカテゴリになるのかなってのが個人的に気になってモヤモヤしてますかね。

 

脱線しましたが、引き続き、Wikipediaさんによりますと、「Distributed object commnunication」ってのは、

⇧ 上図のようなイメージで通信が行われるんですと。

仕組みとしては、

The widely used approach on how to implement the communication channel is realized by using stubs and skeletons. They are generated objects whose structure and behavior depends on chosen communication protocol, but in general provide additional functionality that ensures reliable communication over the network.

https://en.wikipedia.org/wiki/Distributed_object_communication

⇧「Stub」「Skeleton」っていうオブジェクトが生成されることで実際に通信を行うようなのですが、「Stub」や「Skeleton」の構造や挙動ってのは、選択された通信プロトコルに依存するんだと。

でどうやって生成するかというと、

In RMI, a stub (which is the bit on the client) is defined by the programmer as an interface. The rmic (rmi compiler) uses this to create the class stub. The stub performs type checking. The skeleton is defined in a class which implements the interface stub. 

https://en.wikipedia.org/wiki/Distributed_object_communication

⇧ まずは「Stub」の「interface」を作って、そいつを「rmic(rmi compiler)」を使って「Stub」クラスを作成。「skeleton」は「Stub」の「interface」を「implements」していれば良いらしい。 

 

Java RMI(Remote Method Invocation)とRPC(Remote Procedure Call)の違いって?

RMI(Remote Method Invocation)」と「RPC(Remote Procedure Call)」のどっちも「分散システム」環境での通信を行うための手段ってことみたいですが、ザックリとした違いは「通信がオブジェクト指向かどうか」ってことになるんですかね。

Java RMI(Remote Method Invocation)」は、Javaを使った「RMI(Remote Method Invocation)」ってことですかね。

同じ様な疑問を上げてる人がおられましたね。

stackoverflow.com

 

Java RMI(Remote Method Invocation)を使う場合、「rmic(rmi compiler)」は不要?

なんか、Java 8 から「rmic(rmi compiler)」は使ってはいけないようですかね。 

よく分からんけど、

docs.oracle.com

非推奨に関する注: Java Remote Method Protocol (JRMP)のスタブおよびスケルトンの静的な生成のサポートは、非推奨になりました。その代わりに、動的に生成されたJRMPスタブを使用することをお薦めします。これにより、このツールをJRMPベースのアプリケーションで使用する必要がなくなります。詳細は、http://docs.oracle.com/javase/jp/8/api/java/rmi/server/UnicastRemoteObject.htmljava.rmi.server.UnicastRemoteObjectの仕様を参照してください。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/rmic.html

⇧上記のドキュメントによりますと、「スタブ」「スケルトン」の生成については、

  • 静的な生成  ←非推奨
  • 動的な生成

 の2パターンがあるようですかね。

詳細を確認してみると、

docs.oracle.com

Deprecated: Static Stubs. Support for statically generated stubs is deprecated. This includes the API in this class that requires the use of static stubs, as well as the runtime support for loading static stubs. Generating stubs dynamically is preferred, using one of the five non-deprecated ways of exporting objects as listed below. Do not run rmic to generate static stub classes. It is unnecessary, and it is also deprecated.

https://docs.oracle.com/javase/8/docs/api/java/rmi/server/UnicastRemoteObject.html

⇧「rmci」を実施してはいけない、って言ってますかね...

「スタブ」を作る場合は、次の6つの方法で実施すべきってことみたいね。

There are six ways to export remote objects:

  1. Subclassing UnicastRemoteObject and calling the UnicastRemoteObject() constructor.
  2. Subclassing UnicastRemoteObject and calling the UnicastRemoteObject(port) constructor.
  3. Subclassing UnicastRemoteObject and calling the UnicastRemoteObject(port, csf, ssf) constructor.
  4. Calling the exportObject(Remote) method. Deprecated.
  5. Calling the exportObject(Remote, port) method.
  6. Calling the exportObject(Remote, port, csf, ssf) method.

The fourth technique, exportObject(Remote), always uses statically generated stubs and is deprecated.

https://docs.oracle.com/javase/8/docs/api/java/rmi/server/UnicastRemoteObject.html

⇧既存の4つの手法は非推奨ってことみたいですが、そもそも知らんがな。

と言うか、 

初期のJavaにおけるRMIを利用した分散アプリケーションでは、通信処理を行うためのスタブ・スケルトンをrmicコマンドを用いて事前に生成しておく必要がありました。スタブはクライアント側に配置し、スケルトンはサーバ側に配置していました。RMIのバージョンが上がって(JRMP1.2)、スケルトンは生成する必要がなくなりましたが、スタブは必要でした。J2SE 1.5からは、スタブの生成も不要になり、実質的には通常のJavaプログラムを作る手順だけでRMIプログラムを作ることができるようになりました。

https://docs.oracle.com/javase/8/docs/api/java/rmi/server/UnicastRemoteObject.html

⇧最早、JDK 1.5(J2SE 1.5 Tiger)以降(おそらく、Java 5ってことなのかな)については「スタブ」「スケルトン」が不要ってことらしい...

塩漬けにしてるようなシステムでない限り、Javaのバージョンが5以下ってことはないでしょうから、「Java RMI(Remote Method Invocation)」を実施する場合は「スタブ」「スケルトン」は作らなくても動作するらしいってことね。

 

 

実際にJava RMI(Remote Method Invocation)を使ってみる

説明を見てきた限り「RMI(Remote Method Invocation)」は「分散システム」 を想定して使用されることを目的としているっぽいので、マシンを2台用意して、どちらのマシンにもJavaをインストールしている必要がありますかね。

今回は、「Docker ToolBox」で「仮想マシン」を1台用意して、通信してみますか。

「Docker ToolBox」を使う場合は、「DOCKER_HOST」の範囲が変わることに注意ですかね。

⇧「Docker client」は「仮想マシン(上図で言うと、「Linux VM」のこと)」の外で使うってことですかね。

なので、

⇧「Docker ToolBox」を使ってる場合は、

■Dockerクライアント

Docker クライアントは docker バイナリの形式です。これは主にユーザが Docker との通信に使います。ユーザからのコマンドを受け付けると、その先にある Docker デーモンと通信し返します。

アーキテクチャの理解 — Docker-docs-ja 1.9.0b ドキュメント

■Dockerデーモン

先ほどの図で見たように、Docker デーモンはホストマシン上で動きます。ユーザは直接デーモンと通信せず、Docker クライアントを通して行います。

アーキテクチャの理解 — Docker-docs-ja 1.9.0b ドキュメント

⇧ってな感じで、「Dockerクライアント」は「C:\Program Files\Docker Toolbox」の「.exe」形式のファイルのことを言ってるんじゃないかと、たぶん。

f:id:ts0818:20201210225536p:plain

で、「Dockerデーモン」は、「Virtual Box」の「仮想マシン」上で動いてるってことなんですが、「Virtual Box」の仕組みに詳しくないけど、「C:\Users\[ユーザー名]\.docker\machine\machines\default\disk.vmdk」が「仮想マシン」の「イメージファイル(Virtual Machine Disk Format)」ってことだとは思う。

f:id:ts0818:20201210225827p:plain

 

まぁ、脱線しましたが、「Java RMI(Remote Method Invocation)」の実装イメージとしては、以下のような構成になる感じですかね。(多分に推測が入っておりますので、あくまでイメージとして参考ください。Oracleさんがドキュメントで異なるマシン間でのシステム構成図っぽいのを公開してくれるのを期待したいところです。)

f:id:ts0818:20201212212702p:plain

⇧「ホストマシン」に「ゲストマシン(Virtual Box上にDocker ToolBoxで作成された仮想マシン、上図だとVMのこと)」が入れ子みたいな感じになってしまって分かり辛いんですが、異なるマシン2台ってことになります。 

「ポート」は「1099」「1100」で「ホストマシン」「ゲストマシン」をポートフォワーディングしてます。(Docker機能です。)  

自分の環境では「仮想マシン」には「CentOS 8」ベースのDockerコンテナを構築して、 そのコンテナの中に「OracleJDK15(RPMパッケージ)」をインストールしています。

 

ただ、

souiunogaii.hatenablog.com

⇧ 上記サイト様を参考に「CentOS 8」以外をインストールしたほうが良さそうですね。

「OracleJDK 15」のインストールについては、

www.osradar.com

⇧ 上記サイト様を参考にさせていただきました。

2020年12月6日(日)の時点では、「yum install」とかできなかったけど、将来的には「yum install」できるようになるのかな?(「CentOS 8」からは「yum」じゃなくて「dnf」になるのか、でも「CentOS 8」は2021年でサポート終了だしな...)

「OracleJDK15」をコマンドでインストールする時は、「JDKのダウンロードURL」を事前にOracleのダウンロードページから取得しておく必要がありますかね。

 

では、「Docker ToolBox」の「default」って名前の「仮想マシン」を起動します。

f:id:ts0818:20201211092411p:plain

仮想マシン」が起動したら、環境変数を設定して、「Docker deamon」を起動しておきます。その後、SSHログイン。

f:id:ts0818:20201211092627p:plain

Dockerコンテナを起動して、Dockerコンテナにログインしますが、「ホストマシン側」と「ゲストマシン側」のディレクトリをマウントするため、「ホストマシン側」にディレクトリを用意しておきます。

ポートフォワーディングしてDockerコンテナの作成・起動。 (「Dockerイメージ」としては「CentOS 8」ベースに、「OracleJDK15(RPMパッケージ)」をインストールしたものを用意して使用してます。)

docker run -it -d -p [ホスト側のポート1]:[ゲスト側のポート1] -p [ホスト側のポート2]:[ゲスト側のポート2] -v //c/Users/[ユーザー名]/[マウントしたいホストマシン側のディレクトリ]:/[マウントしたいゲストマシン側のディレクトリ] --name [任意のコンテナ名] [イメージのREPOSITORY]:[イメージのTAG]

Dockerコンテナにログインします。

docker exec -it [コンテナ名] /bin/bash

f:id:ts0818:20201211225126p:plain

 

で、「Java RMI(Remote Method Invocation)」の「入門」ってのは、

docs.oracle.com

Oracleのドキュメントに記載されてるんだけど、見事に同一マシンでの実装例となっていますと...

って言うか、何で実際の利用を想定した例にしてくれないのか不思議で仕方ないんだが...

「分散システム」環境において「リモートマシン」に対して通信する目的で作られたはずの「Java RMI(Remote Method Invocation)」で、何で同一マシンの実装例を提示するのよ?

「Hello」って表示されましたね!おめでとう!...

って、同一マシンで実装したら「Java RMI(Remote Method Invocation)」の意図した動作が確認できないやん?

Javaに限らないとは思うけど、「入門」然り「チュートリアル」然り、もうちょっと実用的な内容にしてくれないだろうか?と思ってしまう自分が変な人間なのだろうか...

docs.oracle.com

Java 言語での分散オブジェクトサポートの目標を次に示します。

  • 異なる仮想マシン上のオブジェクトへのシームレスなリモート呼び出しをサポートする

https://docs.oracle.com/javase/jp/1.5.0/guide/rmi/spec/rmi-intro3.html

⇧って言ってるけど、これは、同一マシン内での通信を想定してたんだろうか...

なんちゃって分散システムってやつですかね?

 

というボヤきが出たところで、

docs.oracle.com

Java RMIは、デフォルトで解決不可能なサーバー・ホスト名(修飾されていない名前、Windowsインターネット・ネーム・サービス(WINS)名、修飾されていないDHCP名など)を使用します。Java RMIクライアントが、解釈不可能なサーバー・ホスト名を含む参照を使ってリモート・メソッドを呼び出すと、クライアントはUnknownHostExceptionを発行します。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/rmi/faq.html

実際に機能するリモート参照を作成するには、Java RMIサーバーはすべてのJava RMIクライアントが解釈可能な完全修飾のホスト名またはIPアドレスを提供できる必要があります(完全修飾ホスト名の例: foo.bar.com)。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/rmi/faq.html

⇧ってなってますと。

まぁ、このあたりも何か動的に取得する方法が見当たらないしな...

それはさておき、一番肝心の異なるマシン間の通信の場合なんですが、ネット上のコーディング例(いずれも1つのマシン内で「クライアント」「サーバー」を実装)を確認した感じ、おそらく、同じ「インターフェイス」を「クライアント」側と「サーバー」側に1つ配置しないといけないんではないかと。(「クライアント」「サーバー」のどちらも「インターフェイス」を使用してるので)

 

というわけで、ファイルの構成は、以下のようになりました。

■ホストマシン側(Windows 10 Home)

C:\Users\[ユーザー名]\Desktop\soft_work\java_work\Answer021

  -Answer021Ifc.java

  -Answer021Client.java

 

■ゲストマシン側(CentOS 8)

/home/src/Answer021

  -Answer021Ifc.java

  -Answer021Server.java

 

コーディング内容は、

github.com

⇧ 上記サイト様を参考にさせていただきました。

ですが、上記サイト様は単一マシン内を想定しているため、異なるマシン間を想定した内容に修正いたしました。

「サーバー」側のコーディング以外はそのまま流用させていただいたので、「サーバー」側の内容のみ以下に記載します。

import java.util.Random;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * サーバ側の処理.
 */
public class Answer021Server implements Answer021Ifc {
    public Answer021Server() {}

    public static void main(String args[]) {
        try {
            // リモートオブジェクトを作成し、Java RMIランタイムへエクスポート
            Answer021Server obj = new Answer021Server();
            // portに0を指定すると、portは自動割り当てされる
            Answer021Ifc stub = (Answer021Ifc) UnicastRemoteObject.exportObject(obj, 1100);
            // リモートオブジェクトレジストリに指定した名前でバインドする
            Registry registry = LocateRegistry.getRegistry(Registry.REGISTRY_PORT);
            registry.bind("answer021", stub);

            System.err.println("Server ready");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 0から7の整数値をランダムで返す
     */
    public int getRamdomNum() throws RemoteException {
        Random rand = new Random();
        return rand.nextInt(8);
    }
}    

⇧ 「ポート」を「1100」としてますが、「1099」以外でご自身の環境に合わせて好きなポート番号を指定でOKです。(セキュリティが担保された環境でなら全てのポート開放しても良いのかもしれませんが...)

 

それでは、「.java」ファイルをコンパイルしていきます。

ei-www.hyogo-dai.ac.jp

Javaプログラムのコンパイルは javac コマンドで行いますが、 実行したいmainメソッドがあるプログラムファイルを指定するだけで 必要なクラスすべてをコンパイルしてくれます。

複数クラスのコンパイル - Masahiko's Wiki

⇧ということみたいなのですが、

iihito.dip.jp

このようにAntではファイルのタイムスタンプを見て修正があったソースファイルをコンパイルします。大規模なソフトウェアなどで非常にたくさんのソースファイルが存在する場合はAntのこの修正されたファイルだけコンパイルという機能は非常に役に立つでしょう。

Dream Software Laboratory

⇧ってな感じで、大規模開発の場合は、「ビルドツール」を使ったほうが良さ気のようですね。最近だと「Maven」「Gradle」を使っていく感じになるんですかね。

 

■ゲストマシン側(CentOS 8)

f:id:ts0818:20201211112017p:plain

 

■ホストマシン側(Windows 10 Home)

f:id:ts0818:20201211112347p:plain

f:id:ts0818:20201211112732p:plain


続きまして、「Java RMI レジストリ」を起動します。

docs.oracle.com

レジストリを起動するには、サーバーのホストでrmiregistryコマンドを実行します。このコマンドからは成功時に何の出力もありません。通常、バックグラウンドで実行されます。詳細は、rmiregistryのツール・ドキュメント(UNIX用Windows用)を参照してください。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/rmi/hello/hello-world.html

⇧ ってあるように、「Java RMI(Remote Method Invocation)」の「サーバー」側の「マシンのOS」に依存するみたいですね。

 

f:id:ts0818:20201211223351p:plain

⇧ 何かコマンドの実行結果が出力されたんですけど大丈夫みたいです。

Oracleの「レジストリを起動するには、サーバーのホストでrmiregistryコマンドを実行します。このコマンドからは成功時に何の出力もありません。」って説明が謎なんだけどね...

 

続きまして、「Java RMI(Remote Method Invocation)」の「サーバー」を起動。今回はDockerコンテナを使ってるので、Dockerコンテナが起動してる「仮想マシン」の「IPアドレス」をjavaコマンドのオプションで設定してます。

f:id:ts0818:20201211223543p:plain

そしたらば、「クライアント」側でJavaを実行します。

f:id:ts0818:20201211223654p:plain

⇧「サーバー」側のメソッドが呼び出されました。

無事、動いたことが確認できたので、「サーバー」側のプログラムを停止します。

casualdevelopers.com

⇧ 上記サイト様を参考によりますと、「rmiregistory &」でバックグラウンド実行した場合、「kill」でプロセスを終了する
 

まずは、「Ctrlキー」+「Cキー」で「RMIサーバー」を停止。

f:id:ts0818:20201211224010p:plain

そしたら、「RMI registry」「RMIサーバー」のプロセスを「kill」します。 

f:id:ts0818:20201211224154p:plain

 

というわけで、異なるマシン間で「Java RMI(Remote Method Invocation)」を実施することができました。

ネットの情報がほとんど単一のマシン内での実装例に偏っているところを見ると、「Java RMI(Remote Method Invocation)」は、同一マシン内で利用することを想定してるのかもしれませんね、Oracleの「Java RMI 入門」でも単一マシン内での実装だし。

まぁ、異なるマシン間で「Java RMI(Remote Method Invocation)」を試すときは、ハマりどころが多いので注意ですかね。

ただ、今時は、「Apache Kafka」などの「MOM(Message Oriented Middleware)」を使う方がナウい感じらしい様です、疎結合になるみたいだし。

Java RMI(Remote Method Invocation)」って実際に使われるケース出てくるんだろうか...モヤモヤ感しか残りませんでしたが、

今回はこのへんで。

 

 

NG集

あるある言いたい、異なるマシン間の通信あるある、言いたい~ 。

というわけで、「Java RMI(Remote Method Invocation)」の「クライアント」側から接続...できませんやん!

f:id:ts0818:20201211114912p:plain

 

takeda-san.hatenablog.com

ファイアウォールを設定しましょう rmiregistryで使うデフォルトポート1099とリモートメソッド呼び出し時につなぎに行くポート(匿名ポート)をTCPで開ける。

GCPでもJavaがしたい その3 - takeda_san’s blog

⇧ ということで、ポートを開放する必要があったんですな。

さらに、

lake-michigan.hatenablog.com

JMX over RMI で JMX モニタリングを行う際、サーバー側は「RMI registry port」(クライアントが接続時に指定するポート)と「RMI server port」(内部で使われるポート)と2つのポートを使います。後者の RMI server はデフォルトではランダムに振られるので、ssh tunnel で監視したいようなときは困ってしまうけど、Java7u4 以降は明示的に指定できるようになっているらしいのでメモ。

RMI server port を明示的に指定する(7u4 and later) - MB blog

⇧ というね。

ただ、上記サイト様の情報は「JMX over RMI」ということで「Java RMI」とはまたちょっと違う話になりそう。

Wikipediaさんによりますと、

Java Management ExtensionsJMX)は、アプリケーションソフトウェア/システムオブジェクト/デバイスプリンターなど)/サービス指向ネットワークなどの監視・管理のためのツールを提供するJavaプラットフォーム技術の一種。これらのリソースは MBean(Managed Bean)と呼ばれるオブジェクトで表現される。このAPIの面白い特徴として、クラス群を動的にロードしてインスタンス化できる。

Java Management Extensions - Wikipedia

⇧って感じで、「Java RMI」とは別物であろうと。

 

ここまでの流れを整理すると、

の2つのポートを開放しておいていけないといけないらしい、と言うか、「RMI server port」については固定しとかないとランダムでポートが振られるんだそうな。

よく分からんのは、「RMI registry」が「クライアントが接続時に指定するポート」って言うんなら、開放しとくポートは1つで良いのでは?

って思ったけど2つ空けてないと駄目でした。

で、「Java RMI」で「java」コマンド実行時に「ポート」の指定はできないっぽいので、コーディング時に設定するしか無さそう。 

なので、「サーバー」側のコーディングを、

github.com

⇧ 上記サイト様を参考に、

以下のように部分的に修正しました。

    public static void main(String args[]) {
        try {
            // リモートオブジェクトを作成し、Java RMIランタイムへエクスポート
            Answer021Server obj = new Answer021Server();
            // portに0を指定すると、portは自動割り当てされる
            Answer021Ifc stub = (Answer021Ifc) UnicastRemoteObject.exportObject(obj, 0);
            // リモートオブジェクトレジストリに指定した名前でバインドする
            Registry registry = LocateRegistry.getRegistry();
            registry.bind("answer021", stub);

            System.err.println("Server ready");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

    public static void main(String args[]) {
        try {
            // リモートオブジェクトを作成し、Java RMIランタイムへエクスポート
            Answer021Server obj = new Answer021Server();
            // portに0を指定すると、portは自動割り当てされる
            Answer021Ifc stub = (Answer021Ifc) UnicastRemoteObject.exportObject(obj, 1100);
            // リモートオブジェクトレジストリに指定した名前でバインドする
            Registry registry = LocateRegistry.getRegistry(Registry.REGISTRY_PORT);
            registry.bind("answer021", stub);

            System.err.println("Server ready");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

今回はDocker コンテナ内で起動したJava RMI(Remote Method Invocation)のサーバに接続したいということで、コンテナ起動時に「ホストマシン」側と「ゲストマシン」側でポートフォワーディングしておいてあげないといけないらしい。

stackoverflow.com

Before doing docker run you can remove the original container and then assign the container the same name again:

$ docker stop container01
$ docker commit container01 image01
$ docker rm container01
$ docker run -d -P --name container01 image01

(Using -P to expose ports to random ports rather than manually assigning).

https://stackoverflow.com/questions/19335444/how-do-i-assign-a-port-mapping-to-an-existing-docker-container

⇧ 残念ながら、Dockerはコンテナ初回起動時にしか「ポートフォワーディング」ができないので、既に起動してしまったコンテナは無力ですと。

なので、起動してしまってるコンテナを停止して、コンテナの内容を「Dockerイメージ」として保存し、停止したコンテナを削除し、新たにコンテナを作成・起動する際にポートフォワーディングするって流れですかね。

 

「Ctrlキー」+「Cキー」で「RMIサーバー」を停止して、Dockerコンテナも停止で。

f:id:ts0818:20201211173652p:plain

停止したDockerコンテナの内容を「Dockerイメージ」として保存します。 

docker commit [コンテナ名] [イメージのREPOSITORY]   

f:id:ts0818:20201211173900p:plain

「Dockerイメージ」が保存できたら、停止したコンテナを削除で。 

docker rm -f [削除したいコンテナ名]

f:id:ts0818:20201211174010p:plain

ポートフォワーディングしてDockerコンテナの作成・起動。 

docker run -it -d -p [ホスト側のポート1]:[ゲスト側のポート1] -p [ホスト側のポート2]:[ゲスト側のポート2] -v //c/Users/[ユーザー名]/[マウントしたいホストマシン側のディレクトリ]:/[マウントしたいゲストマシン側のディレクトリ] --name [任意のコンテナ名] [イメージのREPOSITORY]:[イメージのTAG]

Dockerコンテナにログインします。

docker exec -it [コンテナ名] /bin/bash

f:id:ts0818:20201211225126p:plain

 そしたら、「.java」「.class」ファイルのある場所に移動し、「Java RMIレジストリの起動」を実施。

f:id:ts0818:20201211223351p:plain

⇧ 何か表示されたんですけど大丈夫みたいです。

Oracleの「レジストリを起動するには、サーバーのホストでrmiregistryコマンドを実行します。このコマンドからは成功時に何の出力もありません。」って...

 

続きまして、「Java RMI(Remote Method Invocation)」の「RMIサーバー」を起動。

f:id:ts0818:20201211223543p:plain

そしたらば、「クライアント」側でJavaを実行します。

f:id:ts0818:20201211223654p:plain

⇧「サーバー」側のメソッドが呼び出されました。

Java RMI(Remote Method Invocation)」のドキュメント、もうちょっと整備して欲しいかな...