エンジニアHubPowered by エン転職

若手Webエンジニアのための情報メディア

LINEの技術的負債を解消している話 ─ HTTP/2へのプロトコル変更やデータ同期の最適化での改善

サービス開始から10年近くがたったLINEでは、次の10年のため技術的な負債を解消・改善する取り組みをプロジェクトで行っています。

LINEの事例にみる技術的負債の解消法 ─ 通信プロトコルやメッセージ同期の最適化を知る

長い歴史を持つアプリには「技術的負債をどのように解消するか」という課題が常につきまといます。2011年にサービスを開始したコミュニケーションアプリ「LINE」においても同様で、多機能化や、開発・運用の長期化に伴い、いくつもの負債が発生していました。

この課題を解決するため、LINE株式会社では「『LINE』の次なる10年のための改善」を目標とする、LINT(LINE Improvements for Next Ten years)という部署横断プロジェクトを立ち上げました。より利便性や信頼性の高いアプリを目指し、LINTのメンバーは地道なアーキテクチャ改善を続けています。

ここでは彼らの取り組みのなかから「SPDYからHTTP/2へのプロトコル変更」とそれに伴う「Long PollingからPushへの移行」、そして「イベントデリバリーメカニズムの改善」をピックアップし、事例とノウハウを解説してもらいました。

中村さん、大石さん、朴さん(写真)

中村 俊介(なかむら・しゅんすけ/写真左)
LINT TF サーバーサイドエンジニア
HBaseやRedisのチームで基盤ストレージの開発と運用に従事した後、現在はLINEのメッセージングコア部分のオーナーの一人として機能開発やアーキテクチャの改新を技術的にリードしている。2011年新卒入社。分散システムが好き。
大石 将邦(おおいし・まさくに/写真中央)
LINT TF Androidクライアント開発エンジニア
Android版LINEアプリの開発に携わって約4年。最近はアプリの基盤に関わる部分を主に担当。愛車はコペン。
朴 盛民(Park Sungmin/写真右)
LINT TF iOSクライアント開発エンジニア​
​2013年にLINE Plus Corporationに入社してLINE iOSアプリケーション開発チームで多様な機能の開発に従事。2014年からはLINE株式会社に転籍し、引き続きLINE iOSアプリケーションの開発に専念。この数年間はLINE BeaconやLINE Thingsのプロジェクトに参加し、最近はLINTプロジェクトにも合流、LINEのさまざまな改善を進めている。

通信プロトコルをSPDYからHTTP/2に移行

──LINEの「イベントデリバリーのコア」と呼ばれる配信システムでは、通信プロトコルとしてSPDYが導入されているそうですね。各コンポーネントのうち、どの箇所でSPDYによる通信を行っているのですか?

※GoogleがWeb表示の高速化を意図して開発した通信プロトコル。TLS接続の上にセッション層を追加し、単一のSPDYセッションで複数のリクエストを送受信する。

中村 LINEのクライアントとアプリケーションサーバー(talk-server)間には、LEGY(LINE Event delivery GatewaY)と呼ばれる内製のゲートウェイサーバーが位置しています。このゲートウェイがSPDYをベースにした通信を行っており、「SPDYとHTTPとのプロトコルの変換」「リクエストのルーティングやコネクション・セッションの管理」などの役割を担っています。

──どういった意図で、SPDYを導入したのでしょうか?

大石 LINEアプリは非常に機能が豊富ですし、ユーザー数も膨大ですから、API呼び出しは非常に高頻度になります。さらに、ユーザーがトークルームを開いているときは、ほぼリアルタイムでやりとりできるレスポンス速度が必要です。そのためには、メッセージのPushやLong Pollingといった、オーバヘッドの少ない通信を行う必要があります。

SPDYは、こうした条件を満たす通信プロトコルとして適していました。ですが、現在のSPDYは課題を抱えていることもあり、私たちはHTTP/2に通信プロトコルの移行を進めています。

──課題とは、どのような?

大石 SPDYは、もともとGoogleによって独自に開発されていたプロトコルですが、それが標準化の過程においてHTTP/2へと置き換わっていったんですね。そして、Google自身も2016年の時点でChromeでのSPDYサポートを終了させましたし、各種オープンソースのライブラリもSPDY関連の機能はサポートしなくなってきました。社内で用いられている通信ライブラリのうち、SPDYに関連する機能は自社内でメンテナンスしています。ですが、自分たちでライブラリのメンテナンスを続けるのはなかなか大変な作業です。

