LINEのメッセージングサーバが新年のトラフィックに対応する過程

はじめに

LINEの通信トラフィックは、メッセンジャーアプリ特有のパターンを持っています。新年の0時を迎えた瞬間に、ユーザ同士がLINEで新年のあいさつを交わしていることが想定され、それにより平常時に比べてメッセージの送信件数が大幅に増加します。その際、サービスを提供する国ごとに、時差や文化の違いによってさまざまなトラフィックの増加パターンを見せます。LINEでは、このような一時的なトラフィック増加を問題なく処理するため、毎年さまざまな対策を行っています。これを「新年対応」と呼んでいます。本記事では、2020年の新年対応における私たちの取り組みと、成果についてご紹介します。

LINEのメッセージングサーバが新年のトラフィックに備えるプロセス

各国で新年の0時になると、多くのユーザがLINEで新年のあいさつメッセージを送っていると想定されます。そのため、平常時より一時的にトラフィックが大幅に増加します。テキストメッセージを多く送信する国もあれば、画像や動画のメッセージを多く送信する国もあります。LINEでは、このような特徴的なトラフィックの増大をうまく処理するため、長い時では新年の6か月も前から新年対応の準備に取り掛かります。LINEは多種多様なコンポーネント群で構成されているので、新年対応の準備の過程では、各コンポーネントを担当しているチーム同士が緊密に協力し合うことになります。

各国において0時になるとトラフィックが一時的に増大する様子
今年の新年に観測された時間別 画像/動画の送信トレンド
比較的メディアメッセージを多く使う国の場合、テキスト中心の全体的なトラフィックと比べてピーク時間帯が異なる

LINEではサービス開始以来、新年に観測されたトラフィックのパターンと特異事項を毎年、ポストモーテムのためにに記録しています。記録されたデータをもとに、次に迎える新年のトラフィックを予測し、改善が必要な部分を確認します。なお、新年のトラフィック予測のためには、現在利用しているサーバのキャパシティを正確に把握することが重要です。アプリケーションサーバとデータベースの正確なキャパシティの把握と、改善のプロセスについて順番にご紹介します。

新年対応のプロセス

正確に計測する

LINEのメッセージングサーバは、毎年新機能が追加され、さまざまな改善を繰り返しながら、デイリーリリース(daily release)を進めています。コードは常に変化し続けているのです。そのため、毎年正確にトラフィックを計測し、翌年のトラフィックも問題なく処理できるように備えておく必要があります。現在のサーバが新年のトラフィックを問題なく処理できるか検証するには、トラフィック予測を正確にシミュレーションすることが重要です。しかし、現実のユーザの読み書きのトラフィックパターンを、正確にシミュレーションすることはとても困難な作業です。シミュレーションの工程では、たった1つの要素でも実際のトラフィックと異なっただけで、計測精度が損なわれることがあります。シミュレーション環境では発生しなかった問題が、実環境では発生するということも少なくありません。そのため、私たちは実際のトラフィックを使ったベンチマークテスト(Benchmark test、以下BMT)を実施しています。BMTは、サーバの状態を一目で正確に把握できる複数のダッシュボードを準備することから始まります。

さまざまな指標が一目でわかる複数のダッシュボード

ダッシュボードが準備できたら、いよいよ本格的なテストの工程に入ります。テストでは、ダッシュボードを注意深くモニタリングしながら、特定のホストへのトラフィックを徐々に増やしていきます。ダッシュボード上で異常が検出されたら、すぐ該当のホストにルーティングするトラフィック量を減らしていきます。たとえ一時的にキャパシティを超えるトラフィックが特定のホストに流入して処理に失敗しても、さまざまな方法でリトライすることでメッセージングサーバ内での処理が失敗しないように設計されているため、サービスに影響を与えない範囲で高精度な測定が可能です。

従来のトラフィックパターンをもとに新年対応の計画を立てる

アプリケーションサーバは、複数のサーバにトラフィックが分配される構造になっているため、前述のように特定のサーバに流れ込むトラフィックを増やすことでBMTを行うことができました。しかしデータベースの場合は、複数のサーバが同一のリソースを共有する構造になっているため、前述の方法ではBMTを行うことが困難です。そのため、過去に収集されたデータをもとに新年対応の計画を立てることになります。

LINEでは、新年対応に参加するすべてのチームやコンポーネントの活動内容が年別に1つの文書に記録されているため、他のチームやコンポーネントのデータを手軽に参照できます。なお、トラフィックパターンだけでなく、特異事項や課題点についても合わせて収集し、わかりやすくまとめて記録しています。

LINEのメッセージングサーバは、RedisとHBaseをデータベースとして使用しています。Redisの新年対応の計画を例に挙げると、Redisではまず、過去のデータをもとにトラフィックパターンに応じてクラスタを分類します。なぜなら新年のトラフィックが流入したとき、負荷が大幅に増加するクラスタと、そうでないクラスタがあるためです。
トラフィックパターンを確認するときは、IOPS(Input/Output Operations Per Second)とUsedMemoryの数値を参考にします。

IOPS

IOPSとは、Redisに流入された1秒当たりのリクエスト回数を意味します。ユーザからのリクエストが急増する新年などでは、IOPSの数値も共に増加します。GETリクエストが多いサービスの場合、現在のRedisクラスタのキャパシティが、GETリクエストを処理するのに十分なのか確認します。

十分でなければ、負荷をより細かく分散させるためにクラスタの拡張を行うこともできますし、アプリケーション側でRedisリクエストを最適化し負荷そのものを低減することもできます。どちらの方法で対処するかは、関係者間で十分に話し合い、メリット・デメリットと効率性を検討したうえで決定します。

今年の年明け、Redisクラスタに流入された1秒当たりの I/Oリクエストの推移

UsedMemory

UsedMemoryとは、Redisに保存されているデータサイズを意味します。クラスタデータの特性上、新年にPUTリクエストが増加する場合は、UsedMemory値を注意深く確認します。新年にRedisクラスタで利用可能な最大メモリ量を超えるリクエストが発生した場合、データの特性によってはサービスに深刻な影響を与えることがあります。そのため、過去のデータをもとに必要なメモリサイズを算出し、必要な場合はRedisクラスタを事前に拡張します。

今年の年明け、Redisクラスタのメモリ使用率の推移

改善方法

アプリケーションサーバ

前述のように、LINEのメッセージングサーバはデイリーリリースされており、日々改善と更新を続けています。新機能を追加したり、さまざまな改善を行っていく中で、想定外の副作用が度々発生します。通常レベルのトラフィックの場合、このような副作用が発生してもあまり問題になりません。しかし、瞬間的に通常の何倍ものトラフィックが発生した場合、大きな問題に発展する恐れがあるため、毎年入念な計測とモニタリングを続けているのです。

2020年の新年対応を準備するにあたり、重点的に改善を行なったのはプッシュ通知の送信です。LINEでは、メッセージングサーバに対して、LINEがリリースしている高性能非同期サーバ/クライアントライブラリー「Armeria」を段階的に導入していました。 2018年には、プッシュ送信コンポーネントとHTTP通信をArmeriaに切り替えています。当時、前述のBMTを実施し問題がないことを確認したうえで2019年の新年を迎えたにもかかわらず、想定外の問題が発生しました。BMTで検証したトラフィックレベルと大きく乖離していないにもかかわらず、0時からプッシュコンポーネントのレスポンスが急激に遅延し、メッセージングサーバ内のプッシュ通知用のキューにタスクが滞留し、後続のプッシュを処理できないという事態に陥りました。そこで、キュー内のプッシュの一部を破棄することでいち早く復旧させ、すぐに原因分析を始めました。

関連する指標やログを解析したところ、0時からのプッシュコンポーネントに大量のコネクションが一気に生成され、コンポーネントのJVMヒープメモリの使用率が急増していることがわかりました。またこの事が、Full GC(Garbage Collection)を誘発し、パフォーマンスを急激に低下させる原因になっていることが確認できました。

できるだけ現実と同じトラフィックをシミュレーションしてBMTを行おうとしましたが、現実のトラフィックは異なる動きを見せました。従来のBMTでは、TPS(transactions per second)の目標値まで注意深くモニタリングしながら徐々にトラフィックを上げる手法を用いました。そのため、瞬間的に通常の数倍のトラフィックが流入するといった状況まで想定したテストは行っていませんでした。

プッシュ送信のために使用しているArmeriaクライアントは、非同期のHTTPクライアントであるため、限られたコネクション数でも高いTPSを出せるように設定することができます。しかしリクエスト数を制限する設定がないため、一度に大量のリクエストが入ってきた場合、必要以上のコネクションを生成してしまうという問題がありました。この調査結果をもとに、障害の振り返り会議を実施し、以下の改善を進めることにしました。

  • 性能の改善について
    • 限られた数のコネクションのみ生成してそれを再利用できるように改善し、TPSの目標値を達成できるか確認する
  • 安全装置の備えについて
    • 役割ごとに通知キューを分離することで、特定のコンポーネントに発生した障害の影響範囲を最小限に抑える
    • Circuit breakerを導入し、特定のコンポーネントがリクエストを受けつけない場合、自動的にリクエストを遮断して障害を隔離し、コンポーネントを迅速に復旧できるようにする

上記の改善を行なった後、BMTを補強してテストを実施しました。まず、これまでと同様にTPSを目標値まで徐々に上昇させるテストを行い、問題がないことを確認しました。次に、TPSをいったん通常のトラフィックレベルまで引き下げてから、目標値まで一気に上昇させるテストを行いました。

TPSを徐々に引き上げながら確認した後、通常のTPSレベルで一気に目標値まで引き上げて再度確認

TPSを目標値まで一気に引き上げた場合でも、限られたコネクション数で安定的にプッシュ通知が送信されることを確認した後、改善作業を終了しました。

LINEでは予想せぬ障害が発生した場合、その責任を追及するのではなく、透明性を持って問題を共有し、再発防止に努めます。このような文化のおかげで、すべての開発者がイノベーションを恐れず、より高品質な堅牢性の高いシステムを構築することに集中できるのです。このようなLINEの企業文化は、LINEの障害報告とフォローアッププロセスの文化(韓国語版のみ)の記事で詳しく紹介しています。

また、LINEのメッセージングサーバで使用していたApache HTTPクライアントのバージョンをアップグレードしたところ、逆にパフォーマンスが低下するという問題が発生しました。HTTPCLIENT-1809で報告されたように、Apache HTTPクライアントでは、サーバのキャパシティを使い切っていなくても、一定以上の処理量を超えるとそれ以上処理量を増やせなくなり、パフォーマンスが著しく低下するという問題がありました。この問題は、当時LINEのメッセージングサーバにArmeriaを順次導入していく段階だったため、Apache HTTPクライアントをArmeriaに置き換えることで解決しました。

Redisクラスタ

前述のとおり、Redisクラスタは現在のキャパシティと、新年対応のために必要なキャパシティを比較して準備します。LINEのメッセージングサーバは「client-side sharding」(参照)という構成でRedisを使用しており、各ノードに均等に負荷が分散されるように構成されています。現在のクラスタのキャパシティが、予測される必要なキャパシティを満たさない場合、大きく別けて下記の3つの方法で対処しています。

  • Redisクラスタが複数の役割を持っている場合は、役割ごとにクラスタを分離して別々のクラスタを構成する
  • クラスタを効率的に使用するために、アプリケーションレベルでコードを改善する方法があるか確認する
  • Redisクラスタにノードを追加し、各ノードが担当するトラフィックを減らす

上記の方法に沿って、今年の新年対応で実施したクラスタの改善例を3つ、簡単に紹介します。

1つ目は、異なる種類のデータを扱うRedisクラスタの分離に関する事例です。LINEのトラフィックは増加し続けているため、今年の年明けに瞬間的に殺到するトラフィックを安定的に処理するために、一部のクラスタで何らかの対策を行う必要がありました。そこで、別のクラスタを作成し、特定のクラスタから分離できるデータを移動させました。以下のグラフで、クラスタの分離作業前後のIOPSを見比べることができます。

分離前のIOPS
分離後の元のクラスタのIOPS
分離後の新しいクラスタのIOPS

クラスタの分離を通して、過剰なトラフィックが流入していたクラスタで処理するリクエスト数を1/4に減らし、残り3/4のリクエストは新たに生成したクラスタで処理することにしました。クラスタの分離は、一度始めたら中断することなく最後まで行う必要があるため、4〜5日にわたって注意深くモニタリングしながら作業を進めました。

