試した環境
- Mac OS 12.1 (Intel)
- OpenJDK 64-Bit Server VM (build 17+35-LTS, mixed mode, sharing)
- RabbitMQ 3.9.12
プログラム
RPCは、リクエスト/レスポンスがペアになった双方向通信です。サーバーはコールバック用のラムダ式の中で、フィボナッチ数列の第n項を計算して、結果をクライアントに返します。
Remote Procedure Call(rpc)
RPCServer.java
RPCClient.java
クライアントは連続して32回のリクエストを送信して、それぞれ32回のレスポンスを受信します。その間、RPCサーバーが動き続けていないと応答を返せません。動き続けているということは、RPCServerがメッセージブローカーに接続していて、キューを処理できる状態にあるということです。つまり、RPCServerを止めずに、Webサーバーのように常時動作させておきたいのだと思います。
実験
RPCServerの最後のwhile(true)をコメントにすると、RPCServerは1回だけリクエストを処理してレスポンスを返したのちmain()から抜けます。main()を抜けるとRPCServerプロセスが終了して、メッセージブローカーとの接続が切れます。
この時、RPCClientは1回分のレスポンスを受け取り、2回目のリクエストを送信したところでレスポンス待ちになります。メッセージブローカーの先にリクエストを処理してくれるRPCServerがいないから。
RPCServerのwhile(true)はプロセスを終了させずに処理続行するのに必要。これは、アプリの作りとしてそうしているに過ぎません。
考えてみたいこと
キューを利用するシステムは、Webサーバーのように常時動作しなくても良いのでは。もっとゆるい条件で、例えば、RPCServerを32回起動したとしても、RPCClientに正しいレスポンスを返せれば、問題ないと思われます。
試してみると、RPCServerは再起動するとリクエストを処理できませんでした。
追記 (2022-01-24)
「考えてみたいこと」は本題から外れているので無視してください。まず、RCPServerを理解するための技術的な知識を書き、次に、while(true)について意見を述べます。
【技術的な知識】
thread
- Javaプロセスは、スレッド(daemon以外のスレッド)が全て終了した時点で、終了する
- RabbitMQのConnection/Channelを生成すると、スレッドが起動し、Connection/Channelを閉じるとスレッドが終了する
- ExecutorServiceを利用。送受信や、ブローカーの生死確認、シャットダウンのため
- 本来の目的のChannel処理だけでなく、管理用のスレッドが起動されます
mainスレッドが終了したとき、他にスレッドが生きていればプロセスは終了しません。チュートリアルのプログラムでは、Connectionを作成して閉じなければ、スレッドが生きています。プロセスは終わらないので、CTL-Cで中断します。
try-with-resources
- try(<リソース設定>) {} の形式。catch, finallyはオプション
- リソース設定で宣言できる変数は、AutoCloseableインターフェイスを実装するクラス
- tryブロックを抜けると、AutoCloseableリソースを、宣言が内側のものから順に閉じる
try-with-resoucesを使ってConnection/Channelを利用すると、tryブロックを抜けた時点でConnection/Channelが閉じられ、スレッドが終わる。RPCServerプログラムは、try-with-resoucesを抜けると、mainスレッドが1つだけ生きていて、main()メソッドを抜けたところでスレッドがいなくなり、プロセスが終了する。
notify/wait
RPCserverでは、mainスレッドをビジーウェイトせずに無限ループさせるために使っています。目的は、try-with-resourcesブロックに留まり続けて、生成したConnection配下のChannelのスレッドで、コールバック処理を行わせるためです。
【個人的な意見】
RPCServerでwhile(true) を使う本当の理由はチュートリアル作成者に聞くしかありませんが、RPCServerのやり方が好ましいと思います。
- try-with-resourcesを使ってChannel/Connectionを明に閉じることで、リソースの利用スコープを制限することができる。
- Channelを生成すると、メッセージブローカー側にも、ピアのerlang処理が作成される
- while(true) をwhile(<サーバーの稼働条件>)とすれば、きちんとリソースを解放して、サーバーを行儀良く終了させることができる。
RabbitMQのFAQを調べてみてはいかが。
(RabbitMQについては、わからないことが多く、 調べが進めば追記するかもしれません。)
利用したjarを追記します。
bash
1CLASSPATH=$RABBITMQ_LIB/slf4j-simple-1.7.33.jar:$RABBITMQ_LIB/slf4j-api-1.7.33.jar:$RABBITMQ_LIB/amqp-client-5.14.1.jar