できる限り、標準に沿ったプロトコルを使って、デファクトスタンダードのライブラリを使用できるようにしたい。私はAndroid版のLINEアプリの開発を担当しているのですが、もし標準的な通信プロトコルを使っていれば、OkHttpという通信ライブラリを導入でき、多くの利点が生じます。

LINEのアーキテクチャにおけるSPDYの配置

▲ LINEのアーキテクチャにおけるSPDYの配置

──だからこそ、HTTP/2への切り替えを行いたいわけですね。

大石 さらに、このプロトコル変更を機に、既存コードの大幅なリファクタリングをしたかった、という意図もあります。

Android版のLINEアプリにおいて、通信に関する実装に技術的な負債が溜まっていました。レイヤ・バイオレーションが生じており、適切な責務の分解ができていない部分があったのです。そこで、プロトコルの移行と並行して、アプリケーションの基盤に関わるコードのリファクタリングも進めていきました。通信のような非同期的な処理に対してKotlinコルーチンを導入することによって、いわゆる「コールバック地獄」の解消を目指したりといったことです。

この詳細は、LINE DEVELOPER DAY 2019でも「LINE Androidアプリの開発をいかにモダン化していくか」と題して発表しています。

LINE Android アプリの開発をいかにモダン化していくか - LINE DEVELOPER DAY 2019

抽象化レイヤーを設置してプロトコル移行のリスクを低減

──SPDYからHTTP/2への移行にあたり、スイッチングの抽象化レイヤーを設けたとか。この移行プランを採用する利点とは何でしょうか?

 SPDYからHTTP/2への切り替えを行う際、何より優先しなければいけないのは「ユーザー影響を抑えながら、可能な限り安全に移行すること」です。切り替えやトラブル発生時の切り戻しのときに、何かのトラブルが起きることは絶対に避けなければいけません。

その観点で考えると、「全く抽象化レイヤーを設けず、あるタイミングでクライアントアプリとLEGY間の通信プロトコルを一気に切り替える」というプランは現実的でないことが分かります。

アプリをアップデートするタイミングはユーザーごとに違いますから、ある時点で「ユーザーAは最新バージョンを使っているが、ユーザーBは古いバージョンを使っている」ということが十分にあり得ます。この場合、プロトコルを一気に切り替えると、ユーザーBは通信そのものができなくなるおそれがあります。

また、アプリの通信まわりの処理にバグがあったときに、切り戻しも困難になります。アプリの再リリースを行うとき、iOSではAppleの審査が必要になるため、早くて数日から1週間程度の時間がかかってしまうからです。ユーザーがその期間にアプリを使えなくなれば、影響範囲は甚大なものになってしまいます。

▲ クライアントとLEGYの間に設置された抽象化レイヤー(LINE DEVELOPER DAY 2019におけるLINTの発表より)

──それらの前提条件をクリアするには、抽象化レイヤーを差し込んでおくのが最も都合がよかったわけですね。

大石 加えて、先ほどお話しした既存コードのリファクタリングを実施する上でも、SPDYとHTTP/2の両方を許容してくれる抽象化レイヤーの存在は重要でした。

特定の期間内で全ての機能を修正するのは無理があるため、各機能を五月雨式に修正していく必要があります。アプリ内で用いている通信プロトコルが混在する可能性があるわけです。その差分を吸収する上でも、抽象化レイヤーは非常に役に立っています。

Long PollingをPushへと切り替えて通信量を最適化

──HTTP/2への移行により、通信量も最適化できるようになるそうですね。

中村 LINEのイベントデリバリーにおいて、LEGYとクライアントとの間はLong Pollingという方式で、イベントのやりとりを行っています。これは定期的なクライアントからのリクエストに対して、単純なPolling方式のようにLEGYが即レスポンスするのではなく、pollingコネクションを維持しながら遅延処理を入れることで、実際に更新情報が入ってきたときにレスポンスする方式です。

talk-serverには、fetchOperationsという主要なAPIがあるのですが、実はそのレスポンスの大半は更新情報がなく、空の状態で返ってきていました。そこでLong Pollingを採用することで、この空のリクエストについて、単純なPollingと比べてLEGYとクライアント間のやりとりを大きく削減してきました。

しかし、この通信方式にはまだ効率面に課題があります。

大石 Long Pollingによる通信の場合、クライアント側がメッセージの有無を問い合わせた後、talk-server側は何十秒か待機した上で「何もイベントがない」という意味の空のレスポンスを返すんですよ。その後、同様の処理を何度も回していきます。通信の仕様上、無駄なやりとりもまだ発生します。

通信プロトコルをHTTP/2に切り替えることで、Pushによる通信が使えるようになるため、これまで無駄の多かったLEGYとtalk-server間とのやりとりを最適化できるようになります。何かのイベントが発生したときだけ、フォアグラウンドになっている待機中のプロセスに対してPush的にレスポンスを返すという構造にできます。

▲ Long Pollingと、新たに採用を進めているPushの比較(同じくLINTの発表より)

中村 Long Polling方式でも、talk-server内部では未だにfetchOperationsの33%が空のリクエストです。Push方式に移行できれば、この空のリクエストを完全になくすことができます。

そうすると、talk-serverのサーバー台数を減らしてコストを減らせますし、アプリからのリクエスト数も減るため、ユーザーのモバイル端末のバッテリー消費も抑える改善につながります。

アプリの利用状況に応じて最適なデータ同期の方法を

──LINEのイベントデリバリーメカニズムでは、データ同期のアーキテクチャ改善も進んでいるとか。先ほども話が出ましたが、データ同期に用いられているfetchOperationsとは、どのような仕組みなのでしょうか?

中村 前提として、LINEはモバイル中心のメッセージングアプリとして、即時性の高いUX提供を非常に重視してきました。この実現のため、アプリ内のデータをなるべく早急に最新の状態に同期できるよう、基本的には「端末にある情報とサーバーにある情報との差分データだけを送る」という方針を採っています。fetchOperationsは、サーバー側にある更新イベント(Operation)をページネーションで取得する、単一のAPIです。

メッセージや、ソーシャルグラフ、グループ情報の更新といった各種の状態変化が、Operationというイミュータブルなオブジェクトとして、サーバー内のストレージに格納されています。クライアントはfetchOperations APIを呼び、このOperationを順次取得してローカルの状態を更新し、最後までOperationを取得したらサーバーと同期化できたとみなして、処理を終了します。

この方法はアクティブユーザーに対して、iPhoneやAndroidの端末1台だけにサービスを提供していた頃は、まだ適切でした。しかし現在では、このfetchOperations APIに非効率な部分が出てきました。ユーザーが、PC上のデスクトップクライアントや、Chromeプラグイン、iPad、watchOS、Clovaといった複数の端末でLINEアプリを使用するケースがあるからです。

例えば、iPhone上でLINEを頻繁に使用しているユーザーが、しばらく使っていなかった他の端末で久しぶりにLINEを起動すると、大量のOperationを同期することになります。このためユーザーがその端末で再びLINEを使えるようになるまで、非常に時間がかかってしまいます。Operationを100件ずつサーバーから取得しますが、それをクライアントが順序を守りながら逐次的に処理する必要があるからです。同期が完了するまで、ユーザーに対してアプリの操作をブロックする必要があるので、使い勝手も悪くなります。

この問題を解決するため、fetchOperations以外の方式も併用するという方針を決めました。

──具体的には、どのような方式を用いようとしているのでしょうか?

中村 もともとLINEには、特定の端末でアプリを初回起動した際、サーバー側にある全データを取得するために用いるSnapshot APIというものがあります。Snapshot APIは、ユーザーの設定・ソーシャルグラフ・グループ・メッセージングといった各カテゴリー別にインデックスされたSnapshot情報を提供するAPIの集合体です。

今回、LINTプロジェクトでは、この限られた用途だけで使われてきたSnapshot APIを積極的に活用することで、同期に時間がかかる問題を解決しようと取り組んでいます。具体的には、fetchOperations APIを呼び出したときに、サーバー側で与えられたクライアントの端末状態からOperationの差分がどれくらいあるかを判別して、必要に応じてOperationではなく、Snapshot APIを呼び出すようクライアントに指示する、というFullSyncメカニズムを導入します。

Snapshot APIを用いれば、クライアントはオンデマンドでデータ同期できます。逐次ではなく、カテゴリー単位で一括して最新のデータを同期したり、メッセージングのようなデータ量の多いカテゴリーについては、チャットルーム単位で遅延処理を入れたり、新しいメッセージから順に同期化することもできます。

これらの改善によって、ユーザーが任意の端末で比較的早くLINEのサービスを再開でき、同時にユーザーにとって必要な情報から優先して提供できるなど、UXの改善も期待されます。

──同期処理では、他にも改善は考えられているのでしょうか?

中村 アクティブなiPhone・Androidクライアントに対しても、同期処理の信頼性向上のため、Auto Repairと呼ばれる仕組みを導入します。

Operationによる差分方式でのデータ同期は、軽量で効率的な反面、もし何かの不具合が発生してアプリがOperationの情報を1つでも受け取れなかった場合、データを自動復旧することが困難でした。例えば、サーバー内部のシステム障害やバグによって、Operationを格納するストレージへのIOに失敗したり、順序保証が担保できないケースといった日々発生し得る問題に対して、この方式はロバストではありません。

そうした事態に備えて、サーバー側とクライアント側のデータを、カテゴリー別にチェックサム的な値を定期的に交換・比較するメカニズムを追加しています。もし値が違っていれば、Snapshot APIを用いてそのカテゴリー全体のデータを復旧することができます。この仕組みがAuto Repairです。

分散システムでいうところの結果整合性的な考え方で、LINEという数千台のサーバーと推定2億以上のクライアント端末からなる1つの分散システムで発生するあらゆる問題に対して、ユーザーが使用する全ての端末で最終的にサーバーとデータの整合性が取れた状態の実現を目指しています。

アーキテクチャの改善でアプリの信頼性や拡張性が向上

──今回出てきたようなアーキテクチャ改善を実施するには、前提として「改善すべき箇所を発見すること」が必要不可欠かと思います。どのようにして、そのポイントを見つけ出すべきでしょうか?

 大切なのは「計測すること」ですかね。ログを仕込んだり、アプリやサーバーのさまざまな数値を可視化することで、アプリがどのような課題を抱えているのかが見えてきます。

大石 例えば、Operationの同期に関する情報は、LINTプロジェクトが開始してから初めて可視化しました。「同期処理の何%くらいが失敗していたのか」「同期にどれくらいの時間がかかったのか」などを調査した結果、予想していたよりもエラーの発生率が高いこと、この課題を解消することでアプリの使い勝手が大きく改善することが見えてきました。

──やはり、計測することはとても重要なのですね! 最後に、LINTプロジェクトに携わってきて感じた、LINEでの仕事のやりがいや、大規模なプラットフォームのアーキテクチャを改善する意義について教えてください。

中村 僕はLINEという企業に初期の頃からいて、アプリの立ち上げ時期から現在までを見続けてきました。事業フェーズごとにエンジニアの置かれている環境はどんどん変化し続けており、働いていて全く飽きないです。提供しているサービスの種類やユーザー数も増え、今やLINEはアジアにおけるインフラの1つというところまで成長しました。

今後もユーザーにとってのLINEの価値をさらに向上させるには、「アプリとしての信頼性や拡張性をどうやって高めていくか」を考えることが必要になってきます。だからこそ、LINTプロジェクトは非常に意義のある取り組みだと思えました。

今回お話したイベントデリバリのプロトコルとメカニズムの改善は、実際にLINTプロジェクト全体で見ると、まだ始まりの部分にすぎません。今後、その改善に紐付いた多くの技術的なチャレンジがあり、そういった課題を多種多様なスキルを持つエンジニアたちの力を合わせて解決することに、私は面白さを感じています。

大石 私は入社した時点で、会社からも技術的負債を解消していくような役割を求められていました。LINTプロジェクトに関わるようになったのは、ある意味で必然ですね。

そして、このプロジェクトに携わって、得るものは非常に大きかったです。アプリのアーキテクチャをどう改善するかを考え、プロジェクトを自分たちの力で推進していくことが、エンジニアとして成長する上で重要なことだと感じました。

 LINEはこれだけ大規模なプラットフォームですから、そのアーキテクチャを改善することのインパクトは大きいです。今後も多くのエンジニアが入社してくると思いますが、そうしたエンジニアに「私たちはこういう改善をしてきたんだよ」と自信を持って見せられるような仕事をしていきたいです。

そして究極的には、数々の改善施策がユーザーのみなさんにとっての「より便利で使いやすいLINE」につながっていけばいいなと思っています。

取材・執筆:中薗昴

関連記事