norpc: Tokioランタイム上で動くマイクロサービスを作るフレームワーク

microservices

マイクロサービスは現代的なシステムの設計技法の一つであり、 コンテナ技術との相性が良いため、広く使われるようになった。

GoFによる古典的なデザインパターンはオブジェクト指向のコード設計に関するものだが、 マイクロサービスを前提とした場合にもやはりデザインパターンがある。 サイドカー、アダプタ、アンバサダ、こういったパターンを知っていると、 設計をより柔軟に思考することが出来る。

マイクロサービスでは、gRPCが広く使われる。 gRPCは言語非依存のフレームワークであり、あるサービスはRustで、 別のサービスはPythonでなどという実装が可能になる。

Rustの武器は、非同期ランタイムシステムだ。 Rustを使えば、非同期アプリケーションの開発は簡単にはなるが、 おれはさらにそこにマイクロサービスの技術を導入したい。 つまり、非同期ランタイムの上でマイクロサービスを実現したい。 こうすることで、単一のランタイム内においてマイクロサービスの技術を使用可能になり、 開発がさらに容易になる。

tarpc

当然、そのようなことを考える人間はおり、 先行技術としてはtarpcが存在する。 しかし、tarpcは古くから(リリースノートによると、5年も前から)あるソフトウェアであり、 設計が現在のエコシステムを活用していない。 そのため、コードが意味もなくでかい。

Towerは、リクエストからレスポンスを生成する関数を中心としたフレームワークであるが(Scala業界でいえばfinagleのようなものだ)、 もしtarpcがTowerを活用していれば、多くのコードは不要になる。 これについては、tarpc#356にて議論を行った。 おそらくだが、tarpcがこの問題を修正の積み重ねによって解決するのは現実的ではない。

tarpcに関わる問題のもう一つは、プロセス内マイクロサービスと、ネットワーク越しのマイクロサービス(シリアライザにserdeを使っている) を同じ抽象の下に実現しようとしており、 プロセス内マイクロサービス側は、ネットワーク越しのマイクロサービスの特殊なバージョンとして実装されていることだ。 これにより、実装は割を食う。 この点についていうとおれは、gRPCを実装したTonicが存在する今に至っては、 ネットワーク越しのバージョンをサポートする意味は実用上は皆無であり、 意味もなく損をしているだけだという考えを持っている。

norpc

だから、おれはゼロからnorpcを作ることにした。

https://github.com/akiradeveloper/norpc

サービスを定義するにはマクロを使う。 以下のように書くと、サービス独自なコードを生成してくれる。 このマクロの実装には手続きマクロを使っている。

#[norpc::service]
trait HelloWorld {
    fn hello(s: String) -> String;
}

#[norpc::service(?Send)]のようにすれば、no-SendなFutureを返すサービスを生成する。 特定のランタイムは、Futureがno-Sendであることを要求する。

クライアントやサーバの実装はTowerのServiceを実装しており、 timeoutやrate-limitingやbufferingなどといった機能を追加したければ、 Towerのエコシステムにあるデコレータをスタックすればよい。

norpcを使うと、非同期アプリケーションの実装は格段に簡単になる。 ぜひ、趣味でも仕事でも、みんなのプロジェクトで使ってみてほしい。


おすすめサプリです。おれも飲んでます。