初めて で大規模を作り得
た教訓
golang.tokyo #6
■ この発表では golang で大規模な Microservices 構成のシステム -- AndApp -- を作った時に得た教訓をつらつらと語っていきます⁃ 成功談というよりむしろ失敗談の話と、そこから得た教訓の話をしていき
ます⁃ 初めて golang 開発にチャレンジする人たちの参考になれば
■ 初めて Golang を使い、手探りで作り上げていった部署の話を聞いて「あるある」も「ないない」もお楽しみいただければと思います
の基本構成 アーキテクチャ
■■
■ より
教訓 フレームワークに拘らない
■ 新規プラットフォームの立ち上げにあたりテクノロジースタックを刷新⁃ を基本構成に⁃ メンバーの開発経験は主に オンプレミス
■ のベストプラクティスを調査⁃ まずフレームワークを調査し始める⁃ 他の言語におけるフレームワークの成功体験⁃ チーム開発でもコードに統一感がでるし⁃ 言語に熟練していない人でも「それなり」に書けるし⁃ ↑ 部署として に初挑戦するだけにこれが重要だった
で始めた 開発
■ 当時特に話題性のあったフレームワーク■ 良かった点
⁃ シンプルで扱いやすかった⁃ とのインテグレーションもサンプルにあった⁃ 問題
• から を派生させ回避
• の一貫性をキープ
■ 困ったところ⁃ ワイルドカードパスを共有したルーティング問題
• 例えば以下のようなエントリーでコンフリクトが発生する• 例
⁃⁃
• が内部で利用する の制限
gin.Context(App Engine)
context.Context派生
一部で を導入し始める
■ に続いて話題性のあったフレームワーク■ 良かった点
⁃ ワイルドカードパスを共有したルーティングが可能⁃ 特に アプリ系のコンポーネント開発が捗る
■ 困った点⁃ 設計がガリガリ書き換わる⁃ のリリースノートにて
•• コンテキストの派生が不可能に…
⁃ 一時、作者が別の フレームワークの開発に勤しむ• の開発やめるの…?
■ その他困ったこと⁃ と の両対応
結果 は 対応を諦め のまま使う。新規のコンポーネントではなるべくを使わない流れに
そもそも でフレームワークに拘ることはない
■ 洗練された パッケージがある⁃ で事足りるのであればそれでもよい⁃ 実際いくつかのコンポーネントは で作っている⁃ など足りない部分は都度ライブラリで補完していけばよい
■ は言語仕様がシンプルかつ がある⁃ フレームワークに乗らなくてもコーディングスタイルは似てくる
■ ウェブアプリケーションフレームワークに乗っかるのであれば運命を共にする覚悟で
■ で が パッケージに取り込まれたことでまたひと波ありそう
フレームワークに拘らない、振り回されない!
教訓 を尊重する
■ 独自エラー型の使い方でハマった話■ 独自 型の使い方でハマった話■ は として使おう
独自のエラー型を定義
■ 独自のエラー型を定義して利用することに
■ 一般的な 関数の戻り値は インタフェース■ しかし我々は独自のエラー型に寄せることにした
⁃ なぜなら インタフェースからのキャストが面倒だから⁃ すべての関数の戻り値を寄せてしまえばいいと思っていた
思わぬ盲点
■ 以下のコードはうまく動かない。なぜか
には型があるの罠
■ の型問題■ インタフェースと独自エラー型を混在した時に発生
⁃ すべてを独自エラー型に寄せきれないケースが存在する• 例 などのサードパーティ関数
■ 素直に他の関数にならって インタフェースに統一すれば良かった■ キャストがめんどくさい問題は便利関数でどうにでもなる
⁃ 例
独自のエラー型を作っても良いが標準の インタフェースを尊重すべし
独自のコンテキスト型を定義
■ 独自のコンテキスト型においても似たような失敗⁃ リクエストスコープの値を簡単に取れるよう を定義⁃ アプリケーションで引き回すのを ではなく に寄せた
■ きちんとコンテキストを派生させていれば問題なさそうに思えるが…■ 例えば以下の様なコードを書く時に困る
gin.Context(App Engine)
context.Context派生
MyContext派生
context.Context 型になってしまうため、 *MyContext 型の関数に渡せなくなってしまう
は として扱おう
■ の定義されたものは積極的に で扱うべし⁃ 標準ライブラリや他のライブラリとの連携がシンプルになる⁃ の活用によってテストが楽になるという副次的な効果もある
教訓 は遅い
■ 皆さんバリデーションどうしていますか?⁃ 弊社では を利用
■ での として以下が候補に上がった⁃
• で行くと一番メジャーなバリデータ• 動的に を読み込みながらバリデーションしていく
⁃• 国産• のバリデーションコードを生成
■ 以下の理由により を選択⁃ スキーマを複数分割したかった⁃ スキーマをローカルファイルから読み込みたかった
■ この要件を容易に満たしてくれたのが
複数のスキーマを読み込むため を選択
パフォーマンステストで問題発覚
■ 予想以上にアプリケーションの動作が遅いことが発覚■ 調査の結果バリデーションの有無でパフォーマンスに顕著な差が出ることがわ
かった⁃ バリデータあり
•⁃ バリデータなし
•■ を使って のテストスイートを実行した結果を解析
⁃ の 時間が 動的にスキーマを解析する部分にくわれていることが発覚
何が問題だったのか
■ 氏による へのチューニングの数々■ チューニング マッチのキャッシュ
⁃ オリジナル版はパースのたびに コンパイルしていた…⁃ コンパイル結果のキャッシュを導入
■ チューニング 多用部のロジックチューニング⁃ 動的にスキーマを解析する部分が を多用していた⁃ これはどうしようも無かったのでロジックのムダをとことん省いた
■ 最終的に約 倍の改善に成功
■ そもそも で頑張ればさらに段違いの結果に
オリジナル版 7280 msec
チューニング1 3340 msec
チューニング1 + チューニング 2 2700 msec
に関わらず 気をつけないといけないもの
■ などコスト高の操作を意識する⁃ 特に動的言語になれていると の操作は甘く見がち⁃ ライブラリを選定する際にもこの観点は持っておくべき
■ 正直 であれば をつかってバリデーションするぐらい楽勝だろうという過信もあった
は重い!
教訓 非対称暗号は遅い
■ において肝となる認証・認可⁃ を活用している⁃ 内部通信用のトークンには対象鍵を
■ 非対称鍵の署名が重い事が発覚⁃ と比べ の が貧弱⁃ をみているとちらほら⁃ 最近の では改善されているかも 追記 あんまりだった
■ を使った のバインディングも存在する⁃ こちらの利用も検討したが のサンドボックス環境の都合で利用はできなかった
■ 対処⁃ 非対称鍵の署名処理を行う を外出しして回避
対象鍵によるトークンの署名時間 0.37 msec
非対称鍵によるトークンの署名時間 486.98 msec
参考 ベンチマーク
● RSA256● 100 回 JWT Claim を署名
結論
■ 本発表では初めての大規模 開発によって得た教訓について話した⁃ 教訓 フレームワークに拘らない⁃ 教訓 を尊重する⁃ 教訓 は重い⁃ 教訓 非対称暗号は遅い
■ 困ったときには の哲学に帰りシンプルなプローチを取る■ を過信せずパフォーマンスに気を配れ