2つ目は、悪意のあるユーザからの異常なリクエストを判定する機能をサービスに導入することで、クラスタのIOPSを安定化させた事例です。特徴的な行動パターンに基づいて、悪意のあるユーザのリクエストを自動的に遮断するシステムがすでに導入されており、ほとんどの悪質なリクエストは遮断されていました。しかし、悪意のあるユーザは絶えず行動パターンを変化させるため、一時的に大きなRedisのIOPSを誘発するクラスタが存在しました。

2019年の年明け、0時のトラフィックパターンがはっきり見えないほど、
ところどころIOPSが跳ね上がる様子

今年は、悪意あるユーザをより早く、的確に遮断する新機能を導入することで、Redisクラスタ上のトラフィックを安定化させることに成功しました。

2020年の年明け、リクエスト量につれIOPSが増えるパターンを見せクラスタが安定化した様子

3つ目は、特定のRedisクラスタのEviction Policyを変更した事例です。UsedMemoryが継続的に増加し続けるクラスタがありましたが、アプリケーションレベルではTTL(Time To Live)を指定しても影響のないデータだったため、データにTTLを設定し、Eviction Policyを「volatile-ttl」に変更しました。そして、過去のトラフィックパターンに基づいて予測した新年のトラフィックに従って、そのTTL期間中のデータが問題なく維持できるレベルまでクラスタを拡張しました。

新年対応の振り返り

新年対応最後の工程は振り返りです。メッセージングサーバだけでなく、LINEのサーバの全コンポーネントに対するトラフィックパターンと特異事項が文書に記録されます。新年対応が終了したら、関係者全員が集まってこの文書に基づいて振り返りを行います。LINEのサービス開始以来、今まで毎年欠かさずに記録してきた貴重な新年対応の記録文書は、振り返りに向けて各コンポーネントの担当チームが準備します。この文書は、翌年の新年対応の重要な根拠となるので、細大漏らさず正確に記録します。トラフィックパターンを正確な数値やグラフを添えて記録し、その時々のモニタリングダッシュボードのデータをスナップショット(snapshot)形式で保管し、今後の新年対応で漏れなく確認できるようにしています。

文書を作成したら、各コンポーネントの担当者が集まり振り返りを行います。コンポーネントによっては、開発チームが他国にいる場合もあります。その場合、オフィスの開発者全員が参加できるようにビデオ会議システムを活用します。振り返りに当たって最も重要なことは、今年の新年対応の過程で判明した各コンポーネントの課題点を洗い出すことです。新年対応で観測されたデータに少しでも異常があれば、透明性を持って課題点と改善案を共有します。

おわりに

今年の新年対応に向けた皆様のご尽力のおかげで、各国の新年のトラフィックを問題なく処理できました。問題はなかったのですが、振り返り資料を準備する中で、少し奇妙な現象に気がつきました。メッセージングサーバ上の他のコンポーネントを呼び出して通信を行う際、レイテンシ(latency)が想定より増加する現象が見られたのです。該当のコンポーネントのログを見ると、実際のリクエストを処理する以上に、新しいコネクションを生成することに多くの時間がかかっていることがわかりました。瞬間的に通常の数倍のトラフィックを処理する場合、通常では見られない現象が発生することがあります。この問題が発見された後、コンポーネントとの通信にHTTP/2を使用してコネクションを効率的に利用する改善方針が立てられ、現在進行中です。参考までに、LINEで広く使用されているArmeriaは、デフォルトでHTTP/2をサポートしているため、簡単にHTTP/2に切り替えることができます。

LINEでは、2021年の新年対応に向けても、この記事と同じプロセスで対応準備を実施する予定です。LINEではどんな小さな課題点であっても透明性を持って共有し、改善や振り返りを通じて、より良いシステムを構築するために日々たゆまぬ努力を続けています。このような改善プロセスを経て、さらに堅牢性を増していくLINEのサーバコンポーネントの開発に、ご興味のある方はぜひ以下からご応募ください。

採用情報

この記事は、多くのLINERの努力と協力が支えとなって作成されたものです。新年対応にご参加いただいた皆様のご尽力に感謝します!

日本語の新年対応に関する外部記事もありますので併せてご覧ください。
「あけおめLINE」の負荷に耐えるインフラを作った話。LINEのインフラ設計を中の人に聞いた – エンジニアHub|若手Webエンジニアのキャリアを考える!