Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

grpc-webによる内製ゲームサーバ基盤TakashoのUnity WebGL対応【DeNA...

DeNA_Tech
March 02, 2023

grpc-webによる内製ゲームサーバ基盤TakashoのUnity WebGL対応【DeNA TechCon 2023】

youtube:https://youtu.be/hVhFik50YsA

概要:
DeNAではgrpcをベースとした内製ゲームサーバ基盤Takashoを使ったモバイルゲーム開発を行っていますが、UnityをWebブラウザ上で動作させるWebGLビルドを動作プラットフォームとするケースが生まれ、既存の資産を引き継いだ開発を進める為にもTakashoをUnity WebGLビルドで動作させる必要が出てきました。
本セッションでは、Takashoの基礎的な構成とそれをUnity WebGLビルドで動かすにあたって何が壁になり、どのような性質が要件として求めらたのかを説明した上で、これを乗り越えるために採用したgrpc-webという技術の概要と、それをUnity WebGLビルド上で用いるためにDeNAが取った手法についてを解説します。

登壇内でのリンク集:
p5-1, https://engineering.dena.com/blog/2021/10/takasho-overview/
p5-2, https://speakerdeck.com/dena_tech/googlecloudinsidegamesandapps-motohironakamura
p5-3, https://cedil.cesa.or.jp/cedil_sessions/view/2242
p17-1, https://github.com/transformsai/UnityGrpcWeb
p17-2/3, https://www.transforms.ai/
p19, https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md
p25, https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy

◆ チャンネル登録はこちら↓
https://youtube.com/c/denatech?sub_confirmation=1

◆ Twitter
https://twitter.com/DeNAxTech

◆ DeNA Engineering
https://engineering.dena.com/

◆ DeNA Engineer Blog
https://engineering.dena.com/blog/

◆ DeNA TechCon 2023 公式サイト
https://techcon2023.dena.dev/

DeNA_Tech

March 02, 2023
Tweet

More Decks by DeNA_Tech

Other Decks in Technology

