『マイクリプトヒーローズ』の技術スタック ブロックチェーンゲームを支える仕組み

マイクリプトヒーローズの作り方 #2/2 >> 1はこちら

gRPCの概要

上野広伸氏(以下、上野):続いてgRPCについてです。gRPCをご存じの方ってどれぐらいいらっしゃいますか?

(会場挙手)

gRPCは何年も前から出ている技術で、Googleが開発したRPCの呼び出しプロトコルというかフレームワークというか、ネットワークミドルウェアみたいなものです。主な用途としてはServer-To-Serverの呼び出しになります。それをServer-To-Webの呼び出しにも拡張したのがgRPC-Webというものになります。

gRPCは通信プロトコルは差し替え可能ですが、標準的には、サーバとサーバの間、サーバとクライアントの間で、Protocol Buffersを用いてやり取りすることになります。

なおかつ、gRPC-Webに関してはHTTP2を用いることが標準的になっていて、並列呼び出しや双方向呼び出し、ストリーミング呼び出しなども可能になります。可能になるだけで、今のgRPC-Webが全部満たしているかというと実はそうでもないんですが、ゆくゆくはこれらも満たしていくらしいです。ですので、サーバからデータを呼んだり、双方向呼び出してチャットみたいな使い方をするといったことができるので、かなり良いです。

gRPCについてよく調べてみると、Googleが「こういうオープンソースがありますよ」と単に出しているだけでなく、Googleの各サービスもこれでつながっていることがわかります。その仕組みを世に出しているわけです。

Protocol Buffersを使った方はご存じだと思いますが、「.protoファイル」というものから、いろんなプログラム言語に勝手に自動生成されます。その自動生成されたソースは、サーバもしくはサーバクライアント間の通信もできるので、非常に楽です。

その結果、サクサク動いて、フロントとバックが仕様のすり合わせが楽です。つまり、.protoファイルを見ればいいわけです。.protoファイルから、クライアントはJavascriptで、サーバはGo言語で作っているんですが、どっちの言語もソースコードも自動生成されます。

しかも.protoファイルに「このAPIは配列、これは構造的に持って呼び出しましょう」ということを書けば、そのままの構造体としてJavaScriptとかGo言語で扱えます。しかもAPI一覧もそこに書かれるかたちになるので、結果的にそこに関する仕様書はまったくいらないんですね。「.protoファイル見りゃいいじゃん」みたいな話になるので。

そこのすり合わせが非常に楽で、技術将来性的にも良い選択になったと思います。技術将来性というのは、来たるべきマイクロサービスの時代になってもこれがおそらく主流になるので、「結局、これに則ったほうがいいじゃん」みたいな話です。

これは、gRPC-Webの呼出例です。

HTTP2ってWebブラウザからでも標準的に100並列ぐらいで動作するようになっているので、何も考えなくても全体としてサクッと動きます。

それだけ並列的に動かせるので、「この呼び出しとこの呼び出しはなるべくまとめて」といったことは考えなくても、すごくシンプルなAPIを並べておくだけでそれぞれのAPIは結果的にシンプルな責務となって、その分レスポンスタイムも速いし、サイズも小さいし、並列で動いてサクッと動かすところまで、すべてつながっている技術です。楽ですね。

.protoファイルには書き方があるんですけが、その書き方に則って書けば、APIの種類はもちろん、リクエスト/レスポンスの型をすべて明示できます。しかも、すべて配列や構造化も可能です。

中身はProtobufなので、下位互換性を保ちながら更新可能です。型明示による齟齬のなさ、余計なドキュメントの省略、互換性を維持したバージョンアップ、しかも高速といういろんなメリットがあるので、かなりメリットのほうが大きいと思います。

型に対して1、2,3とか書いているので、「なんだこれ?」と思うかもしれませんが、これは互換性を保つために必要なことです。新しい型を追加したら増えていきますし、使わなくなったら2は欠番にしておけば、仮にこの2番のところに値が入ったリクエストが来たとしても、それを無視できます。そういうことを守っていくために、このように書いています。

また、いろいろなコードに対応しているので、gRPCのWebサイトを見れば「どの言語に対応してますよ」とたくさん出ていると思いますが、とりあえずうちではGoとJavaScriptで書いています。ちなみに、クライアントはnuxt.jsを使っています。

