GO/GAEで作る金融システム

それでは、GO/GAEで作る金融システムと題して、発表させていただきます。

今日は、まず会社について軽くお話しさせていただいて、金融システムを作る上で「いいぞ」と思ったことが4つあるので、それをみなさんに紹介していけたらなと思っています。

まず、会社についてご紹介します。株式会社ネクストカレンシーという会社です。DMMの完全子会社で、今、「cointap」という仮想通貨取引サービスを準備しています。

これは、この春(2018年の春にリリース予定)と書いてあるんですけれども、昨今の業界に対する規制や体制強化のためにリリースが延びていまして、現在はリリースに向け鋭意開発中です。

仮想通貨交換業と言いますと、ブロックチェーンという文脈で語られて、そのイメージを持たれる方が多いと思うんですけれども、実態としてはほとんど金融として扱われています。金融とは何かと言いますと、お客さまのお金をお預かりする業種。そのため、高い安全性が求められています。これはいろいろなところから求められています。

ただ、そうは言っても業界の流れは非常に速くなっていまして、高速な開発はそれはそれで求められています。

サーバーサイドエンジニアはやることが非常に多いんですが、時間が限られているということもあり、これを合言葉にしながらやっているというものがあります。

「Environment is Easy, Code is Simple」。日本語で言うと、環境は簡単に構築してしまって、コードは賢くやるんじゃなくて、単純にわかりやすくやっていこう、というコンセプトでやっています。

使っている技術は、インフラはほとんど「Google Cloud Platform」です。選定理由としては、本当に簡単に使えて、実務での導入実績があったからです。

使用言語については、メインとなるロジックは、ほぼGolangを使っています。一部Node.js等もあるんですけれども、これは本当に一部です。この選定理由も、Golangは非常にシンプルに扱えるからです。

開発をしていて、いろいろとやることがあったなかで、「○○はいいぞ」というようなことがいろいろと出てきたので、今日はみなさんに4つの「いいぞ」を紹介していきたいと思います。

1つ目が「『Google App Engine(GAE)』はいいぞ」、2つ目が「『Stackdriver Logging』はいいぞ」、3つ目が「Protocol Buffersはいいぞ」、4つ目が「Golangはいいぞ」です。

Google App Engineの概要

まず、「『Google App Engine(GAE)』はいいぞ」というものです。知らない人もいると思うので、簡単に説明させていただきます。

Googleが提供しているPlatform as a Service(PaaS)で、デフォルトでPython、Java、Node.js、PHP、あとGoにも標準で対応しています。非常に高い可用性を持っているのが特徴です。

何がいいのかと言いますと、とにかく楽であるということに尽きます。Webアプリケーションを作るに当たって、コードを書く以外の部分でこういったことがでてきます。

例えば、Blue/Greenデプロイができるかとか、適切にスケールアウトができるかとか、セキュリティ、例えばミドルウェアであるとか、踏み台にならないようなセキュリティ設定ができるか、という問題です。

「Google App Engine」を使うことで、このあたりの問題が簡単に解決してしまうので、今回は採用しています。

GAEの構造

なぜ簡単にできるのか、「GAE」の構造から紹介していこうと思います。「Google App Engine」とは、1つのプロジェクトに1つ存在しているサービスです。

その中にServiceという単位でアプリケーションを作成していきます。

このサービスはYAMLファイルで1つのサービスを定義することで、本当にこれだけで、最小構成であれば1つのアプリケーションを簡単に表現しきることができるので、非常にシンプルに構成できます。

分け方としては、役割として分割していくものです。例えば我々のサービスで具体的に言うと、APIが大外に向いているアプリケーションから叩かれるもので、AdminやBatchサービスは、このように動かしています。

他にもデバッグみたいなのもあります。これは自動でURLが振られるという特徴がありまして、例えば、デプロイをした後のテストも、非常に簡単にできるようになっています。

もちろん自分でURLを振ることもできるので、実際の本番環境とほぼ差異なく使うこともできます。さらにその中にVersionという単位がありまして、基本的にはこの単位でデプロイをしていきます。サービスの中に、複数のVersionを持たせることができます。

これにもURLが振られていて、例えば1人がcommitをpushしたら、環境にこのVersionでデプロイをして。

URLのパターンも、「Version1-dot-api-dot-project名」みたいに完全に決まっているので、CIで例えばE2Eテストを回すということも、非常に簡単にできるようになっています。

