ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

Yahoo!ショッピングアプリの負荷に対する取り組み

本記事は2022年11月に開催した「Tech-Verse 2022」で発表したセッションを要約したものです。アーカイブ動画を文末に掲載しています。質疑応答の様子も収録されていますのでぜひご覧ください。

大規模なeコマースのセールにおいて常につきまとう問題がシステム負荷です。今回はYahoo!ショッピングのWebサイトやアプリで取り組んだ負荷対策を紹介します。

2022年10月から年末まで開催中の「超PayPay祭」は全国にあるオフラインのPayPay加盟店様や、オンラインのeコマースショップで開催されるキャンペーンです。Yahoo!ショッピングも同キャンペーンに参加し、期間中はお得にショッピングを楽しめます。一方で、この時期はシステムへのトラフィックが大幅に増加する時期でもあり、Yahoo!ショッピングでは負荷対策の専門チームを作り対応しています。

超PayPay祭のような大規模なセールがアプリに及ぼす影響を下図で示しています。縦軸は通信回数、横軸は時刻(一日のタイムライン)を表しています。通常時をグレーで、超PayPay祭開催時を青色で示しました。セール時は一日を通してリクエストが増加し、特にセール開始とともに負荷が上昇しています。その差は最大で通常時の10倍以上も生じるということがこの図から分かります。

大規模セール開催時と平常時の通信回数を比較した折れ線グラフ

では、このような負荷がアプリにどのような影響を及ぼすのか、事例を紹介します。例えば過去には、高負荷によって「アプリのネイティブ機能の停止」や「トップページの一部モジュールの停止」、「アプリの主要機能の停止」といった事態を招き、ユーザーの方々にご迷惑をおかけすることもありました。そこで、Yahoo!ショッピングでは、こうした急激な高負荷にも不具合を起こさないための対策に取り組んでいます。

2種類のアプリケーションで異なる対処法

実際の負荷対策についてを紹介する前に、まずアプリケーションについて解説します。アプリケーションはサーバサイドとクライアントサイドの2種類に分けられ、それぞれ実行環境は異なります。例えばサーバサイドのアプリはリモートで実行され、クライアントサイドのアプリはユーザー自身の端末で実行されます。もし負荷による何らかの問題が発生した場合、これらの対処法はそれぞれ異なります。

まず、サーバサイドについてです。一般的なWebアプリケーションのシステム構成を示すと下図のようになると思います。まずフロントエンドサーバにアクセスがあると、データ取得のためにAPIサーバへリクエストが発生し、APIサーバは大元のデータベースにリクエストします。

Webアプリケーションのシステム構成

仮にフロントエンドサーバに大量のアクセスが集中した場合を想定してみましょう。まず、サーバサイドのアプリケーションに高負荷がかかることが予想されます。これによりなんらかの問題や障害が発生すれば、負荷対応が必要です。緊急の修正やリリースをする場合もあるでしょう。具体的にはプログラムの改修やデータのリカバリーなどさまざまですが、こうしたサーバサイドの問題は、全作業をサーバサイドだけで完結させることが可能です。

一方のクライアントサイドについても考えてみましょう。今回はスマートフォンのネイティブアプリケーションを想定します。アプリからはAPIサーバへのリクエストが発生し、APIサーバからはデータベースへのリクエストが発生します。

高負荷による不具合が発生した場合、サーバサイドアプリケーションはプログラムの改修を行える一方で、ネイティブアプリケーションはプログラムの改修を即時に行うことができません。プログラムを改修したあとにアプリストアへ申請し、審査を受ける必要があるためです。

仮に審査が通ったとしても、実際にユーザーが更新後のアプリを使用するためには、ユーザー自身の操作でアップデートが必要です。つまり、ユーザーがアップデートしない限り、不具合は残り続けてしまいます。

このようにクライアントサイドは、修正のタイミングをコントロールできず、緊急時の対応には注意が必要です。実際にYahoo!ショッピングのアプリでも、プログラムの改修申請後。審査に2週間以上を要したこともありました。アップデート時期を任意にコントロールできないことで、影響の範囲が拡大する恐れもあります。これはネイティブアプリケーション開発における一つのリスクとして認識しておくべきです。

ネイティブアプリケーションの緊急対応は難しいとお伝えしましたが、もちろん、Yahoo!ショッピングのアプリでは、不具合が発生した際にまったく対応できないわけではありません。対応策として「ユーザーの皆さまへ告知を掲載する」といった対応や、一部モジュールにおける不具合の場合は「該当モジュールを非表示にする」、「モジュールの順序を入れ替える」といった機能をもともと備えています。とはいえ、クライアントのアプリである以上、緊急時のリリースやアプリから特定のアクセスを制御することは困難です。

Yahoo!ショッピングアプリで実施した負荷対策

こうした課題を抱える中、Yahoo!ショッピングアプリが実施してきた負荷対策について紹介します。負荷対策を考える上で、そもそもYahoo!ショッピングのアプリから発生するアクセスは、ほぼAPI通信である点に注目しました。そこで、このAPI通信を外部から制御できる仕組みを導入しました。

具体的には、APIのオンオフ情報(APIコールするかしないか)を記載した外部ファイルを用意しました。この外部ファイルをYahoo!ショッピングのアプリから読み取って、各種APIの設定を確認します。アプリ内部には「APIの通信判定ロジック」を新たに導入し、通信リクエストをするかしないか判定します。