アーキテクチャの方針

クライアントからDBまでのアーキテクチャの方針ですが、すごくシンプルなルールでバックエンドを作りました。

サービス層と機能層とDB層があるぐらいのシンプルなもので、単純に機能はDBと一体です。機能同士は呼び出しを避けました。サービス層は機能層の機能を組み合わせてサービスを実現しましょう、というかたちになっています。

おそらくどんなアーキテクチャになったとしても、疎結合性や部品化はこういったルールで実現可能です。機能と機能を呼び出した瞬間に組み合わせ爆発が起きるので、それだけ守れば大丈夫です。

DBの使い方としては、余計なJOINしないのが僕は好きなので、やっていません。結果的に、『マイクリ』はオンラインで動いているところに関してはJOINは一切使っていません。

ProtobufとJSONの変換が標準的にサポートされているので、Protobufでクライアントとサーバをやりとりして、サーバでデータを残したくなったらJSONにして、JSONのままDBに入れるかたちにしております。

これは必ずしもいいかというと、フェーズによると思います。ゲームの初期は仕様が変わることが多いので、初めはJSONのままで扱ったほうが変更に対して強くなります。途中で「だいたい形が決まってきたね。もう仕様はあんまり変わらないよね」となってきたら、スキーマ型にしてもいいですね。そういった選択肢もありながら、まずはJSONで突っ込むのが、速く開発できるポイントかなと思っています。

アーキテクチャにまつわる補足

補足ですが、機能層を単機能にすることでTestabilityも上がりますし、性能問題などもまずはその中で解決することが可能です。基本的に性能問題が出るときって検索だと思いますが、検索もその機能に閉じて解決することが可能なので、このように作ったほうが楽です。

なおかつ、サービス層は機能という部品を組み合わせて作ります。新たなサービスと作るときに「この機能にこういうメソッドがないとダメですね」といったものがどんどん機能の中に追加されていくことで、メソッドも必要最小限ながら最大限の効果を発揮する機能として、どんどん強化されていきます。

実はMySQLにはJSON型が存在して、これを使うと壊れたJSONを受け付けないので、めっちゃ楽です。

しかも、JSONを操作するための関数がMySQLに用意されているので、結果的にクライアントとサーバでProtobufで扱われて、DBに突っ込まれるときはJSONになりますが、DBの中を見れば可読性の高いJSONのかたちで運用者には見えます。なおかつ、JSONの操作を誤らないようにMySQLのJSON関数を使えばいいわけです。

ですが、罠もあります。Go言語のProtobuf-JSON変換の仕組みで、omit emptyというものが自動生成されるコードの中に書かれています。これはどうなるかというと、ある項目に0が入っていて、0はデフォルト値なので、JSONの項目ごと消してしまいます。

JSONとしては確かにコンパクトになります。ですが必ずProtobufからJSONに変換してDBに入れるだけだならいいのですが、運用する上では、どうしてもなにかの値が足し算・引き算とかして0になることもありますよね。そうすると、JSON上は0なのか、もしくはnullなのか、どちらも検索しなければうまく求める検索結果になりません。こういった罠は若干存在します。

ちなみに、この書き方がMySQLのJSON関数の文法です。階層化された項目も$.foo.barのように指定すれば取得可能です。

フロントからDBまでProtobuf〜JSONで扱うことで、JavaScriptやGo言語のプログラム構造体は、クライアントとサーバで同じ構造で扱うことができます。完全に型も一致して、DBの構造もそのままで扱えるので、非常に楽です。階層化構造や配列を表現するためだけの無駄なDBの正規化ってあったりしますよね。そういう無駄なことはやらなくていいので楽です。

そもそもの工夫も重要である

上野広伸氏(以下、上野):とはいえ、これあらゆることに言えるますが、Web高速化する上で企画の工夫は大事です。いくら技術的に高速化しようと、Webサービスの画像が重いと処理はすぐに重くなってしまいます。

『マイクリ』はドット絵なので、ドット絵のデータは10KB程度なんですが、そういった画像ファイルを使っています。さらに、そのドット絵を普通にWebで拡大するとぼやけてしまうので、CSSのpixelatedで指定することでdot-to-dotで拡大して軽くしようとしています。

このあたりは、使う技術の特性に合わせてどのようなテイストにするかというところがあります。企画と技術は切っても切れない関係にあるので、はっきり言って技術だけでどうにかできる問題ではありません。

あとは雑感です。

技術的親和性の高いアーキテクチャについて、これは『マイクリ』の今後にもつながってくるので、まだ構想段階なんですが。

現在オフチェーンで作ってるんですが、将来的に技術最適にしていくなかで、条件に合わせて考えていかなければいけません。親和性の高いアーキテクチャとしては、イベントソーシングをオフチェーンでも採用していくことが、後にブロックチェーン化する場合にはいいのではないかと思います。

もしくは、ブロックチェーンゲームはロジックの変更は許されるのですが、いったんユーザーの手元に渡ったデータをいじくり回すのはよくないこととされているので、マスターデータを変えないことが必要です。「immutable master data」みたいな考え方がこれから出てくるのではないかと考えております。

あと、web3の世界というか、スマートコントラクトの世界になると、そのスマートコントラクトというものすごくマイクロなサービスを連携させて巨大なサービスを作るという考え方が将来訪れることになるのですが、「そのときには、マイクロサービス一つひとつがビジネス的にも持続可能でないといけない」という結論に個人的には至っております。

ですので、来たるweb3の世界は、マイクロサービス単体でビジネスとして成立するサービスでないと、継続的には存在し得ないのではないかと思っております。

最後に宣伝です。そういった難しいことがあるブロックチェーンゲームをもっと気軽に作ってみようということで、6月より『My Crypto Heroes』の技術・ノウハウをパートナーに提供する開発支援プログラム「MCH+」を開始しております。

これは開発支援・エコシステム構築支援・ファイナンス支援・人材育成支援の4つのプログラムを合わせたものになっています。

「MCH+」で描く未来としては、多くのブロックチェーンゲームがこの「MCH+」を通じて簡単にリリースできて、そのゲーム内のデジタルアセットがゲーム間でも流通させることがブロックチェーンであれば可能なので、ゲーム内データが流通し、価値を持つようになる世界を築けたらおもしろいなと考えております。

「MCH+」の実績の第1弾としては、小澤さんのところのCrypto Gamesの『CryptoSpells』というタイトルです。

『CryptoSpells』、なんと、初日でEtherの売上が600ETHいきました。

おめでとうございます。

(会場拍手)

昨今レッドオーシャン化というかブラックオーシャン化してきているソーシャルゲームなどではなかなか難しい数字なんじゃないかと思います。今はまだブロックチェーンゲームは黎明期で、ブロックチェーンゲームを出すプレイヤーも少ないので、まだいろいろチャンスがあるような世界となっております。

「MCH+」のアクセラレータープログラムとして、gumiさんとも共同で、Tokyo XR Startupsというものも行っております、最大500ETHの開発資金も支援してくださるそうです。

というわけで、気になった人はまずはご登録を。www.mch.plusをよろしくお願いいたします。登録画面があるので。

以上です。

(会場拍手)

不正がないことを証明する仕組み

司会者:ありがとうございました。お時間少しありますので、せっかくなのでご質問がある方は受け付けたいと思います。

質問者1:今日は貴重なお話ありがとうございました。何点かあるのですが、まず、IPFSを使っていらっしゃいますよね。

上野:そうですね。

質問者1:あれって画像を入れたりしているんですか?

上野:そうです。『マイクリ』はヒーローの見た目の画像をユーザーが変更することができて、その画像データはなるべく非中央集権で残していこうということで、IPFSに画像を収めています。

質問者1:あと、NFTについてで、数の保証って「実際このカードは何枚よ」的なものってユーザー側から見えるようにして……。

上野:見えます。

質問者1:あっ、そうなんですね。

上野:NFTのスマートコントラクトはEthereum上にデプロイして使うので、そこで見えるようにしている感じです。もちろん見せないこともできるのですが、見えたほうが確実に100個までだとわかるので、見えるかたちにしています。

