LINE Event Gatewayとは

イ・ビョクサン氏:それではプレゼンテーションをはじめます。まずこのセッションに参加していただき、ありがとうございます。イ・ビョクサンです。LINEのAPI Gatewayチームでソフトウェアエンジニアをしています。今日はHTTP/2とTLSによる、LINEのクライアントとサーバーの接続性向上についてお話しします。

本日のアジェンダです。まず私たちが何を解決しようとしたのか、その背景を説明します。そして、接続性を向上させるために行った3つの主なアプローチを説明します。最後に、まとめをお話ししたいと思います。

まず背景についてです。詳細を説明する前に、この発表の範囲を明確にしておきたいと思います。この図は、LINE Messaging Platformの全体的な構図を表しています。LINEのクライアントは、LEGYと呼ばれるAPI Gatewayを介して、Talk Serverや連携サービスと直接または間接的に通信します。

LEGYは、LINE Event GatewaYの略です。ここからは、LEGYとAPI Gatewayを同じ意味で使っていきます。talk-serverと連携サーバーは、それぞれ独自のビジネスロジックを実行して、メッセージングやメディア送信などのLINE機能を提供します。LEGYの主な役割の1つは、LINEのクライアントとの接続を管理することです。このプレゼンテーションでは、LINEのクライアントとLEGY間の通信に焦点を当てます。

サービス開始当初はHTTP/1.1を採用

LINEサービスへのトラフィックの多くはLEGYを経由するため、LEGYは効率的かつ信頼性の高い方法で接続を処理できなければなりません。またセキュアであることも必要です。LINEのクライアントとAPI Gatewayの間のプロトコルが、どのように変更されたかを見てみましょう。

2011年にサービスを開始したLINEでは、HTTP/1.1を採用していました。ご存知のように、HTTP/1.1はリクエストとレスポンスのペアを1つのコネクションで処理するのが基本です。LINEの主な機能の1つに、HTTP/1.1を通じて新着メッセージの到着をクライアントに知らせるものがありました。そのため我々は、ロングポーリングという手法を用いました。

仕組みはこうなっています。LINEのイベントは、すべてオペレーションとして定義されています。クライアントはfetchOpsというAPIを使ってイベントを同期し、オペレーションを開始します。LEGYはfetchOpsのリクエストをロングポーリングとして処理します。LEGYは、クライアントからfetchOpsリクエストを受け取った直後に、Talk Serverに渡します。

しかし、talk-serverがコンテンツなしと応答すると、LEGYはtalk-serverからpublishを待ちます。新しいオペレーションが作成されると、talk-serverはLEGYにpublishリクエストを送信します。talk-serverからpublishを取得した後、LEGYは元のfetchOpsリクエストをtalk-serverに再送し、その応答をクライアントに返します。

2012年末からSPDYを採用

2012年末から、LEGYではHTTP/2の基礎となるSPDYを採用しています。HTTP/1.1とは異なり、SPDYは1つのコネクションを確立し、複数のリクエストとレスポンスを1つのコネクション内で多重化します。

このほかにも、HTTP/1.1より改良された点をSPDYは持っています。まず1つ目は、ヘッダーの圧縮です。大きなヘッダーを含んだHTTPリクエストを圧縮することで、サイズを小さくすることができます。これは、ネットワークの帯域幅を削減するには良いことですが、LEGYが圧縮状態を保存するためにメモリを大量に使用するという問題があります。そのため、接続によっては、この方法によるヘッダー圧縮をLEGYが正しく扱うことができていませんでした。

クライアントからの多くのリクエストは、同じHTTPヘッダーを繰り返し持つ可能性が高く、それが何であるかわかっているため、LEGYではそのような繰り返しのヘッダーをキャッシュして、ネットワークの帯域幅とメモリ使用量の両方を削減することにしました。

接続の中でLEGYは、リクエストを受け取るとキャッシュ可能なヘッダーをメモリに保持し、キャッシュキーをクライアントに返します。その後のリクエストでは、クライアントはキャッシュキーをHTTPヘッダーとして添付するだけで、LEGYはキャッシュされたヘッダーを復元してから、talk-serverにリクエストを送信します。

2つ目の改善は、Server Pushです。Server Pushは、1つのリクエストに対して複数のレスポンスを可能にします。その目的は、Webページをレンダリングするための追加リソースの取得を高速化することです。しかし、「Server Push」とは呼ばれていますが、それはリクエストをかならず1つ必要とします。一部のLINEサービスではロングポーリングにおける空の204 No Contentの応答を避けるために、SubscribeとPushのパターンが必要です。

つまり、リクエストを必要としない未承諾のPushを送れるようにすることが必要です。これを可能にするために、すべてのPushストリームをstream 0に紐づけています。stream 0は、SPDYでは使用されておらず、SPDYの仕様上では無効な値とされています。我々のPushでは、クライアントはまずLEGYを通じてサービスにSubscribe要求を送信します。

