MagicOnion v1 -> v2リブート, gRPCによる.NET Core/Unity用ネットワークエンジン
- 2018-12-28
先にCygames Engineers' BlogでMagicOnion – C#による .NET Core/Unity 用のリアルタイム通信フレームワークとしてリリースを出しましたが、改めまして、MagicOnionというフレームワークを正式公開しました。
MagicOnionはAPI通信系とリアルタイム通信系を一つのフレームワークで賄う、というコンセプトを元に、前職のグラニで「黒騎士と白の魔王」の開発において必要に迫られて捻り出されたものでした。
で、今更気づいたのがMagicOnionって正式リリースしてなかったんですよね、このブログでも↑のような形でしか触れていなくて、公式ドキュメントも貧弱な謎フレームワークだったという。今回Ver2って言ってますが、その前はVer0.5でしたし。まぁここでは便宜的にv1と呼びます。
何故に正式リリースまで行かなかったかというと、リアルタイム通信部分が微妙だったから。↑のp.39-40で説明していますが、Unary + ServerStreamingという構成で組んだのが、かなり開発的に辛かったんですね。時間的問題もあり強行するしかなかったんですが、ちゃんと自分が納得いく代案を出せない限りは、大々的には出していけないなあ、と。
その後すったもんだがあったりなかったりで、プレーンなgRPCでリアルタイム通信を組む機会があって、↑の時に考えていたDuplexStreaming一本で、コマンドの違いをProtobufのoneofで吸収する、という案でやってみたのですが、すぐに気づきましたね、これ無理だと。UnaryはRPCなのですが、Duplex一本はRPCじゃないんで、ただたんにoneofをswitchしてメソッド呼び出す、だけじゃ全然機能足りてない、と。
ただまぁコネクション的にはDuplex一本案は間違ってなさそうだったので、その中で手触りの良いRPCを組むにはどうすればいいか……。と、そこでMagicOnionをリブートさせるのが一番手っ取り早いじゃん、というわけで着手したりしなかったりしたりしたのでした。その間にCysharpの設立の話とかもあり、事業の中心に据えるものとしても丁度良かったという思惑もあります。
早速(?)Qiitaでも何件か紹介記事書いてもらいました。
- Unity+MagicOnionで超絶手軽にリアルタイム通信を実装してみた
- MagicOnion v2を使ってUnity IL2CPPでgRPC通信をする
- Docker を利用して MagicOnion & .Net Core の開発環境を整える
v1 -> v2
Unary系(API通信系)はほとんど変わっていません。それは、v1の時点で十分に高い完成度があって、あんま手を加える余地はなかったからですね。ただしフィルターだけ戻り値をTaskからValueTaskに変えています。これはフィルターの実行はメソッド実行前に同期的にフック(ヘッダから値取り出してみるとか)するだけ、みたいなものも多いので、TaskよりValueTaskのほうが効率的に実行できるからですね。
元々フィルターを重ねることによるオーバーヘッドを極小にするため、純粋にメソッド呼び出しが一個増えるだけになるように構成してあったのですが、更により効率的に動作するようになったと思います。
SwaggerのUIを更新するのと、HttpGatewayの処理を効率化するのが課題として残っているので、それは次のアップデートでやっていきます。
また、Unity向けにはコードジェネレート時にインターフェイス定義でTaskをIObservableに変換していたのですが、今のUnityは.NET 4.xも使えるということで、インターフェイスはそのまま使ってもらうようにしています。Taskのままで。
StreamingHub
の、導入に伴って、v1でリアルタイム通信系をやるための補助機構である StreamingContextRepository を廃止しました。StreamingContextRepositoryは、まぁ、正直微妙と思っていたのでなくせて良かったかな。決して機能してないわけではないのですけれど。
代わりのコネクションを束ねる仕組みはGroupという概念を持ってきました。これはASP.NETのWebsocketライブラリであるASP.NET Core SignalRにあるものを、再解釈して実装しています。
MagicOnionのGroupの面白いところは、裏側の実装を変えられることで、デフォルトはImmutableArrayGroupという、MORPGのようなルームに入って少人数で頻繁にやり取りするようなグルーピングに最適化された実装になっています。もう一種類はConcurrentDictionaryGroupという、こちらはMMORPGやグローバルチャットのような、多人数が頻繁に出入りするようなグルーピングのための実装です。更に、RedisGroupというバックエンドにRedisのPubSubを置いて複数サーバー間でグループを共有するシステムも用意しています、これはチャットや、全体通知などに有効でしょう。
また、GroupにはInMemoryStorage[T]というプロパティが用意されていて、グループ内各メンバーに紐付いた値をセットできるようにしています。これは、通信のブロードキャスト用グループの他に、値の管理のためにConcurrentDictionary[ConnectionId, T]のようなものを用意してデータを保持したりが手間で面倒くさいんで、いっそグループ自体にその機能持ってたほうが便利で最高に楽じゃん、という話で、実際多分これめちゃくちゃ便利です。
まとめ
というわけで、リブートしました!最初チョロいと思ってたんですが、割とそんなことはなくて、この形にまとめあげるまではそれなりに大変でした……。の甲斐もあって、今回のMagicOnionはかなり自信を持って推進できます。以前はそもそもgRPC本体をフォークして魔改造したり、というのもあったのですが、今は公式ビルドを使えるようになったのでUnity向けにも良い具合になってきています。
MagicOnion2の内容は、(v1を)実際に使ってリリースした後の反省点が盛り込まれているので、そういう点で二周目の強みがあります。最初からこの形で出すのは絶対にできないであろうものなので、しっかりと経験が活かされています。実プロダクトで使って初めて見えるものっていっぱいありますからねー。とはいえv1はv1で大きな役割を果たしたと思いますし、まぁあと自分で言うのもアレですが「黒騎士と白の魔王」が証明したこと(gRPCがUnityでいけるんだぞ、という)ってメチャクチャ大きかったなあ、と。
CysharpとしてもMagicOnion、使っていきますし、ほんと是非是非使ってみてもらえると嬉しいです。コードジェネレーターもついにWin/Mac/Linux対応しましたので(まだ微妙にバグいのですが年内か、年明け早々にはなんとかします)、ガッツリと使っていけるのではないかとです。