質問者1:コントラクト自体はGitHubで公開しているんですか? データ部分というか、トークンのコントロール部分というのか。

上野:そうですね。それに加えてEthereum上では、最も有名なEthereumのエクスプローラーサイトとして「Etherscan」というものがあります。Etherscan上でデプロイされているソースの中身を見れるようになっています。

質問者1:インターフェースだけでなく、ちゃんと不正はないことがそこで見える?

上野:そうです。ソースを明示していて、なおかつそのソースを直接サイト上から叩けるようになっていて、その残数を見るためのメソッドを叩くと残数や発行数が確認できるようになっています。また、「この数字は本当かな?」ということはソースを読めば確かに合っていることが確認できるので、そういう世界で動いています。

質問者1:あと、途中でGAS代がすごくかかっているとおっしゃったと思いますが、ある程度動的に変えていらっしゃるのでしょうか?

上野:ある程度は、ですね(笑)。そこまで最適にコントロールできているわけではありません。

質問者1:実際どれぐらいかかっていますか? 更新頻度によると思いますが、おそらくトークンの移動が発生するとけっこうな頻度になってしまうのではないかと思うのですが。

上野:まあまあかかってますね(笑)。gatewayコントラクトを動かすためのアドレスのEtherがどんどんどんどん減っていくんですよね。だから、ちょっと経ったら5ETH入れ、ちょっと経ったら5ETH入れ、という形になっているので、ちゃんとしないとインフラコストは馬鹿にできないです。

質問者1:ありがとうございました。

gRPCとRemote Procedure Callの違い

質問者2:gRPCの、昔でいうRemote Procedure Callとの違いというのは、サクサクと動くか、昔と同じように動くのか、Googleのやつはどのように動くのでしょうか?

上野:そういう意味では、標準的に「こうすればこういうプログラミング言語で動くことを保証していますよ」というGoogleの裏付けがある安心感があるというか。オープンソースではありますが、Googleで、しかも数年間もうメンテされているところを見ると「これ使っておけば安心だな」という気持ちもあります。

「Googleもクラウドをやっているし、Kubernetesにもけっこう力を入れているし、なおかつgRPCでServer-To-Serverの呼び出しでこうしましょうと言っている中で、これに則っていたほうがイケてるんじゃないかな」とか、そういう単純な判断もあります。

質問者2:なるほど。昔のはよくやってたんですけど、最近はやっていなかったので、パフォーマンスはどういうふうになるのかなって。

上野:正直、パフォーマンスも品質的な部分はかなり良いです。かなり良い上に、将来性も今言った理由でまあまああるので。

質問者2:どんどん使われるだろうと。

上野:そうですね。はい。

質問者2:ありがとうございます。

質問者3:弊社でもブロックチェーンゲームを開発しているのですが、実はinfura.ioを使っています。先ほどinfura.ioだと安定しないと言われたのでちょっと驚いたのですが、nodeとinfura.ioでどれぐらい差があったのか、どういった経緯があったのかなと。

上野:とはいえ、infura.ioは普通に使う分にはいいと思うんですよね。ただ、たまにある「eventがうまく取れない」みたいなところがあると、それがたとえ月1だったとしてもインフラエンジニアは困ってしまいます。そういうレベルの話ではあります。

だから、普通に使っている分には気づかないというか、別にそこまで問題ないのですが、とはいえ月1で取れないということになってしまうと、しんどいなと。

質問者3:infura.ioを使いつつ、バックアップで安定したnodeを見つけて両方使うみたいにして……。

上野:それが一番良いですね。もちろんユーザーがブラウザから直接web3の情報を見たりもするので、「そこはinfura.io使ってくださいね」と。結局、結果的にMetaMaskを通してinfura.ioを使っているので、そこは別にいいんですが。gatewayサーバみたいなところのつなぎはけっこうセンシティブなので。

質問者3:転送したのに来ないみたいな、そんなことが起きてしまうと。

上野:そうです。なので、たとえ有料のEthereum nodeを使うとしても、ピンポイントで可用性高くやらないといけないところだけやると。

質問者3:わかりました。ありがとうございます。

司会者:ありがとうございました。では、一度こちらで区切らせていただきます。あらためて、ありがとうございました。

(会場拍手)