【インターンレポート】LINE LIVEの新機能開発や負荷対策を行いました

はじめに

こんにちは、京都大学2年の岸直輝です。この度、LINEの冬インターンシップのエンジニア就業コースに2/18~3/15の一ヶ月間参加させていただきました!

今回のインターンでは、サーバーサイドエンジニアとしてチームに加わり、通常の業務と同じタスクに取り組むことができました。ここでは、その内容について紹介したいと思います。

取り組んだテーマについて

私はLINE LIVEというライブ配信サービスのサーバーサイドのチームに配属され、LIVE Q」という新機能のAPI開発や負荷対策の設計・実装・検証を行いました。

ここで簡単に「LIVE Q」について説明しておきます。「LIVE Q」は時間で進行していくクイズイベントで、開始時刻になると、参加を希望した配信者全員に一斉に問題が配布されます。配信者は出題される問題を順番に解いていき、その正解数を競い合います。視聴者はクイズに答える配信者をアイテムで応援することができ、好きな配信者をサポートすることができます。TVのクイズ番組で芸能人が問題に答える様子を見ている感覚に近いと思います。

さて、「LIVE Q」は仕様上、多くのユーザーが同じタイミングでアクセスするという性質を持っています。 例えば、問題配布時には、配信者だけでなく、その配信を見ている視聴者全員が同じAPIにアクセスします。 そのため、愚直に実装を行うとレスポンスが重たくなり、ユーザー体験が損なわれることが予想されました。

そこで、インメモリデータベースであるRedisを効率的に使うことによって処理時間が短くなるような設計を考え、実装しました。

Redisを使った負荷対策

簡単にRedisについて説明します。RedisはNoSQLデータベースの一つで、メモリ上でデータを管理するため、MySQLよりも高速に動作することが特徴です。LINE LIVEでは、Redisの分散型システムであるRedis Clusterを利用しています。自動フェイルオーバーなどの機能が備わっているため、高い可用性を保ったまま運用することが可能となっています。

今回はそのRedisを主に2つの用途として使用することにしました。

  • 1つ目はMySQLに管理されているデータのキャッシュです。 クイズの問題文や選択肢などはMySQLに保存されていますが、Immutableなデータな為、毎回MySQLにアクセスするのは無駄です。そこで、クイズが開始される前にMySQLからRedisにキャッシュをする処理を挟むことにしました。
  • 2つ目は配信者のステータス管理です。 配信者の回答情報は後にランキングの生成などで使用するため、MySQLで永続化する必要があります。しかし、回答情報は視聴者が数秒おきにAPIにアクセスして取得するため、こちらも毎回MySQLから読み込むと負荷が高くなってしまいます。MySQLに書き込むと同時にRedisにも書き込むことで、回答を取得する際はRedisからも取得できるようにしました。

これらを実装したことで、MySQLに対するアクセスを減らすことができ、データベースがボトルネックにならなくて済むようにすることができました。

Spring Bootを用いたAPI実装

Redisの構成が決まった後は、RedisのDAO(Data Access Object)や、APIのコントローラ、サービスを実装しました。

LINE LIVEのサーバーサイドの開発言語はJavaで、WebフレームワークにはSpring Bootが使われています。しかし、私はインターンに来るまでJavaを一切書いたことがなかったので、既存のコードを読むのにとても苦労しました。特に、Dependency Injection周りの処理やモックを使ったテストがなかなかうまく動かず、何回もNull Pointer Exceptionを出してしまいました。最初の1週間はJavaを勉強しつつコードを読み進めていき、なんとか1週目の最後にPull Requestを出せるレベルまでたどり着くことができました。

APIの実装が大方終わった最後の週には、iOSアプリと合わせて動作テストを行いましたが、Redis周りでいくつかバグが見つかってしまいました。配信者のステータス管理に考慮漏れがあったのが原因だったのですが、コードをもう少し見通しよくかけていたら早期に発見できたのではないかと反省しています。

ここまで大きなプロジェクトに携わったことがなかったため、依存の流れをシンプルにすることの重要さを実感することができました。

APIサーバの負荷テスト

せっかくRedisを使って負荷対策をしたので、最後の3日間くらいで負荷テストをやってみました。ただテストするだけでは面白くないので、一台のAPIサーバに対し、複数のサーバから負荷をかけられるようにし、分散負荷テストができる構成を作りました。

HTTPリクエストの生成や各種メトリクスの計測には、Golang製のVegetaを使用しました。Vegetaはリクエストの総数や実行時間を決めて実行すると、GoroutineでHTTPリクエストを生成し、レスポンスのレイテンシーやステータスコードなどを集計してくれるライブラリです。今回はVegetaをHTTPサーバでラップし、HTTPリクエストを送ることで負荷テストを開始できるようにしました。負荷テスト開始の指示はWebコンソール経由で行えるようにし、複数のアタッカーで負荷テストを同時に開始できるUIも作成しました。この仕組みによって、アタッカー側の負荷を分散させ、測定結果にアタッカー側のスペックが影響しないようにすることができました。

そして以下が、2台のアタッカーサーバを使用して、APIサーバに対して負荷テストを行った結果です。

左: MySQLを使うAPI 右: Redisを使うAPI

合計リクエスト数と平均レイテンシーの関係 (Chart.jsで作成)

2つの片対数グラフを見てみると、秒間リクエスト数が小さい時は素早くレスポンスを返すことができています。しかし、MySQLを使うAPIは600 req/sから、Redisを使うAPIは1000 req/sあたりからレイテンシーが急激に大きくなっています。

Jetty(Webサーバ)のスレッドプールの数やDBのコネクションプールの数が影響しているのではと推測されますが、時間がなく原因を解明することはできませんでした。しかし、「Redisを使った方が低レイテンシーで捌くことができるレスポンス数が多い」という事実を確認することはできました。

レイテンシーだけでなく、スループットの計測を行えば違った視点からの考察ができそうなので、機会があればやってみたいと思います。

おわりに

LINEのインターンでは、インターン用の課題を決めて取り組むという形を取る場合もあるのですが、今回は実際に進行しているプロジェクトに参加してコードを書くことができました。 現役のエンジニアの方からコードレビューを受けたり、企画の方とのミーティングに参加しどういった経緯でこのプロジェクトが動いているのかといった話を聞いたりすることができたのは良い経験になりました。

また、人事の方には、懇親会やランチ会など他のインターン生と交流を深める場を作っていただきました。今回は機械学習の方が多く、自分と違う分野の話をすることも多かったのですが、分かりやすく話してくれて、優秀だなと感じる場面が多々ありました。優秀な方と横の繋がりを持てるのもこういった場ならではだと思います。

最後になりますが、インターンに応募してみようか迷っている方は、是非一歩踏み出してチャレンジしてみてください!LINEには学歴や国籍を問わず優秀な人が集まっています。そんな環境で1ヶ月(もしくは2ヶ月)働けるというのは、自分の実力アップにも繋がります。絶対に来てよかったと思えるので、皆さん是非応募してみてください。

 

Related Post