SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

大規模レガシーサービスにおけるパフォーマンス改善の道のり

大規模Webサービスの改善に向けたR&Dの取り組み――SPAのボイラープレート開発とFastlyの活用

大規模レガシーサービスにおけるパフォーマンス改善の道のり 第4回

Fastly VCL

 Fastlyは「Varnish」を利用して動作しているため、Varnish Configuration Language(以下、VCL)で設定をプログラマブルに記述できる特徴を持っています。VCLを記述することで、リクエストとレスポンスを加工することができるのです。これにより、エッジサーバーにおいて細かいカスタマイズが可能となります。これをリクルートでは以下のように利用しています。

  • ユーザーエージェントからデバイスタイプを選択して出し分ける。
  • A/Bテストパラメータを設定する。
  • HTMLのキャッシュを行う。

 例えば、PCとスマートフォンユーザーで描画するページを出し分けることがあります。FastlyのVCLを用いると、ユーザーエージェントからデバイスがPCかスマートフォンかを判別し、その結果をリクエストのヘッダに加えることが可能になります。そこで、Fastlyでリクエストを受信したときにリスト1のようなVCLを実行すれば、BFFへのリクエストヘッダ「X-UA-Device」にデバイスタイプの結果を載せることができます。

リスト1 ユーザーエージェントからデバイスタイプを選択するVCLの例
sub detect_device {
  if (req.http.User-Agent ~ "(?i)ip(hone|od)" || ...) {
    set req.http.X-UA-Device = "sp"
  } else {
    set req.http.X-UA-Device = "pc"
  }
}

 次に、描画するコンポーネントのA/Bテストを行う場合について説明します。CDNを置かない場合では、BFFはアクセスしてきたユーザーごとにランダムにA/Bテストパラメータを設定し、テストパラメータに応じて描画するコンポーネントを出し分けます。この時点のレスポンスをCookieに書き出しておくことで、次回以降も割り振られたA/Bテストパラメータで同じコンポーネントを描画し続けることができます。

図4 概要図:一般的にBFFでA/Bテストパラメータを設定する場合の構成
図4 概要図:一般的にBFFでA/Bテストパラメータを設定する場合の構成

 単純にCDNを置いただけでは、最初にアクセスしたユーザーに割り振られたA/BパターンでHTMLのキャッシュが生成されてしまい、それ以降にほかのユーザーのアクセスがあっても初期にキャッシュしたパターンで描画され続けてしまいます。そのためBFFではなく、エッジサーバーでA/Bテストパラメータを算出する必要があります。

図5 概要図:FastlyでA/Bテストパラメータを設定する場合の構成
図5 概要図:FastlyでA/Bテストパラメータを設定する場合の構成

 リスト2はエッジサーバーで、リクエストが届いたときにA/Bテストパラメータを設定する例です。Fastlyがランダムにブール(bool)値を返してくれるrandombool関数を持っているため、容易に実装が可能です。

リスト2 A/Bテストパラメータを設定するVCLの例
sub set_abparam_into_header {
  #in recv
  if (!req.http.Cookie:TestParam) {
    if (randombool(50,100)) {
      set req.http.x-test-param = "a-pattern";
    } else {
      set req.http.x-test-param = "b-pattern";
    }
  } else {
    set req.http.x-test-param = req.http.Cookie:TestParam; 
  }
}

 Fastlyでは基本的にURL(リクエストのホストとURLパス)をキャッシュキーに利用しており、同一のキャッシュキーに対して該当するキャッシュを返却します。しかしPCとスマートフォンやA/Bテストのように、同じURLで異なるWebページが表示されうるケースがあります。このようなケースのためにFastlyでは、URLに加えてVaryヘッダをキャッシュキーに利用し、Varyヘッダに特定の値を追加することでキャッシュの出し分けを行うことができます。リスト3は、エッジサーバーがキャッシュを取得する際に、VaryヘッダにデバイスタイプとA/Bテストパラメータを載せる例です。

リスト3 Varyヘッダを利用するVCLの例
sub set_vary_header {
  #in fetch
  if (beresp.http.Vary) {
    set beresp.http.Vary = beresp.http.Vary ", x-test-param, X-UA-Device"; 
  } else {
    set beresp.http.Vary = "x-test-param, X-UA-Device";
  }
}

 また再訪された際に前回と同じコンテンツを表示する必要があるので、ユーザーへのレスポンスでA/BテストパラメータをCookieに書き込んでもらう必要があります。リスト4は、ユーザーにレスポンスを返すときにA/BテストパラメータをCookieに書き込む例です。