その後、サービスがLEGYにPushリクエストを送信すると、LEGYは関連するクライアントとの接続を見つけます。LEGYはデータをストリームで送信し、それをstream 0に関連付けます。ちなみに、fetchOpsのAPIはこのパターンではなく、ロングポーリングとして動作します。

LEGY Encryption暗号方式

セキュリティも通信において非常に重要です。SPDYでは、より堅牢なセキュリティのためにTLSの使用が推奨されています。しかし当時は、TLSを使うと3Gモバイルネットワークの接続確立が遅くなるため、非常に大きなオーバーヘッドとなっていました。モバイルネットワークは遅く、切断や再接続が頻繁に発生していました。

そこでLEGY Encryptionという暗号方式を自社開発しました。これは接続全体ではなく、メッセージ本文と一部の機密性の高いヘッダーのみを暗号化するものです。ハンドシェイクのプロセスは、TLS1.3の0-RTTハンドシェイクに似ています。暗号化キーの交換とHTTPリクエストとレスポンスの処理が、同時に行われます。

LEGY Encryptionを使用する場合、LEGY Encryptionに関するメタデータがヘッダーに追加されます。リクエストやレスポンスに含まれる機密性の高いヘッダーは、すべてメッセージ本文に移され、オリジナル本文とともに暗号化されます。Wi-Fiネットワークで接続するクライアントは通常TLSを使用し、モバイルネットワークで接続するクライアントはLEGY Encryptionを使用します。

3つの問題点

LINEのクライアントとLEGYの接続性を向上させるために、社内独自で使ってきた技術を紹介してきましたが、問題もあります。まずSPDY自体が非推奨となっており、我々のSPDYプロトコルに対応したライブラリがありません。なので、自分たちでSPDYのコードを書いて管理する必要があります。

2つ目の問題は、我々のプロトコルが標準に準拠していないことです。これは混乱を招き、ネットワークのデバッグを困難にします。

最後の問題は、TLSのほうが社内独自の暗号化方法よりも優れていて安全ですが、私たちはすべての接続でTLSを使用しているわけではません。

我々の使命は、UXを損なうことなく、高い信頼性とセキュアな接続性を確保することです。。これらのLEGYの3つの問題点を解決するLINTサブプロジェクトとして解決を試みました。LINT(LINE Improvement for Next Ten years)とは、急成長の際に見落としてきた技術長期課題をLINE Platformの次の10年を見据えて解決するプロジェクトです。

このサブプロジェクトの目標はは古いSPDYプロトコルをHTTP/2に置き換え、標準に準拠したPushメカニズムを採用して、クライアントのネットワーク環境にかかわらず、すべての接続にTLSを採用することです。では何を検討して、何が行われたかを説明していきたいと思います。

HTTP/2の採用

HTTP/2を使うのは非常にシンプルです。SPDYのプロトコルをHTTP/2に置き換えるだけです。ちなみに、標準的なHTTP/2を使用しており、ヘッダーキャッシュはHTTPで使用されている効率的なヘッダー圧縮法であるHPACKに置き換えられているため、使用していません。ここではSPDYとHTTP/2の比較ではなく、どのようにして安全にHTTP/2に切り替えたかに焦点を当てたいと思います。

移行を失敗させる2つの悪いケースがあります。1つ目のケースは、サーバーの実装にバグがあることです。クライアントと違って、サーバーはいつでも修正できますが、重要なのは修正にどれぐらい時間がかかるかということです。バグが単純なものではなく相応に時間がかかる場合は、修正されるまでHTTP/2を完全に無効にしておく必要があります。これが最善の対処法です。これには、クライアントがHTTP/2を使用しないように制御する機能が必要です。

2つ目のケースは、クライアント側のHTTP/2にバグが多い場合です。この場合、クライアントはリクエストの送信やレスポンスの取得に失敗する可能性があります。失敗は、一時的にも永続的にも起こり得ます。最悪の場合、LINEの全サービスへのリクエストが失敗するということになります。このような状況に対処するためには、クライアントがHTTP/2だけではなく、既存のSPDYも使用できるように維持する必要があります。

この図は、通信の仕組みを説明したものです。プロトコルを直接使うのではなく、ネットワーク抽象化レイヤーを入れてプロトコルを管理しています。どのプロトコルを使用するかを決めて、プロトコルレベルの問題を処理します。デフォルトはHTTP/2です。この抽象化層は、HTTP/2への移行だけでなく、将来のプロトコルへのアップデートにも役立ちます。

何か問題が起こると、抽象化レイヤーがそれを検知し、SPDYに切り替えます。このようにして、クライアントは前バージョンのように問題なくLINEのサービスを利用できます。すべてのクライアントでHTTP/2を無効にする必要がある場合は、外部設定を更新します。更新された設定は、クライアントのネットワーク抽象化レイヤーに伝播されます。

すると抽象化レイヤーは、使用可能なプロトコルリストからHTTP/2を除外し、クライアントはSPDYのみを使用するようになります。もちろん設定でHTTP/2をオンにすれば、ネットワーク抽象化レイヤーは再びHTTP/2を使い始めます。

