Blog
【スライド公開】ITモダナイゼーションサミット「JavaからScalaへの継続的なマイグレーション」
AMoAdの福原です。
先日 ITモダナイゼーションSummitというイベントで、AMoAdでの取り組みを話してきました。今回はこのセッション資料を紹介します。
[slideshare id=47327197&doc=javatoscalait-modern2015-150423061017-conversion-gate02]
口頭での説明が多かったので、この場で補足いたします。スライドと合わせてご覧ください。
[P10] 1. 高トラフィック/低レンテンシ
一般的なWebサービスと違うところは、リクエストが詰まること無く配信をし続ける必要があるところです。 一日億単位の膨大なトラフィックを受けるため、例えば処理時間の長いセッションがあった場合でも、全体のパフォーマンスは落とさずに配信を続ける必要があります。 そのため、負荷計測も一般的なAverageタイムよりも、95 percentile、median(中央値)の値がどれくらいであるのかというのを指標として います。
[P11] 2. リアルタイムユーザ判定
「一度見たページの広告がよく出る」ということがあると思います。 これはシステムがユーザの行動履歴を分析し、次にユーザが広告面に接触した場合、最適な広告を表示するよう作られているためです。 日本国内のスマートフォンユーザだけでも6000万件を超えており、ユニークユーザ数は億単位になります。 これをリアルタイムで判定しユーザに適切な広告を表示する必要があります。
[P12] 3. 最適化ロジック
行動履歴やログデータを元に、機械学習などを用いた最適化ロジックがあります。 ここが一番アカデミックな分野で、日夜データサイエンティストが最適化ロジックを研究しています。 俗にいうビックデータ解析ですね。 最適化とは単純にいうと、ユーザや広告枠に対して、どの広告を出せばより目的が達成できるのかということです。
近年ではこの最適化ロジックについてリアルタイム性が求められています。膨大な量のデータを如何に効率よく、さらにリアルタイムで広告配信に反映していくということが重要です。
[P13] 4.レポート集計
配信実績ログを集計し広告主、メディアに対するレポートを作る必要があります。 広告主に対しては、広告出稿金額に対して目的が果たせたのか メディアに対しては、収益がどの程度であったのかなどが管理画面から参照できます。
もちろんこれも数億というログデータを扱うため、HadoopやSparkなどを使い決められた時間以内に集計を完了する必要があります。
[P16] 2013/4- 内製化開始
仕様書が無いとなると「動いてるソースが正」となりがちです。
「ソースから仕様書作成だ!」みたいなことになると思います。 そうなってしまうとかなり時間がとられてしまいます。僕らのビジネスにとってそこに時間を費やしてしまうことは致命的です。システムとして新しい価値が提供できないからです。
しかし、僕らはそうはなりませんでした。
[P17] Key Point
僕らは仕様については丸投げせずに自分たちで把握していました。 正しさが自分たちの中にあったおかげで、動いているソースでも自分たちを信じて修正していきました。 内製化ではこれが大きなポイントとなりました。
[P19] なぜScalaか?(不変)
僕自身、数多のJavaプロジェクトのメンテナンスをしてきたのですが、 バグの原因のほとんどが、オブジェクトの状態変更が把握しきれないことによるものでした。 Scalaは普通に書くと変数の参照変更やオブジェクトの変更ができません。これによりメンテナンス性が向上することが容易に想像できした。
[P20] なぜScalaか?(参照透明性)
こちらは参照透明性の例ですが、関数twiceに対して123を渡せば、必ず246が返ってくる。 関数の答えが引数にのみ依存している状態です。
これだけみると「なんだあたりの事じゃないか」と思われると思いますが、 例えば、この関数が内部で現在時刻を参照するとしたら、ランダム値を扱うとしたら、Exceptionを投げるかもしれないとしたら。 それは副作用となり関数の答えが変わってきてしまいます。
参照透明性はコードをメンテナンスをする上で重要なことです。
ご存知の方もいると思いますが、この不変と参照透明性は SparkのRDD, DAGスケジューラの考えの根底にもなっています。 ですが、今までのオブジェクト指向の考え方と合わない部分も出てきます。
[P28] 前提
これは担当プロジェクトの言語比率です。Scalaは28%ですね Scalaのメリットについて語ってきましたが、案外少なく思われるかもしれません。実際はScalaの方がシンプルに記述できるので、JavaとScalaの比率は半々くらいだと思います。 何故か?なのですが、リプレースの前提としてこのようなことがあります。 僕たちはビジネスに貢献する場合にのみシステムのリプレースをしています。僕もエンジニアなのでレガシーシステムを全てリプレースして理想の形にしたいと思うことはあります。 ですが、絶えず発生する機能追加、負荷対策の中で、リプレースだけに多くの時間を割くことはできません。 また、この変化し続ける環境の中で理想の形は時間とともに変わっていきます。
[P31] 既存機能への機能追加
機能追加の全体設計は要件により色々な方法をとりますが、ポイントとして必ずリリースをロールバック出来るようにするということというのがあります。 僕達のシステムは24時間365日稼働しており停止することが許されません。リリース後不慮の自体が起きてもモジュールをロールバックし配信を継続するということが大事になってきます。
実装についてですが、レガシーコードにそのまま機能追加をするということはしません。 テストコードを書き動作を詳細に把握するというところからはじめ、リファクタリングを行いモジュールの依存関係を整理します。このフェーズが非常に大事でこれを行うことによりコードのメンテナンス 性を高め、意図しないバグを抑制することが出来ます。
ここまでくれば追加機能の実装が容易になってきます。
[P32] 負荷対策(チューニング)
簡単に書きましたがチューニングと言うのは奥が深く対策も一筋縄にいかないことが多いです。 それでもポイントとして挙げさせたのが「チューニングをやり過ぎない」と「スケールアップで対処する」ということです。 チューニングをやり過ぎないことについてですが、テストをどこまでやるかという問題と同じですが、チューニングを突き詰めすぎるとコストの割に成果が上がりづらくなります。また僕達の扱っているシ ステムは負荷の種類も変わり続けるので、ひとつの事象にとらわれ過ぎないということが大事になってきます。 次にスケールアップで対処するということですが、負荷対策は複雑ですぐに対処できるか判断できないことが多くあります。リソースが許す限りでまずはスケールアップで対処に、その間に本対策を進めると いう対処も場合によってはあります。
Enjoy Scala
Author