僕の考えた最強のリアルタイム通信基盤(検討編)〜みんなでライブの場合〜

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

#概要

あんさんぶるスターズ！！Musicのエンジニアリーダーをしています。
複数人で音楽ゲームを行う「みんなでライブ」で使われたリアルタイム通信基盤について書きたいと思います。
今回は技術選定の段階で検討した内容について紹介し、Redis Streamsを用いたスケーラブルな基盤を提案します。

#みんなでライブについて

みんなでライブは、あんスタ！！Musicにおいて複数人で音楽ゲームを行うシステムです。
画面遷移の同期やスタンプ送信、コンボ数の共有などがリアルタイム通信を用いて行われています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196413/e1a1c64d-d684-3e20-4da0-24e2ac317abb.png)

#リアルタイム通信の技術選定について

弊社ではポーリングを用いた複数人参加のバトルや、[Photon](https://www.photonengine.com/ja/photon)を用いた協力プレイなど実績があります。
あんスタ！！Musicのみんなでライブでは同時接続1万人以上を想定しており、それをさばけるリアルタイム通信基盤が必要でした。
インフラグループの発足により、新たにインフラ基盤を整えられる状況でもあったので、改めてリアルタイム通信技術について検討しました。

##Unity公式が勧めているリアルタイム通信技術選定

[自分のゲームに適したネットコードを選ぼう](https://blogs.unity3d.com/jp/2020/09/08/choosing-the-right-netcode-for-your-game/)を見ていくと、音楽ゲームの参加者は5人であり複雑な処理を行う必要はないため、Photon PUNが最適なソリューションだと考えられます。
ただ、同時接続1万以上を想定しており、スケーラビリティも考える必要がありました。
リアルタイム通信が必要な処理に複雑なものを想定していなかったので、Photonでスケーラビリティを実現するための努力をするよりは単純な構成のWebSocketサーバーを使った方が良いと判断しました。
(もし2021年なら[DOTS-Netcode](https://blogs.unity3d.com/jp/2019/06/13/navigating-unitys-multiplayer-netcode-transition/)を使っていたと思います。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196413/1bf932c0-34bd-0857-7c97-8764affbd64e.png)

##スケーラブルなWebSocketサーバー

初期の段階では、[AWSのAPI Gateway](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-websocket-api-overview.html)でWebSocketを使おうと考えていました。
みんなでライブリリースまで3ヶ月程度しかなかったので、より簡単な方法で実現したいと考えたからです。
これについてインフラグループに相談に乗ってもらったのですが、Lambdaの管理を考えなければならず逆に手間がかかってしまうということで断念しました。

その後、より鉄板のよくある構成でいけたらということで下記のような構成を考えました。
ALBでWebSocketサーバーのロードバランスができるので、適当に繋いだらリアルタイム通信ができて、WebSocketサーバーもRedisクラスタもスケーラブルという完璧な構成です(この時はそう思っていました)！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196413/b17f5f61-eff8-a3a9-3544-ae9622e426a7.png)

##Redis Pub/Subはスケーラブルではない

再度、インフラグループに相談したところ、Redis Pub/SubをCluster構成で使うと規模の増大によってネットワーク負荷が上がりスケールしないことがわかりました([Redis Cluster PubSub - Scalability Issues](https://github.com/redis/redis/issues/2672))。
簡単にスケールして、ラフに接続できる完璧なリアルタイム通信基盤の夢はもろくも崩れ去ったのです。

Redisを使わない案や、Redis1台でさばける単位で分ける案などあったのですが、どれも3ヶ月で検証が終わってリリースできるような構成に感じられませんでした。
(検証に使えるのは3ヶ月よりずっと短い期間です)
みんなでライブの実現は崖っぷちに立たされたのでした。

##突如現れた救世主 Redis Streams

Redis Pub/SubのCluster構成がなかなか諦めきれず、何か良い方法がないか調べていました。
よくよく読んでいくと、Redis Pub/Subの問題について議論されているissueに下記のようなコメントが見つかりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196413/ce6303d7-c2dd-3f41-dec9-58820c524bf5.png)
https://github.com/redis/redis/issues/2672#issuecomment-599438473

[Redis Streams](https://redis.io/topics/streams-intro)ならスケーラブルで、Pub/Subのように使える！
これが本当なら、先程の構成のRedis Pub/Sub部分をRedis Streamsに変更するだけでうまくいくはずです。
Redis Streamsについて何も知らない状態でしたが、みんなでライブの実現のためこの機能にかけてみることにしました。

##Redis Streamsを用いたリアルタイム通信基盤

最初に検証したのは、そもそもNode.jsでRedis Streamsが使えるのかどうかということでした。
試行錯誤はありましたが、[ioredis](https://github.com/luin/ioredis/blob/master/examples/redis_streams.js)を使うことでRedis Streamsを使えそうということがわかりました。

次に検証したのは、ioredisを使ってどのようにPub/Subを実現するかということでした。
こちらもioredisの作者が[issue](https://github.com/luin/ioredis/issues/747#issuecomment-500735545)で実現方法を提案してくれていて、そちらを参考に実装することができました！
ついに「僕の考えた最強のリアルタイム通信基盤」が完成したのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196413/61ded09a-36d2-5903-0ded-d7f781e9265f.png)

#まとめ

こうして、みんなでライブで用いるリアルタイム通信基盤の技術選定を行うことができました。
スケーラブルであること、構築が非常に簡単であることが特徴のリアルタイム通信基盤で、個人的には最強の構成だと思っています。
明日はハンズオン形式で、実際にRedis Streamsを用いたリアルタイム通信基盤を作ってみようと思います。

# メンバー募集

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

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
[Happy Elements株式会社 採用特設サイト](https://recruit.happyelements.co.jp/)