それによりアプリ単独でAPI通信のコントロールが可能となるため、アプリにアクセスが集中した場合、特定のAPIだけを遮断できるようになりました。 また、リクエストやエラーの頻度も設定できるようにしました。例えばAPIが一定数エラーになった場合に、一定期間リクエストを拒否する仕組みも同時に開発しました。こちらも同様にアプリ単独での実現が可能です。

APIの設定情報を持った外部ファイルはアプリエンジニアが任意に更新できます。もし高負荷が発生して特定のAPIを遮断したい状況になった場合には、対象のファイルを更新することでリアルタイムにAPIリクエストを制御できるようになっています。

外部ファイルの中身は、下図に示すように簡単なJSON配列です。APIのホスト名やパス、リクエストの頻度、スイッチのオンオフ情報などを記載しています。複数のホスト名、複数のAPIで設定が可能で、アプリから発生するほぼ全てのAPIを制御可能です。

APIの設定情報を持った外部ファイルのサンプル。JSON形式で設定情報を定義できる

こうした負荷対策によって、Yahoo!ショッピングアプリで実現したことを三つ紹介します。

1つ目は、リモートで全APIのオンオフ切り替えができるようになったことです。プログラムの修正リリースなしで行えることがポイントです。2つ目はサーキットブレーカーの導入です。先述の「一定数エラーになった場合に、一定期間リクエストを拒否する仕組み」によってサーキットブレーカーの役割を果たすと考えています。

最後に3つ目がサーバサイドエンジニアの負担軽減です。これまではその都度サーバサイドのエンジニアに負荷対応を依頼していましたが、アプリ側で負荷対応が完結できるようになり、エンジニアの負担軽減につながりました。

高負荷によって不具合が発生したときの対処法

続いて、予期せぬ不具合への備えについて説明します。いくら準備をしていても、想定外の負荷による不具合が起こるかもしれません。そこで、Yahoo!ショッピングでは簡易的なWebページ画面を表示する機能を備えています。もしアプリの機能全般が停止したとしても、ユーザーの混乱を防ぐために特定のWebページを表示させます。以前は簡素なエラー画面を表示させていましたが、それよりは混乱を低減できるようになるだろうと考えています。

次に商品ページについてです。正常時はネイティブアプリケーション側でページを描画します。ただページの描画にもいくつかのAPIをコールしたり内部で処理しており、その際にもしエラーが発生すると、自動的にWebビューを開いて対象の商品詳細のWebページを表示するようにしています。これは何らかの異常が発生すると、自動的に発動する機能として、商品ページのセーフティ機能という位置づけになっています。

また、普段のモジュール開発についても負荷の耐久性を確認し、対策を講じています。下図に示すように、キャンペーンやランキング、注文履歴、閲覧履歴、クーポンなどさまざまなモジュールがありますが、それらに対する負荷の強度に応じた対策を想定しています。

例えば、キャンペーンやランキングなど負荷に強いモジュールについては、高負荷時の対応はそこまで厚くせず、対応するにしてもキャッシュ版に切り替える程度の対応を想定しています。負荷に強くない注文履歴や閲覧履歴、クーポンのモジュールなどは、負荷発生時に「簡易版を表示する」、「負荷のかからない静的なモジュールに切り替える」といった対策を想定しておきます。

負荷に対するアプリエンジニアの心構えとは

最後に私が考えるアプリエンジニアの負荷に対する心構えについて紹介します。

まず、インフラエンジニアでなくてもアプリエンジニアも、負荷対策をして当たり前という意識を持つことです。ネイティブアプリはユーザーが真っ先に触れるシステムであるため、アプリエンジニアも負荷対策をあらかじめ考え、トラブルを想定して対応する手段がないかどうかを検討することが大切だと考えています。

次に、異常時を想定したモジュール設計です。さまざまなモジュールを開発する中で、高負荷になった場合の対応策を想定しておくことも大切です。実際に高負荷が発生した際に慌てないための準備は重要だと感じています。

そして、バックエンドシステムの理解です。アプリの負荷対策では、アプリの知識だけでなく、アプリが通信する先のシステムを理解することも必要です。アプリの対向であるAPI、APIが通信している先のデータベースなどを見据え、全体のシステム構成を理解した上で最適な負荷対策を行えればと考えています。

もちろん、アプリ側で全ての負荷対策を完結させることはできませんが、アプリはさまざまなバックエンドのシステムと連携しているので、「どういった分担でどの負荷対策をすれば良いか」といった観点で最適なソリューションを考えていくことが必要だと考えています。

ここまで高負荷への対策を紹介してきました。実際に紹介したアクセスコントロールの仕組みをすでにYahoo!ショッピングアプリで実装し、本番にリリースしています。超PayPay祭のような大規模セールも何度か経験し「特定のAPIリクエストを減らす」、「アプリから発生したリクエストを減らす」といった対応を求められた場面もありました。

その際に、このアクセスコントロールの仕組みを利用することで、秒間1万以上のリクエスト削減に成功した実績もあります。今回実装したアクセスコントロールが一つの成果だと考えています。 アプリ側では負荷対策は難しいと感じているエンジニアの方も多いと思いますが、ぜひ、ここまで紹介した内容を負荷対策のヒントにしてもらえればと思います。

アーカイブ動画

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました


和泉 明憲
ショッピング統括本部 部長
入社以来、Yahoo!ショッピングサービスを担当。Webフロントエンド開発、バックエンド開発、システム運用、アプリ開発を経験し、Yahoo!ショッピングを技術領域から支えています。

このページの先頭へ