特徴として、「Google App Engine」上では、Versionごとにトラフィックを制御することができるようになっています。

例えば、デプロイしたばかりで、Version1にトラフィックが集中していたとすると、GAEで本当に簡単にVersion2へトラフィックを移動させられます。ちょっとおもしろいやり方としては、トラフィックを勝手にGoogle App Engine上で振り分ける。こっちのVersion1に半分、Version2に半分みたいなのを、GAEからぽちぽちできる構造になっていて、このあたりも非常に簡単にできるようになっています。

さらにその中に、Instanceという単位がありまして、これがいわゆるコンテナです。

この単位で処理をされるものになっています。もちろんこういうふうに、Version1にも2にもあります。大きいトラフィックが来たら、ばんばん簡単にスケールアウトしていって、ばしばし処理をさばいていってくれます。小さくなったら、勝手に下がっていくようになっています。

GAEの利点

先ほどの課題、Blue/Greenだとか、スケールアウトができるかとか、セキュリティ的に問題ないかという点についてです。

まずBlue/Greenについては、今ご説明したとおり、「Google App Engine」のVersion機能でほとんど解決します。トラフィックの切り替えは、GAEでぽちぽちやれると言ったんですけれども、このあたりもGracefulにできています。

今さばいているリクエストはそのままさばいて、その後からちゃんと切り替えてくれるというのを、勝手にやってくれます。

これはうちの使い方として1つあるんですけれども、メンテナンス用のVersionを事前にデプロイしておくことで、メンテナンス時にVersion切り替えをして、メンテナンスのVersionに切り替えるという使い方をして、メンテナンスモードの切り替えも簡単にできるようにしています。

スケールインとスケールアウトは本当に簡単です。簡単な設定さえしておけば、勝手にやってくれます。スケールアウトの時間も、Goの場合は立ち上がりに数十ミリ秒ほどなので、ほとんど考えることはないです。スケールアウトにも2種類あって、大量のトラフィックのために、Automaticで勝手にやってくれるものがあります。

実はリクエストが10分でタイムアウトしてしまうという制約があり、Batch処理は長いので、Basicに切り替えるなどしますが、これくらいしか本当にやることはありません。

セキュリティについてです。先ほどあったInstanceというものは基本的な構成として侵入が不可能な形になっています。DDoS対策についても、同様に「GAE」でやってくれています。

ミドルウェアは、ほぼ自分たちで個別の管理は行っていないので、基本的に自社の管理に起因する脆弱性の問題はなく、全部Googleがやってくれています。ただし、もちろんコードのセキュリティは別です。あくまで、インフラ構築で悩まなくていいということで、「『Google App Engine』はいいぞ」という話でした。

Stackdriver Loggingの良いところ

次は、「Stackdriver Logging」です。これもGoogleのサービスで、フルマネージドなロギングサービスです。

GCPで出力されるログが、全部ここに集約されるようになっています。Pub/Subという、GCPのQueueサービスに簡単に流せる仕組みが最初から用意されているので、加工や通知が簡単にできるようになっています。

フィルタ機能も、「Stackdriver Logging」だけでもそれなりに充実しています。(言うのが)2回目なんですけれども、GCPで出力されるログが全部集約されます。何がいいかと言うと、2回言ったから、3回目があって……GCPで出力されるログが全部集約されることなんです。なんでこんなに押しているかと言うと、自分がGCPを最初に触って、一番感動したサービスなので、「何もしなくても、全部ログがここに出てくるんだ」というのをただ共有したいがために、入れてあります。

ログなので見せられないものが多く、切り取ってすごく雑な感じになっているんですが、こういう画面になっています。

何も設定しなくても、こういうふうに1リクエスト単位で全部のログが出てくれるようになっています。日付だとか、POSTで来て404で返したよとか、93バイトで、3ミリセックで、「Google App Engine」のサービスだよとか、このPaaSに来たよというものとか、本当に何もしなくてもここに来ます。

中を見てみても、ログ出力したのが全部(出てきます)。

これはinfoの情報、例えばEndPointと書いてあるのは、自分がコード上で書いたものが出てきたり、panicのエラーとかもここに出てきたりします。細かい情報は全部ここに書いてあります。これプルダウンでフィルタかけられるんですけれども、これだけのサービスが最初から対応しています。

細かいデバッグAPIや、「Google App Engine」のAPI、重大なエラーのレベルでも絞ることができます。

また、こんな感じで細かいフィルタもかけることができます。

これを説明したかっただけなので、ログ収集を何も考えていないんです。

全部ここに集約されているだけなので。それを「いいぞ」と言いたかっただけという、スライドになりました(笑)。

(会場笑)

Protocol Buffersの利点

次は、「Protocol Buffersはいいぞ」です。突然ですが、みなさんに質問です。APIドキュメントについて、どうされていますか? 「社内Wikiに書いている」という方、ちょっと手を挙げてもらえますか。

(会場挙手)

おお、いらっしゃる、いらっしゃる。次、「『Swagger』とか『Blueprint』を使っているよ」という人たちいますか。

(会場挙手)

お、けっこういますね。じゃあ、「JSON Schema、けっこうがんばって書いています」という人だとどうでしょうか。お、いない。いない。

(会場笑)

なるほど。「プロジェクトのREADMEに書いて、ちゃんとやっているよ」という人は? これも、いない。じゃあ、もう1つ質問なんですけれども、メンテナンスされていますか? あ、これは挙手しなくて大丈夫です。

(会場笑)

APIドキュメントは、一般的に実装とどんどん乖離してしまうという問題が起きがちです。なぜ乖離してしまうかと言うと、ドキュメントを書かなくても実装ができてしまうからなのです。そのため、ドキュメントが全然追従していかないという問題を、どのプロジェクトも抱えていると思います。

ドキュメントを記述するツールもあるにはあるんですけれども、習得コストが高いということもあって、なかなかメンバーに根付かない。別にドキュメントを書いたからって、実装に影響があるわけではないものも多いので、クライアントとサーバーがやり取りをして初めて、「これ違うよね」と気付くことがあります。

そこで、我々が使っているのがProtocol Buffersです。

Googleが開発をしているインターフェース定義言語で、シンプルで高速をテーマにしていて、主要な言語には基本的には対応しています。うちで使っているのだとGolang、あとJavaとSwfitとJavascriptでも利用できるようになっています。たまにgRPCと同じ文脈で語る人がいるんですけれども、違うものです。

厳密に言うと違うんですけれども、gRPCはプロトコル、TCPやHTTPの仲間で、Protocol BuffersはXMLやJSONの仲間に近いものです。

これ、マサカリは投げないでください(笑)。

(会場笑)

我々がやっているのは、Protocol BuffersをHTTPの上に乗せるという話で、やっていることとしては本当にシンプルです。通常、みなさんがJSONで、HTTPでクライアント・サーバーやり取りしているのと同じように、Protocol Buffersで作成したBinaryを、ただBodyに乗せているだけです。

Protocol Buffersの構造

Protocol Buffersの構造を知らない方も多いと思うので、ちょっと説明しようと思います。Protocol Buffersはこういう構造をしています。

ここはおまじないです。大丈夫です。ここもoptionって書いてあって、goとかjavaとかswiftと書いてあるんで、それっぽいoptionなんだなと思ってくれれば大丈夫です。

主要なのはここです。

message EchoRequestとかmassage EchoResponseみたいな、massageを定義していて、これが、いわゆるプログラムで言うところの構造体に近い定義になっています。このServiceがRPCなので、叩かれるメソッドの名前を定義するものです。

これをprotocというコマンドでデプロイ、pluginとかいろいろあるんですけれども、これでコンパイルをすると、こういうGolangのファイルが勝手にできあがります。

先ほどのEchoRequestという構造体がそのままできあがり、中にあったmessageというものがそのまま反映されます。またServiceもサーバーというかたちで、インターフェースで定義されています。

これはJavaでもちゃんとビルドできるようになっていて。先ほどのプロトファイルをJavaでビルドしたものが、これです。

これは本当は1,000行あったものを17行にしています。こういうふうにEchoRequestがあって、messageがここに定義されています。これを各言語のライブラリに通して、Binaryにして、サーバーとクライアント間でやり取りをするものです。

ドキュメントと実装がほとんど乖離しない

これの何がいいかと言うと、ドキュメントと実装がほとんど乖離しないということです。Protocol Buffersは、記法が非常にわかりやすくて、コメントもちゃんと書ける構造になっているので、「このResponse・Requestは何か?」というのを、そこに書くことができます。また、intとかdouble、repeated、Mapという名前のデータ表現の方法が豊かです。

さらに、各言語のコードそのものを生成して使うので、変更があった場合、とくに型があるような言語であれば、ビルド時に気付くことができます。

わかりやすいという話です。例えば、赤く四角で囲ったここを見て、プログラムを一通り書いたことがある人だったら、「このmsgはstringなんだな」というのが、ぱっと見てわかると思います。

こちらにあるEchoのRequestのstructも、こういうふうに、stringの型でちゃんと定義されているようになっています。

このmessageは名前を変えたら、こっちのビルドしたものも変わるので、変わっていたらもちろんコンパイルエラーになります。

APIドキュメントをProtocol Buffersに寄せられる点が非常に便利で、このおかげで我々、APIドキュメントをクライアントとやり取りする際はほとんど書かずに、サーバーサイドがProtocol Buffersを書いて、それをクライアントサイドが引っ張ってきてビルドするだけで、そのすり合わせをほとんどする必要がなくなっています。

ということで、APIドキュメントとの実装をほとんど気にせずに、開発を続けられています。「Protocol Buffersはいいぞ」という話でした。

GolangとJavascriptを比較する

次は、「Golangはいいぞ」です。いきなり何がいいかという話をするんですけれども、シンプルであるという話です。言語仕様の理解が容易だとか、並行処理の扱いが簡単にできるとか、ビルドの時間が短いという良さがあります。

みなさん、こういうのはもう聞き飽きたんじゃないかなという話なんですよね。みんな話しているんですけれども、「本当にそれ、どこがいいの?」みたいな話を聞きたいんじゃないかなということで、具体的な比較をしていきます。これちょっと危ないな。はい、いきます。Javascriptと比較をしようと話です(笑)。

(会場笑)

これは偶数の値を倍にしたリストを返すみたいな処理です。「読みやすいのはどっち?」という話で、私は実はJavascriptが読みやすいと思います。「生産性が高いのはどっちか?」という話としては、どっちかな……このくらいだったらJavascriptがいいかなと思ったりもします。

「実行の効率がいいのはどっちか?」という話については、Javascriptのコンパイルを知らないんですけれども、Golangが速いんじゃないんですかね、というような。それで、コンパイラが解釈するとか知らないです、という感じです。ここで言いたいことは何かと言うと、ぶっちゃけどっちでもいいという話なんです。

コードには、そもそも意図を表現したくて、どんな記法であっても、意図そのものをコードに表現していきたいというのが重要です。

例えば、intの配列の偶数の値を2倍にする意図は何かという話です。これは数値計算なのか、Userのステータス遷移なのか、店舗のStarの倍率がこういうふうにサーバーのドメインで決まっているよという話なのかが重要であって。

このコード見てみると、説明もしていないので意図がないのは当たり前なんですけれども、ぶっちゃけ言うとどちらも意図がない。

だから、どっちでもいいよねという話をしています。

Golangを使う理由

じゃあ、なんで我々はGolangを使っているのかと言いますと、ビジネスロジックに集中しやすいというメリットがあるからです。

例えば、Javascript。これはすごく局所的なので、いろいろあると思います。

例えば、配列の要素を特定の条件で絞りたいとなったらfilterがあるよとか、要素をすべて操作したいとなったらmapやReduceみたいなのがあるよとなって。でも、これ全部forループでも実現できます。

その点Golangは、高級な機能がないと言われているとおり、本当にforとifしかないので、配列を操作したくなったら、だいたいforを使うしかありませんが、我々はこれでいいと思っています。

先ほど、仮想通貨交換業は金融だと言ったんですけれども、金融業界特有のドメイン知識は非常に膨大になっています。我々はエンジニアで、エンジニアリングの専門家ではありますが、金融そのものの専門家では決してないので、どちらかと言うと、社内にいる金融業界の出身者など、専門知識を強く持っている人たちとのコミュニケーションに、最大限時間を使っていきたいというのがあります。

自分自身でもインプットをしていきたいため、シンプルに構成されているGolangはアプリケーションを書くことに集中できるので、「Golangはいいぞ」という話です。4つの「いいぞ」を紹介してきました。

最後のところについて、とくにいろいろとお話ししたいことがある人も、もしかしたらいらっしゃるかもしれないんですけれども、そういうのは懇親会でお話ししていきましょう。また、一緒に働いてくれる人も募集していますので、ちょっとお話を聞きたいな、という気持ちがあったら、どうぞ懇親会でよろしくお願いします。以上です。

(会場拍手)