Transcript

  1. 自己紹介 • 大竹 悠人(Haruto Otake) ◦ ゲーム事業本部開発事業部開発二部テクノロジー推進グループ ◦ a.k.a @Trapezoid

    • 2013年よりDeNAにジョイン ◦ Unityに関連した技術サポートと様々な内製ライブラリの実装・保守に従事 ◦ 内製ゲームサーバ基盤TakashoのクライアントSDK担当も兼務 3
  2. 内製ゲームサーバ基盤 Takashoとは? • DeNA内製のゲームサーバ基盤(フレームワーク) ◦ 最近のDeNAのゲームの多くで利用している、共通基盤 ◦ golang製のゲームサーバと、C#によるクライアントSDK ◦ Protocol

    Buffers IDLを元にサーバ/クライアント双方のコードを自動生成する • 技術スタック ◦ サーバ: golang, k8s(GKE), Cloud Spanner ◦ クライアント: C#(非Unity環境も対応) ◦ プロトコル: gRPC ▪ ゲームクライアントからのTakashoサーバのAPI呼び出しのワイヤプロトコルとして利用 ▪ 独自暗号化やSSL PinningをgRPCを使いつつ独自に実装 • gRPC以外の独自のコード生成も行う • 参考リンク(既存の講演内容) ◦ 社内ゲームサーバー基盤 Takasho とは ◦ DeNA の次世代ゲームを支える Takasho と Google Cloud の活用 ◦ サーバー"コード"レス!? クライアント中心のモバイルゲーム開発手法 5
  3. Webブラウザからゲームサーバに接続する為の選択肢 1. Takashoを使わず、新規にゲームサーバを構築する ◦ Swagger等の既存の仕組みでWebGLビルドでも利用できるクライアントSDKを生成できる ◦ 既存タイトルで積み上げたゲームサーバ実装を活かせなくなり、実装工数が大きくなる ◦ k8s/IaC等の運用上のノウハウも散逸してしまう ◦

    不採用。Takashoのまま接続可能にする選択肢を探ることに 2. TakashoにgRPCではない通信手段を実装して、置き換え可能にする ◦ TakashoのままgRPCを使わず、通常のHTTPで呼び出せるエンドポイントを定義する ◦ サーバ/クライアント共にのフレームワークに大きな手入れが必要になる ▪ 1.ほどではないが実装工数が大きい ◦ 不採用 3. grpc-webを介して、grpcのTakashoサーバを間接的に呼び出せるようにする ◦ 採用 11
  4. grpc-web • gRPCをWebブラウザから利用するための派生プロトコル • gRPC(の一部機能に限って)HTTP/1.1とブラウザのHTTP通信APIで実装可能な仕様にしたもの ◦ Unary(1Req:1Res)RPCに対応 ◦ ServerStream(1Req:NRes)RPCに対応 ◦

    ClientStream(NReq:1Res), Bi-DirectionalStream(NReq:NRes)には非対応 ◦ Takashoは全てUnaryを採用しているため問題ない • 既存のgRPCサーバへのリバースプロキシとしてgrpc-webサーバを別途設置するのが主流の構成 ◦ フレームワークとしてのTakashoサーバにはほぼ手を入れずに、 クライアントSDKとインフラ面の対応のみの最小工数でWebGL対応を実現できそう... 13
  5. Unity WebGLビルドのgrpc-webの動作の障壁となる制約 • シングルスレッドでのみ動作し、スレッドを作成できない ◦ サブスレッドが動作する前提のライブラリは利用できない ◦ Taskを同期的にWaitするなど、非同期処理のやりかた次第では容易にデッドロックする • ネットワークアクセスに大きな制約がかかる

    ◦ そもそもブラウザ上でソケット通信は利用できないため、System.Net.Socketsは利用不可 ◦ System.Net.HttpWebRequestによるHTTP通信も利用不可 ◦ UnityのAPIであるWWW/UnityWebRequestでのHTTP通信は利用可能 • ネイティブプラグインの動作と利用可能な形態に大きな制約がかかる ◦ WASM Object Files/LLVM Bitcode/C/C++のソースのみ利用可能 ▪ x86/arm等の特定のCPUアーキテクチャ向けにコンパイルされたものは利用できない ◦ ネイティブプラグインからのネットワークアクセスが出来ない ▪ socket/bindなどのソケット通信用の標準関数がそもそも動作しなくなる ◦ JavaScriptのソースを埋め込むことでJavaScriptで実現可能な動作はプラグイン化できる ▪ WWW/UnityWebRequestは内部がJavaScript側で実装されている為に通信が可能 • 消費メモリ量を絞る必要がある ◦ 特にモバイルブラウザでは、利用できるメモリ量が端末によっては非常にシビア ▪ モバイルWebView上で実行されるケースもあり、その場合は更にシビア ◦ ファイルシステムを使えないため、多くのアセットをインメモリに展開する 15
  6. Unityにおけるgrpc-webの通信スタックの選択肢 1. Grpc.Net.Client.Web ◦ gRPCから公式に提供されている、.NET向けのgrpc-webクライアント実装 ◦ 公式gRPC実装(Grpc.Net/Grpc.Core)の通信スタック部分を差替可能な構造を利用した実装 ◦ HttpWebRequestを用いて実装されているため、Unity WebGLでは動作しないため不採用

    2. GrpcWebUnity ◦ Transitional Forms社が公開しているOSS ◦ Grpc.Net.Client.Webと同様に、Grpc.Netの通信スタックを差し替えた実装 ◦ WebGL向けにJavaScriptのfetch APIを利用しており、Unity WebGLで動作する ▪ JavaScriptプラグインはEditorでは動作しないため、ビルド後とEditorで実装が異なる ◦ Transitional Forms社の他のOSSに依存していて、コード量と依存が多いため不採用 3. grpc-webの通信スタックの独自実装 ◦ Grpc.Net/Coreに依存せずUnity WebGLで動作するものを1から実装する ◦ 採用 17
  7. grpc-web実装をなぜ一から実装したのか • grpc-webの仕様は非常にシンプルで、grpcとの差分はあまり大きくなく、簡単に実装できる ◦ grpc自体も本来は非常にシンプルで、実際のHTTP通信以外で必要な実装量は少ない ◦ Takashoで使うUnaryによる通信を実現できればよく、必要な実装量はさらに少なくなる ◦ 実際かかった工数も1~2週程度 •

    TakashoクライアントSDKはgRPCを隠蔽している為、Grpc.Netをベースにする必要がない ◦ Takasho SDKで抽象化がなされているので、Grpc公式実装による抽象化を必要としていない ◦ Grpc公式実装なしでも、これまでのgRPC版と同じように使えるインターフェースを実現できる ◦ Unity WebGLで重要なコードサイズに考慮したシンプルな実装を提供できるようになる ◦ Editorとランタイムでの実装を統一できる ▪ JavaScriptによる実装はEditorで動作しない ▪ UnityWebRequestを利用することで解決 18
  8. Content-Typeによる通信モード分岐 • Dataの値はContent-Typeの値によって、テキストとバイナリどちらかの形式になる ◦ ブラウザのAPIの挙動による制約の為、利用できる機能範囲に差異がある • テキスト形式(application/grpc-web-text) ◦ DataにProtocol BuffersのバイナリをBase64でテキストにエンコードした上で乗せる

    ◦ UnaryとServer Side Streamingが利用できる • バイナリ形式(application/grpc-web+proto) ◦ Dataに直接Protocol Buffersバイナリを乗せる ◦ Unaryのみ利用できる • TakashoクライアントSDKでは、バイナリ形式のみをサポートする形で実装 ◦ TakashoはUnaryのみしか利用しないため、テキスト形式を利用するメリットが無い ◦ 暗号化で独自のエンコードもかけるため、Base64エンコードの負荷は避けておきたい 20
  9. Awaitableパターンによる非同期対応 23 • asyncを使うと内部的にステートマシンがコード生成されてしまう ため、コードサイズが増加する ◦ Callbackによるインターフェースがコードサイズ面では優秀だが、 使い勝手が悪すぎる • Task以外でも、Awaitableパターンを満たす型はasyncメソッド内か

    らawaitキーワードで非同期に終了を待つことが出来る ◦ SDKの内部的にはCallbackベースで実装しつつ、呼び出し側では awaitで待機できる ◦ 呼び出すメソッド側ではasyncで定義する必要がある • Awaitableパターンを独自に実装することで、(SDK自体で) asyncを使わずコードサイズの増加を抑えつつ、awaitに対応する
  10. AwaitableとCoroutineの両対応 • Awaitableだけでなく、IEnumeratorも実装することでUnityの Coroutineからも待機可能にする ◦ yield returnで待機可能 • asyncと同様にクライアントSDK内部ではyieldも使わない ◦

    yieldもステートマシンが生成されてしまうため • 利用側が非同期処理の手法を自由に選択できるようになる ◦ 1. 同期コンテキストの無効化した上でTask<T>を利用 ◦ 2. UniTaskを導入 ◦ 3. async/awaitを使わずにCoroutineを利用 ◦ 4. Callbackを利用 ◦ 5. 同期的にStatusをポーリング 24
  11. grpc-webを独自実装するにあたっての作業進行の工夫 • 新規にプロトコルを利用する場合、サーバ/クライアント両方が手探りな状態での開発は非効率 ◦ 問題があった場合に、どちらの実装に問題があるかの切り分けが難しくなってしまう • grpcwebproxyを用いて、既存のサーバを手元でgrpc-web化してクライアントSDKを先行開発 ◦ 手元で単純なEcho gRPCサーバをgrpc-web化し、

    (暗号化等なしで)接続可能なクライアントSDKを実装 ◦ 次に手元で既存のTakashoサーバをgrpc-web化し、接続可能なクライアントSDKを実装 ◦ クライアントSDKの完成後、実際の開発環境のTakashoサーバを正規にgrpc-web化して確認 • 既存の動作する実装に対して接続可能な実装/構成を徐々に用意していくアプローチ ◦ 確度の高い動作確認が可能になる ▪ クライアントSDKの対応時には既存ソフトウェアを活かして段階的に確認できる ▪ サーバ側の対応時にはすでに接続確認に使えるクライアントSDKが存在する ◦ それぞれの作業をできるだけ独立して、高速に進めることができる ▪ 最後の段階まで、サーバ担当の工数は不要 ▪ サーバ側の構成時もコミュニケーションコストが非常に低い 25