クライアントの設定には、conn-infoと呼ばれるLEGYで定義されているネットワーク設定を使用します。クライアントは最初にこのconn-infoをLEGYから取得してネットワークの動作を決定します。どこに・どのように接続するか、いくつかの機能パラメーターを定義します。これは決して新しいものではありません。私たちは、HTTP/2が次の主要なプロトコルとみなされる前から、長い間conn-infoを定義して使用してきました。なぜこれが必要なのでしょうか。

それは信頼性のためです。LINEはグローバルなサービスとしてLEGYのPoP (Point of Presence)が世界中にいくつもあります。クライアントは高速で安定した通信を行うために、適切なPoPに接続できなくてはなりません。国によってネットワークの特性が異なるため、すべてのネットワークに同じネットワークポリシーを適用することはできません。クライアントの種類ごとに、独自のネットワーク設定を行い、その上で、動作するプラットフォームでのパフォーマンスを最適化できます。

またサービス機能は、アプリケーションのアップデートに合わせて追加・削除されます。そのため、基本的にはユーザーの国、クライアントの種類、そしてバージョンに応じて設定をコントロールしています。主な使用例としては、新年の準備が挙げられます。新年を迎えた直後は、トラフィックが急増することがわかっているので、自動リクエスト・再試行を無効にしたり減らしたりするように、conn-infoを設定しています。

conn-infoの使用

ここではconn-infoの例を紹介します。トップレベルのキー、ここではLEGYが設定項目を示しています。この例では、LEGYサーバーの項目を設定します。次のレベルのキーは、国です。この例ではallとJPの2つがあります。allはフォールバック設定のような働きをします。日本のユーザーに使用されるJPの設定が適していない場合、クライアントはallの設定を使用します。

次のレベルでは、クライアントの種類とバージョンを指定します。この例ではクライアントのバージョンに応じて、異なる構成を定義しています。バージョンa.b.cのAndroidクライアントとバージョンx.y.zのiPhoneクライアントは、1つ目の設定を使用し、その他のクライアントは2つ目の設定を使用します。他のクライアントタイプは設定されていませんので、この構成は使用できません。

なお実際の設定ではすべてのクライアントがLEGYに接続する必要があるため、すべてのクライアントタイプとバージョンに対応した設定を行います。設定項目はモバイルとWi-Fiの2つのグループに分かれています。これはクライアントの現在のネットワーク環境に応じて、どの設定項目を使用するかを指定するものです。

それぞれにLEGYサーバーの情報が、優先順位付けで記載されています。クライアントのネットワーク抽象化レイヤーはこれを参照し、使用する適切なプロトコルを選択します。secureフィールドでは、LEGY暗号化とTLSのどちらを使用するかを指定します。特定のバージョン以降のAndroidやiPhoneのクライアントは、HTTP/2を主要なプロトコルとして使用するように設定されています。

このフィールドをSPDYに置き換えることで、HTTP/2を完全に無効にできます。クライアントがLEGYのconn-infoを使用できないケースがあります。例えばクライアントアプリケーションがユーザー端末に新規にインストールされた場合などです。この場合には、LEGYから取得したconn-infoデータがなくてもクライアントが動作できるように、LEGYはデフォルトのconn-infoをクライアントと共有する必要があります。

デフォルトのconn-infoはLEGYの設定から、別のリポジトリに抽出されます。クライアントはこのリポジトリをソースコードにインポートし、設定はアプリケーションのバイナリ内で展開されます。クライアントが実行されると、まずローカルにconn-infoのキャッシュがあるかどうかを確認します。ない場合は接続先のLEGYサーバーを決定するためにデフォルトのconn-infoを参照し、最新のconn-infoを取得します。

成功した場合は、取得したconn-infoをローカルストレージにキャッシュし、それを使用してネットワークの動作を決定します。失敗した場合は、埋め込まれたデフォルトのconn-infoを使用します。conn-infoはSPDYからHTTP/2へ安全に移行するための、主なツールとして使用されています。これが私たちの、ステップバイステップの移行プロセスです。

すべてのステップにおいて、conn-infoはHTTP/2を最優先のプロトコルと設定して、SPDYはフォールバックのプロトコルとして設定されています。開発環境でのテストのあと、私たちはHTTP/2を本番環境でリリースしましたが、更に細かな段階を踏みました。クライアント側に追加設定を設けて、HTTP/2の使用可能なユーザーをより細かく制限できるように工夫しています。

本番環境では、まずLINE社員にHTTP/2を許可し、次に日本のLINEユーザー、最後に全世界のLINEユーザーにHTTP/2通信を許可しました。ユーザーがHTTP/2の通信の仕様を禁止されている場合、conn-infoのHTTP/2 Entryはクライアントによって単に無視されます。幸いなことに、移行作業中に大きな問題は発生しなかったため、グローバルにHTTP/2を無効にするためのconn-infoの更新は行われませんでした。

現在LEGYへの接続の85パーセントが、HTTP/2を使用しています。近日中にクライアントのSPDYコードの完全削除を準備する予定です。

LINEのクライアント側の視点から解説した記事は以下をご覧ください。

後半へつづく