はじめに
クライアント・サーバモデルをベースとした自作のRPCプログラムを、クライアントサイドを Node.js、サーバサイドを Python で、UNIXドメインソケットを使って実装しました。
この記事では、課題に対してどのように取り組んだかについてまとめていきます。
GitHubはこちら
課題内容
異なるプログラミング言語で書かれたクライアントとサーバが共通の方法で通信し、特定の関数を実行できるようにするシステムを作ること
具体的には、クライアントが Python で書かれたサーバに対して、JavaScript(Node.js を使用)から命令を出す場面を想定しています。
※ https://recursionist.io/dashboard/course/31/lesson/1101 より引用
リクエストとレスポンスの形式
{
"method": "subtract",
"params": [42, 23],
"param_types": ["int", "int"],
"id": 1
}
{
"results": "19",
"result_type": "int",
"id": 1
}
想定される課題の目的
以下の項目を理解して実装することと判断しました。
- 異なる言語間でのデータのやりとりをどうするか
- ソケット通信を通じてデータの送受信
- 呼び出す関数をどう識別して実装するか
Remote Procedure Call(RPC)とは
ネットワークで接続された異なるコンピュータ上にあるプログラムの関数や手続きを、まるで自分のコンピュータのローカルにあるかのように呼び出して実行するための技術、またはそのプロトコルです。
RPCの処理の流れ
- 呼び出し側(クライアント) は、通常の関数を呼び出すのと同じコードを書く
- RPC が、その呼び出しをメッセージ(リクエスト)に変換し、ネットワーク経由でサーバーに送信
- 実行側(サーバー) はメッセージを受け取り、対応する関数を実行し、結果をメッセージ(レスポンス)としてクライアントに送信
実装方針(仮説)
以下の流れをクラスを設計して実装する
クライアント側(Node.js)
- CLI に「メソッド」「引数」「引数のデータ型」を入力
- クライアント番号を作成(自動生成するようにする)
- 引数チェック
- 関数名に関する文字列チェック
- 関数に応じた引数の数チェック
- CLI入力の変数への格納
- リクエストとしてオブジェクト化
- JSON形式に変換
- ソケット通信でJSONデータをサーバに送る
- エラーハンドリング
サーバ側(Python)
- ソケット作成・bind()・listen()・accept()
- 受け取ったJSON形式のリクエストデータをオブジェクト化(辞書化)
- 後で使用するものだけ変数に格納
- 辞書から関数欄を注目する
- 該当する関数一覧から、呼び出す関数を決める
- 引数のデータ型をチェックする
- Python用のデータ型に変換する
- 呼び出す関数に引数を渡す
- メソッドを処理する
- 結果を変数に格納する
- 辞書を作る
- JSON形式に変換する
- ソケット通信でNode.js(クライアント)側にJSONデータを渡す
- エラーハンドリング
検証
仮説で作った実装方針に基づいて実装しましたが、クラスの設計がままならず、長期間にわたる膠着状態が続いたため、関数ベースの処理に変更しました。
その結果、正常系までは実装することができました。
工夫した点
-
クライアント側での引数の型チェックを導入
これによって、ネットワークに余計なデータを送らずに済みました。 -
Node.jsで readline を使用し対話型CLIを実現
これによって、CLIへの入力が簡単になりました。
当初、コマンドpython ファイル名 関数名 引数で実行できるようにしていたが、関数の存在や引数の型検証などを一度に行なう必要が出てきたため、入力の都度検証するように変更しました。
これにより、エラーが発生した場合にどの入力に対するメッセージなのかをはっきりさせることができました。
仮説からの変更点
-
クラス設計の辞退
予想を遥かに超える工数がかかってしまい、時間を要したため、今回はクラスを使った設計を行ないませんでした。 -
コマンドにまとめて記述から対話式の入力へ変更
今後の課題
- クラスを使って設計すること
- Node.js と Python で同じ method_table テーブルを参照する方法
- 複数のクライアントから同時接続できるようにすること
- 関数ごとに適切な引数のデータ型を表示すること
- 関数の追加がしやすい設計にすること
- メッセージの個別化
関数ごとに引数に関するエラーメッセージの出力内容を変えること - sort 関数の引数に上限を設けること
現状ではいくらでも追加できるようになっているため - エラーハンドリングを実装すること
エラーが発生した場合にサーバが送信するレスポンスにエラーメッセージの詳細がないこと
気づき
-
例外処理を実装中に、接続用ソケット作成後に while ループは不要であるとわかった
-
処理の流れを紙に書き、正常系→異常系→テスト の順で実装していったが、最後まで時間内にやり切ることができなかったため、次回こそは時間内に一通り実装できるように、どんなタスクをいつまでに完了するか、タスク管理を細分化して徹底する必要があるとわかった
-
使用したことないライブラリはじめ、書いたら小さくテストしてどのような挙動になるのかを確認しながら進めたことで手応えを感じながら実装することができた
-
どのカレントディレクトリからファイルを実行してもいいようにしたいという課題に対して、pathlib があると知り、「課題→解決策」の流れをベースに、その瞬間には必要ないものにはアクセスしないようにすることも必要だとわかった
まとめ
今回の課題では、UNIXドメインソケットをつかって、異なる言語間での情報のやり取りのために、JSON形式のデータの扱い方と処理の流れを、RPCプログラムの作成を通じて学ぶことができました。
当初の予定では、クラスを使って実装しようとしましたが、想定以上に苦戦してしまい、時間を浪費していたため、方針を切り替えて、関数ベースで作成することにしました。
この関数ベースのコードを、どのようなクラスに分けるのが最適かは、今後の課題とし、プログラムが1周したタイミングで再び取り組んで改善していきたいと思います。
最後までお読みいただき、ありがとうございました。
参考URL