リスト4 A/BテストパラメータをCookieに書き込むVCLの例
sub set_abparam_into_cookie {
  #in deliver
  if (!req.http.Cookie:TestParam){
    add resp.http.Set-Cookie = "TestParam=" req.http.x-test-param + "; Expires=" + time.add(now, 24w) + "; Path=/;";
  }
}

 以上の内容をエッジサーバーで行うことで、Fastlyが導入された構成でA/Bテストを行うことができるようになります。

FastlyとCI連携

 こうしたVCLを記述することでエッジサーバーに機能を持たせることができる一方、開発時にVCLの修正のたびにFastlyへデプロイし、エンドポイントを更新するのは容易ではありません。そこでリクルートでは、CIと連携してFastlyのエンドポイントを自動的に立ち上げる仕組みを検討しています。Fastlyは「Terraform」向けのプロバイダが準備されているので、TerraformでCIと連携してFastlyのエンドポイントを立ち上げることができます。これを利用して、GitHub上の特定のブランチに修正したVCLをpush(プッシュ)したことをトリガーにしてFastlyのエンドポイントを新規に立ち上げ、Fastly導入時の動作確認もより容易に行うことが可能になります。

図6 概要図:FastlyのCI連携
図6 概要図:FastlyのCI連携

キャッシュ戦略

 フロントエンドのパフォーマンス改善の文脈でキャッシュの利用は欠かせないものです。その中では、CDNの選択も今後必須の技術となっていくことが予想できます。それと同時に、今後のフロントエンドのWebサービス開発では設計段階からCDNを活用したキャッシュ戦略も検討する必要があるでしょう。

 コンテンツの情報更新が頻繁にあり、表示結果が毎回変わるようなコンテンツの場合はキャッシュを保持できません。新しく更新された情報で正しくWebページを表示するためには、コンテンツ情報の更新があるたびにキャッシュをパージしないといけないからです。その点Fastlyではミリ秒単位の短い時間でパージが可能なため、パージされるまでの時間を気にする必要はほとんどありません。しかしパージするまでの間隔が短ければ短いほど、CDNキャッシュの恩恵は低くなります。そのため、ビジネス的に許容できる範囲でパージするまでの間隔をできる限り長くできれば、キャッシュを効率的に活用することができます。

 そのほかには、HTMLページの中でキャッシュする箇所とキャッシュしない箇所を決め、キャッシュしない箇所に関しては後からJavaScriptで更新するといった戦略もあります。例えば、コンテンツ情報の更新に関係のある部分がオススメのコンテンツを表示するコンポーネントだけであった場合、この部分だけをCSRにしてそれ以外の部分をSSRで配信するようにします。こうすることにより、CDNにキャッシュされるHTMLはコンテンツ情報に関わらない部分しかなくなるので、コンテンツ情報の更新があってもキャッシュをパージする必要がありません。ただし、CSRにされる部分は初期表示されていないことが許容できる場合に限ります。

図7 概要図:CDNドリブンなコンポーネント設計の例
図7 概要図:CDNドリブンなコンポーネント設計の例

 図7をご覧いただくと、CDNのキャッシュを残すことを前提としてシステム設計や描画するコンポーネントの設計を検討することの必要性を理解していただけると思います。CDNやクライアントサイドのキャッシュなど、キャッシュを多層化させると問題は複雑化していきます。そのため、アプリケーションの要件に応じて適切なキャッシュ戦略を決めていく必要があります。

まとめ

 本稿では、今後取り組むリクルートのWeb技術として、リクルートテクノロジーズがR&Dとして取り組んでいるredux-plutoとFastlyの導入について紹介しました。redux-plutoはBFFでReact/Reduxを使ったSSRが可能なボイラープレートで、社内のサービスにおいて開発と運用を続けています。FastlyはA/Bテストを行うようなケースで技術検証しており、次期サービスへの導入を予定しています。

 今後もフロントエンドの最新技術をキャッチアップしながら、さらなるWebサービスのパフォーマンス改善にチャレンジしていきます。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
大規模レガシーサービスにおけるパフォーマンス改善の道のり連載記事一覧

もっと読む

この記事の著者

可児 潤也(株式会社リクルートテクノロジーズ)(カニ ジュンヤ)

 リクルートテクノロジーズ ITエンジニアリング本部 プロダクトエンジニアリング部 APソリューショングループ ソフトウェアエンジニア 2018年に中途でリクルートテクノロジーズに入社。リクルートジョブズやリクルートキャリアが提供するWebサービスの開発に従事。モダンなWeb技術を利用したフロントエ...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/11579 2019/07/10 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー