LINE DEVELOPER DAY_2015 Tokyo「LINE Platform Development Chronicle」レポート #linedevday
はじめに
こんにちは、虎塚です。
今日は、LINE株式会社さんが開催されているイベントLINE DEVELOPER DAY_2015 Tokyoに来ています。
午前中のラストセッション「A-4: LINE Platform Development Chronicle」を聴講しましたので、レポートします。LINEのサーバサイドを開発されているTom Tsuruharaさんによる講演でした。
セッション概要「LINE Platformを支えるアーキテクチャ、組織、文化について、LINEの歴史を振り返りながらお話します」とのとおり、充実したセッションでした。
以下、レポートです。
セッションレポート
LINEの全体的なアーキテクチャの話として、大きく次の2テーマについて話す。
- 1.LINEメッセージング基盤の進化: アーキテクチャの進化を通してみる裏側の仕組み
- 2.LINE流Microservices: メッセージング以外も含めて各種のサービスについて
LINEメッセージング基盤の歴史
LINEでは、登録ユーザ数の増大にしたがって、基盤も拡充してきた。
初期アーキテクチャの改善: long polling
LINEは、2011年6月にリリースされた。スマートフォンで使い易いチャットアプリとして、スマートフォンが主流になってくる中でとにかく早く、ということで2ヶ月くらいでリリースした。
そのため、最初期のアーキテクチャは非常にシンプル。リバースプロキシ(Apache)、アプリケーションサーバ(Tomcat)のバックにRedisとMySQL。アプリケーションはJavaとSpring(ここは今もおなじ)。
メッセージの処理は、ふつうのpollingだった。クライアントがサーバにフェッチして、データがなければn秒スリープして、また取りに行く、というもの。メッセージを送っても最大n秒遅延してしまうので、n秒待っている間にpush通知を受け取ったら、取りに行くようにしていた。
Polling+Push通知の問題点
- pushの遅延
- pushの仕組みが外部にあったので、コントロールできなかった。メッセージアプリでメッセージが遅延することも、その部分をコントロールできないことも致命的だった
- 無駄なrequest-response
- pollingするたびに、無駄なrequest-responseが発生していた。電池を消費してしまう。
そこで、Push通知に変わってlong pollingを実装して解決した。
クライアントとサーバの間にGateway層をおき、クライアントはGatewayに対してメッセージがあるかを問いあわせる。メッセージがなければGatewayは待ち、メッセージがきたらサーバからGatewayにそのことをPublishする。Gatewayからサーバにメッセージをフェッチする。
クライアントが待つのは変わらないが、こうしてリアルタイム性を確保した。
リリース後3ヶ月くらいで、Apacheだった層を、Nginx+拡張モジュールでlong pollingする層に置きかえた。
segmentation faultの改善: Erlang製Gatewayの導入
2012年1月、1000万ユーザをこえた頃に、segmentation faultが頻発するようになった。プロキシが落ちるので対処する必要があった。
問題だったのは共有メモリだった。クライアントAがクライアントBにメッセージを送る時、サーバはクライアントBの接続を持っているプロセスにPublishするが、その部分に共有メモリを使う必要があった。共有メモリに問題があって、セグフォが発生していた。
そこで、Erlangを導入した。ポイントは、並行性、分散、高級、無停止デプロイの4つ。Erlangは、軽量プロセスをたくさん並べてメッセージパッシングで並行性を確保するので、問題になっていた処理が上手くいった。しかも、並行性の処理をアーキテクチャの奥底に堅牢に隠蔽できた。分散処理もしやすかった。C言語にくらべて抽象度も高く、高レイヤーのプログラミングができた。VMを再起動しなくてもコードを更新できるhot swap があった。サービス停止ができないメッセージサービスにおいて、無停止デプロイできることは大きなメリットだった。
というわけで、Erlang製のLINE Event Delivery Gateway(LINE社内ではLEGYと呼んでいる)を導入した。メッセージ以外にも、既読通知、友達がプロフィールを更新した通知などのイベントを、メッセージ同様にデリバリするので、イベントデリバリと呼んでいる。
Erlangで書きかえたことで、ロジックをコントロールできる形で実装できるようになり、happyだった。
connectionが多すぎる問題の改善: SPDYの導入
2012年7月、クライアントとサーバ間のconnectionが多すぎる問題が発生した。キャリアと協力して減らしていった。問題はいくつかあったが、今回はその中から、プロトコロルレベルでconnection数を減らす改善をした話をする。
そもそも、なぜそんなにconnectionを張っていたかというと、HTTPでlong pollingのconnectionに加えて、その他のAPI用に、つねに2本以上のconnectionを張っていた。
これに対して、HTTP(S)からSPDYへプロトコルを変更した。SPDYは、当時はGoogleから提案されたdraftのプロトコルだった。しかし、LINEが直面していた課題をほとんどすべて解決するプロトコルだったので採用し、LEGYとクライアントアプリに実装した。
2015年になってHTTP2という名前で広く知られるようになったとおり、1つのconnectionの中で複数のconnectionを扱える。また、ヘッダ圧縮の機能があり、通信量を減らせる。LINEはモバイルの通信量が少ない環境でも利用されているので、このことはユーザ体験の向上に効いてくる。
結果として、connection数は、半分〜3分の1に削減できた。
海外でのパフォーマンス追求: 非同期メッセージ送信の導入
2012年10月、海外でのパフォーマンスを改善することにした。LINEのほとんどのサーバが、メインのデータセンターに存在する。遠いところにいるユーザは、ネットーワクレイテンシや帯域の影響を直接受けていた。
ソフトウェアのロジックでは解決しづらいので、グローバルの拠点にデータセンター(POPと呼ぶ)を構えて、ロードバランサとLEGYを設置した。クライアントは、最寄りのLEGYをみつけて通信する。LEGYとメイン拠点の間は、専用線で接続した。
しかし、専用線とはいえ、距離が遠いとレイテンシはやはり大きい。そこで、非同期メッセージ送信を導入した。
LEGYは、バックエンドにSendした後、応答を待たずにクライアントへレスポンスを返す。UIを更新して、ユーザが次のアクションが取れるようにした。これによってUXを向上させた。Backendで通信エラーになった時にリカバリできないので、LEGYは先ほどのバックエンドへのSendの結果を受け取ってクライアントへ返し、エラーはクライアント側でカバーする。
メッセージ基盤は、このように進化してきた。現在も、MySQLがHBaseに変わった以外は、初期アーキテクチャのままになっている。世界規模でメッセージングを捌いているが、図にするとシンプルだ。
LINE流Microservice
ここからは、メッセージング以外の話。
LINEは、サービスの成長にしたがって、スタンプやタイムラインなどの機能を追加してきた。それらをどのように実装してきたかを話す。
LINEは急速にユーザを獲得したこともあり、ユーザ要望が多く、また、新しい機能にはつねにスピード、機能性、品質のすべてが求められる傾向にある。これに対応する開発スタイルを実践している。
LINE流開発スタイル
MONOLITHICでなくMICRO SERVICE。
モノリシックなシステムでは、優秀な人に開発に参加してもらっても、まず全体を把握しなければ新サービス開発に着手できない。サービス間をAPIでつなぐことで、新規に参加した開発者にもすぐに新機能の開発に着手してもらえるようにした。
具体的には、Talk-server(メッセージング基盤のアプリケーションサーバ)には、メッセージングとソーシャルグラフの機能だけが実装されている。その他のすべての機能は、別のサービスとして動いていて、別々のチームが開発している。
開発プロセスやデプロイフローもサービスごとに独立していて、開発言語やバックエンドのミドルウェアもバラバラ。たとえば。Talk-serverはJavaだが、アカウント管理はScala。バックエンドのメインはHBaseとRedisだが、サービスによってはMongoやCassandraを使っている。
たとえば、次のような各サービスを独立して開発し、APIでつないでいる。
- 認証管理
- abusing検出
- イベント処理
- push通知基盤
- CMS
そのため、全体としてはシンプルに開発できる。
開発の工夫
サービスを分けて開発するために行っている工夫を3点紹介する。
- 組織
- チームを独立したものにしても、それぞれに裁量をもたせないと、コミュニケーションコストが増えたり、担当が誰だかわからない仕事がでてきたりして、開発の効率が上がらない。そこで、組織図や会社をまたぐアドホックなチームを編成した。
- 短期間でイテレーションをまわし、目的を達成したら縮小あるいは解散する。
- プロトコル管理
- Apache Thrift、Protobuf、RESTの採用
- 簡単にやろうとして、wikiにAPIを書きならべて、それをベースに開発するようなことになりがち。それを避ける。
- 上記のうちThriftを今回は説明する。Thriftはインタフェース定義言語(IDL)。Thriftでサービスのインタフェースを記述する。Thriftは、記述されたものから各種言語のStubを出力する。これによって、インタフェースが明確に定義される。IDLはテキストベースなので、APIの(サービスの)スペックに関するディスカッションを、Github上でPull Requestベースでできるようになる。微妙なAPIを乱立させると全体としていびつになるので、こういうところからサービスをレビューしていく。
- ファシリティ(インフラ)
- 開発チームがたくさんできるので、共通して必要なものを用意しないと、みんなが同じものを作っていたみたいなことが起こり、全体として開発効率が落ちる。
- 共通の開発フローに応じたファシリティを用意する。Github Enterprise、Jenkins、内製のデプロイツールPMC(ブラウザベースでGUIでリリースできる)、内製のサービスモニタリングツールIMON(裏側で様々な指標を可視化できる)
このような開発スタイルをはじめた頃には、まだMicroserviceという言葉がなく、いびつな部分が残っている。しかし、Microserviceの利点とされているものは享受できている。
組織が大きくなることで開発の効率も上がることが重要だと思う。
今後の課題
- マルチデータセンター
- 海外にはフロントエンドサーバだけがあり、最後はどうしてもメインのデータセンターにつなぐ必要がある。理想は、海外の現地で完結してサービスを受けられるようにしたい。
- Microservicesの発展
- Talk-Serverが結構巨大になってきており、分割したい。また、あるサービスが落ちた時、全体的にどうなるかを管理する障害設計を、これからさらに整備する必要がある。
おわりに
なかなか聞くことができない世界規模のアプリケーションの裏側のお話、大変面白かったです。当初のアーキテクチャの骨格が今も残っている点と、そうはいってもErlang製GatewayやSPDY(HTTP2)の導入などのダイナミックな変更を取り入れて基盤を成長させてこられたお話が、両方印象的でした。
通常は、社内の人数が増えて組織が大きくなったら、開発にオーバーヘッドが出てしまいがちだと思います。しかし、「組織が大きくなることで開発の効率も上がることが重要」と言いきってらっしゃったのは、カッコいいなあと思いました。マクロサービスという言葉が流行する以前から、開発スタイルを模索してこられたLINEさんだからこそ言えることなのでしょうね。
それでは、また。
今回のイベントのその他のレポートは、こちらです。