🕙

MagicOnionから始めるリアルタイム通信 (前編)

2020/12/07に公開

Happy Elements Advent Calendar 2020 7日目の記事です。

メルクストーリア」エンジニアの 岸本 と申します。

今回は、メルクストーリア で現在開発の進めている リアルタイム通信 の取り組みについて、
ご紹介したいと思います。

基本的な部分も含めてのご紹介になるため、
MagicOniongRPCKubernetesAgones などなど、
マシマシな感じでかなりボリュームのある内容なため、2日に分割しての記事となります。

しばしお付き合いいただければ幸いですので、何卒よろしくおねがいします!

本内容ですが、現在開発中のものであり、実際の仕様とは異なる場合があります!

前提

メルクストーリア は、おかげさまでもうすぐ7周年を迎えます!🎉🎉🎉

そんな メルクストーリア では、チャットギルドバトル をはじめ、
MMOのような見た目の 急襲!降臨モンスターイベント など、
ユーザー様同士のマルチプレイができる遊びを多く提供していますが、
基本的には ポーリング で実現しています。

ポーリング である以上、一定のタイムラグが発生し、
ユーザーの皆様にご不便をおかけしている部分もあると感じており、
さらなる遊びの提供やユーザー体験の向上を目標に、リアルタイム通信 を用いた開発に取り組んでいます。

ただし、現在運用中の Ruby on Rails (Ruby) の APIサーバー が存在していますが、
そちらを リアルタイム通信 に対応するのではなく、
リアルタイム通信専用 に別系統のサーバーを立てて、部分的なリアルタイム通信の導入 を目指しています。
なお、クライアントは Unity です。

サーバー フレームワーク インフラ
APIサーバー (既存) Ruby on Rails (Ruby) オンプレミス
リアルタイム通信サーバー (新規) .NET (C#) クラウド (AWS)

また、インフラ面でも課題があり、
既存のAPIサーバーは、オンプレミス での運用に対して、
現在開発中のリアルタイム通信サーバーは、クラウド (AWS) での運用が前提になっています。

そのため、運用中のユーザーデータなどへのアクセスに制限があり、
リアルタイム通信サーバー (クラウド)APIサーバー (オンプレミス) で通信することも可能ですが、
APIサーバーの負荷等も考慮すると、頻繁なアクセスはできないため、
基本的に運用中のユーザーデータへのアクセスはできない前提とします。

技術的にいろいろハイブリッドな構成となっていますし、
いろいろな事情も絡んで、リアルタイム通信サーバー で実現できることも限られていますが、
そのあたりも含めて、全体像をご紹介したいと思います。

使用している技術 (2020年12月時点)

サーバーサイド

  • .NET 5.0 (C# 9.0)
  • MagicOnion 4.0
  • MessagePack for C# 2.2
  • ZLogger 1.3
  • StackExchange.Redis 2.1
  • System.Reactive

クライアントサイド

  • Unity 2018.4 (C# 7.3)
  • MagicOnion 4.0
  • MessagePack for C# 2.2
  • UniTask 2.0

インフラ

  • Kubernetes 1.18
  • Agones 1.0.9
  • AWS
    • ElasticKubernetesService (EKS)
    • ElasticContainerRegistry (ECR)
    • ElastiCache (Redis)
    • SecretsManager
    • CodeBuild

MagicOnion

MagicOnion とは、 gRPCMessagePack for C# がベースの通信フレームワークで、
クライアントもサーバーも 「C#で大統一!」 できる特徴があります。

MagicOnion では、C# の interface で gRPC のメソッドを表現できるようになっていたり、
また、通信に用いるデータを MessagePack for C# でシリアライズすることで、
gRPCベース の Remote Procedure Call を利用できるようになっています。

機能的には、2種類 の通式方式がサポートされていて、
単発のAPI通信に向いている「Service」 と、
双方向通信も可能でリアルタイム通信に向いている「StreamingHub」 があり、
用途によって使い分けることができます。

また、Filter という共通処理のための仕組みや、
Group というマルチプレイのための仕組みなども実装されており、
通信まわりは一通り MagicOnion におまかせできるようになっています。

今回は、リアルタイム通信での使用を目的としていますが、
単純なAPIサーバーとしても使用できるようになっています。

サーバー構成

さて、MagicOnion を用いることで リアルタイム通信 は実装できそうですが、
インフラ面で問題が出てきます。

マルチプレイでの遊びを考えた場合、
複数ユーザー間でデータを同期する必要があり、どうやってデータを管理・共有するか問題になってきます。

方法としては、おおまかに 2種類 あります。

ステートレスな構成

ロードバランサーによる分散を前提としたサーバー構成の場合、
当然、ユーザーの接続するサーバーもバラバラになるため、
マルチプレイに必要なデータは、Redis Pub/Sub など、
サーバー外部の共通ストレージで管理・共有することで解決できます。

MagicOnion には、Redis Pub/Sub での繋ぎ込みをサポートする、
MagicOnion.Server.Redis というライブラリも用意されています。

ステートフルな構成

サービスディスカバリー という リアルタイム通信サーバー への誘導用サーバーを立てることで、
ロードバランサーによる分散はせず、
マルチプレイさせたいユーザー同士を直接同じサーバーに接続させ、
マルチプレイに必要なデータは、サーバー内部のメモリ上で管理・共有することで解決できます。

MagicOnion には、サーバー内でユーザー同士を束ねることのできる、
Group というブロードキャストシステムが内蔵されています。

メルクストリーアでは

どちらで構成してもメリット・デメリットがあり、
一概にどっちが良いかはケースバイケースとなりますが、
今回は、ステートフルな構成 を採用しています。

マルチプレイさせたいユーザー同士をひとつのサーバーに集めて、Group で束ねることで、
リアルタイム通信 を実現します。

たとえば、チャットルーム のような機能を実現したい場合、
チャットルームごとに Group を作って、ユーザー同士を束ねることができます。

Group は、チャットルームID のような任意の文字列で簡単に作ることができ、
Group に所属しているユーザーに対して、サーバーからクライアントへのブロードキャストが可能となります。

データの管理・共有は、共通のインスタンスを利用することで解決し、
どうしても外部に保存しておきたいデータのみ、外部のストレージに保存するようにします。

前編まとめ

ということで、前編は MagicOnion を用いた マルチプレイなリアルタイム通信 を実現する上で、
基本的な仕組みについて、ご紹介させていただきました!

gRPC 含めて サーバーサイドC# ということだけであれば、
.NET標準 だけでも十分開発できるようになっており、
先月リリースされた .NET 5 では、gRPC のパフォーマンスもトップレベルで良くなっているようです。

MagicOnion は、.NET標準 の実装に沿いつつ MessagePack for C# によるシリアライズもサポートし、
さらに いい感じC#gRPC できるようになっています!

機能面でも複雑に多機能な感じではなく、基本的な部分は README や サンプル が用意されているため、
それほど学習コストも高くないように感じました。
どちらかというと、Kubernetes など他の技術の学習コストの方が高めです…。

明日は、本日の内容を前提に KubernetesAgones といったインフラ寄りの話題に加えて、
具体的な処理の流れなどをご紹介したいと思います。

引き続きお楽しみいただければ幸いです!

メンバー募集

Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です!

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
Happy Elements株式会社 採用特設サイト

GitHubで編集を提案
Happy Elements

Discussion