PLAIDがNode.jsを採用し、5年間で12万行書いてわかったこと
エンジニアのgamiです。趣味はポッドキャスト配信です。 今回は、「Node.js」に焦点を当てた記事です。 PLAIDでは、約5年前から「KARTE」というサービスを開発しています。そのサーバーサイドの実装は、ほとんどがNode.jsで記述されています。 一方、国内企業の中で、サーバーサイド実装にNode.jsを採用している企業は少ない印象があります。 実際にPLAIDがどのようにNode.jsを使っているかを紹介します。PLAIDでは、「リアルタイムユーザー解析サービス」であるKARTEを主に開発しているので、KARTEの開発について説明します。 まずは前提として、KARTEのプロダクトとしての規模がわかるデータを紹介します。 KARTEが解析しているデータの規模感は、2018年8月現在の値でおよそ以下の通りです。 KARTEが扱うユーザー情報は、「イベント」という単位で連携、解析されます。エンドユーザーのブラウザから飛んでくるリクエストの中に、1つ以上のイベントが含まれています。 KARTEのメインのリポジトリのうち、Node.jsで書かれたサーバーサイドの実装に関するファイルの数と行数をカウントすると、以下の結果になりました。 (マイグレーションバッチ、テストコード、空行、コメント行などは除く) 他にも、一部の独自ライブラリはリポジトリを分けて開発しているので、それを加えるともう少し大きくなります。 以下の図がKARTEの大まかな構成図です。 今後の説明のために、ざっと解説します。 KARTEでは、解析対象のサイトにタグ(ネイティブアプリならSDK)を設置してもらいます。 特に、エンドユーザーからのリクエストに対してポップアップなどの最適なアクションを1秒以内に返す必要があり、高いパフォーマンスが求められる部分です。後述するように、NodeのClusterモジュールを使って負荷分散しています。 なお、KARTEではエンドユーザーから送られるイベントデータのスキーマをほとんど決めていないため、大量のスキーマレスなJSONデータを受け取る必要があります。Node.jsはJSONデータの取り回しが楽である反面、大規模なJSONを大量に扱うというCPUインテンシブな処理が苦手なのが悩みです。 「admin」では、管理画面上でユーザーデータを閲覧できる機能等を提供しています。 KARTEではスキーマレスなNoSQL DBである「MongoDB」を多用しています。MongoDBはほぼJSONライクなドキュメントを扱うので、Node.jsとの相性はかなりよく、DB周りの処理を自然に書くことができます。 主に実現している機能は以上です。 KARTEは「単純なWebサーバーとしての機能」だけではなく、「エンドユーザーの解析機能」をプロダクトのコアとして提供しています。その両方の機能について、特に言語を分けずに全てNode.jsで記述しています。 なお、システムの全体像や解析サービスを実現するGCPの活用方法については、PLAIDのエンジニア陣が執筆した以下の記事をご覧ください。 大規模解析サービスを支えるGCP活用事例連載一覧:CodeZine 言語については、場所によって使い分けています。 解析エンジン部分だけは、固めに書きたいのでTypeScriptを使っています。大半のソースコードは、開発速度を優先して生のJavaScriptで記述しています。 元々はCoffeeScriptを使っていたのですが、世の多くの会社がそうであるように、Pure JavaScriptの機能強化に伴い、徐々に書き換えを進めています。 主に以下を使っています。(挙げるとキリがないので、だいぶ削っています) 上述したKARTEの実装を日々書いているPLAIDのエンジニアに、Node.jsのメリットとデメリットを訊いてみました。カテゴリ別に整理します。 Node.jsの良く知られたメリットは、KARTEの開発でも享受できています。 逆に、非同期処理を多用するため、例外発生時やプロファイルをするときに問題の箇所を特定するのが難しかったり、そもそも記述が複雑になりやすかったりします。 シングルスレッドであることで設計がシンプルになりやすい点は、意外とメリットを感じる点です。 やはりCPUバウンドな処理が苦手な点が大きな弱点です。特に弊社の場合は大規模なJSONを扱うことが多く、JSONのパージングが同一スレッドで起きることでブロックされることに悩まされたりしています。 特にPLAIDではフロントエンド/サーバーサイドでエンジニアを分けずに開発しているので、どちらも同じ言語で書けるというメリットは大きいです。 逆に、Node.js自体が自由すぎて起こる問題もあります。なお、一部では前述のようにTypeScriptを導入しています。 大量のnpmライブラリがOSSとして公開されていて、CTO曰く「センスがいいモジュールが多い」ということです。 また、Node.jsが使っているJavaScriptエンジンであるV8の進化が早く、Node.jsのバージョンを上げただけでパフォーマンスが大幅に向上したことがあります。過去にはKARTEでも、Node.jsのバージョンを6から8に上げたとき、パフォーマンスが2倍以上上がった例がありました。 反面、npm自体のバグや依存性解決の難しさで時間を溶かすことも多かったです。依存性解決については、以前このブログでも紹介しています。ただし、npmのバージョンアップで徐々に解消されつつあります。 大規模で高いパフォーマンス要求を求められるプロダクトの実現や、Node.jsの弱点の補完のために実施していることをいくつか紹介します。 KARTEでは最大で1秒間に数千リクエストを処理する必要があります。ただし、通常は1つのポートにアクセスできるプロセスは1つだけであり、そこがボトルネックになってしまいます。 そこで、Node.jsのClusterモジュールを使うことで、Cluster内に組み込まれたロードバランサが、リクエスト処理を複数のワーカープロセスに分割してくれます。これにより、CPUのコア数を最大限活かすことができます。 KARTEでは、特にtrackのパフォーマンス向上に役立っています。 CPU使用率が急上昇したときに、どの処理が重いのかを調べるため、CPUのプロファイリングをすることは多いと思います。しかし一部のプロファイリング用のライブラリは実際にコードの中に組み込むため、アプリケーション自体のパフォーマンスに影響する可能性があります。 そこで、perfコマンドで外側からプロファイルを取ることで、パフォーマンスにほぼ影響を与えずにCPUリソースを無駄に使っている関数を特定できるようにしています。 Node.jsでperfを使ったプロファイルを実現するには、 プロファイル結果は、このライブラリを使ってFlameGraphとして出力しています。FlameGraphというのは、こういうやつです。 出典: Profiling Node.js | Node.js 使いどころとしては、hubotのコマンド経由でいつでも実行できるようにしている他、特定のインスタンスについて自動でプロファイル結果を出力するようにしています。KARTEではDatadogで各インスタンスのパフォーマンスを監視していますが、パフォーマンスが出ないインスタンスは自動で殺すようにしています。後からその原因を追いたい場合に備えて、殺す前に自動でperfコマンドを実行してプロファイルをしています。 なお、KARTEを支える監視サービスと構成については、以下の記事をご覧ください。 大規模解析サービスを支える監視サービスと監視構成のポイント | CodeZine(コードジン) Node.jsは基本的にシングルスレッドで動作するので、特にCPUインテンシブな処理が苦手です。KARTEではスキーマレスな構造データを大量に行うため、その点がボトルネックになりやすいのが悩みでした。 Node.jsでもマルチスレッド処理がしたいという熱い想いは、弊社エンジニアの牧野が以前ブログで書いています。 MicrosoftのNapa.jsでJavaScriptをマルチスレッド化する | PLAID engineer blog Node.js v10.5.0からWorker Threadsが使えるようになったので、今後は一部の処理をそちらに置き換える計画を立てています。 Node.jsを採用して5年間ほどKARTEの開発に使い続けましたが、特に「リアルタイムユーザー解析サービス」である特性上、その恩恵を受けることが多い。 また、Node.jsやV8の進化も早く、今後の発展が非常に楽しみです。 国内企業がプロダクションでNode.jsを採用する例はまだまだ少ないですが、とても強力な言語なのでぜひ使っていきましょう! リアルタイムユーザー解析プラットフォームの「KARTE」を運営するプレイドでは、KARTEを使ってこんなアプリケーションが作りたい! KARTE自体の開発に興味がある!というエンジニア(インターンも!)を募集しています。 詳しくは弊社採用ページまたはWantedlyをご覧ください。 もしくはお気軽に、下記の「話を聞きに行きたい」ボタンを押してください!
そこで、以下についてご紹介します。
1. PLAIDがNode.jsで実現しているプロダクト
プロダクトの規模
リクエスト数、イベント数
1日で10億回ものユーザー解析を処理しているので、かなり「大規模」と言えるかと思います。ファイル数
実現している機能
track/analyze(エンドユーザーの解析とデータ送受信)
そこから送られるイベントをリクエストとして受け取り解析をするのが、「track」と「analyze」です。
admin(クライアントに管理画面を提供)
言語(AltJS)
フレームワーク、ライブラリ
2. Node.jsを5年間使ってわかった強みと弱点
ノンブロッキング/イベントループ
強み
弱点
シングルスレッド
強み
弱点
言語としての性質
強み
弱点
npm/エコシステム
強み
弱点
3. Node.jsを使いこなすための工夫
Node.jsのClusterモジュールによる負荷分散
perfコマンドとFlameGraphを使ったCPUプロファイル
--perf-basic-prof-only-functions
などのプロファイル用のオプションを起動時に付ける必要があります。オプションを付けるとperf用の.map
ファイルを吐いてくれます。マルチスレッド処理へ
まとめ