Reactのサーバサイドレンダリングとパフォーマンスについて調べてたのでメモ
基本的なこと
サーバサイドとReactのproduction build
要約: Reactをサーバサイドで使うときも、クライアントサイドのように圧縮(というよりはcode eliminate)しないと遅い
Reactはdev向けのコードを大量に含んでいる。
これはprocess.env.NODE_ENV !== 'production'
の時実行されるassertや警告などが主となりproductionには必要ない。
そのため、process.env.NODE_ENV = 'production'
をしないとかなり不利な結果を得る。
I tend to agree that "compiling server side code with webpack to remove access to process" is not what Node developers would typically do, and likely many React users are not even aware of this possibility. So the current results are probably more representative of React SSR perf in the wild. However, it can also be a bit unfair to not show React's perf with full optimization.
サーバサイドもprocess.env.NODE_ENV === 'production'
でuglifyしてdead codeを消し去ったReactを使うことでデバッグ情報がないものとなる。
Reactが公式で配っているbundle済みのもの(min.js)を使うのが手早い。
- webpackのaliasでproduction build時に差し替える
- Server rendering is slower with npm react · Issue #812 · facebook/react
- Hoist
__DEV__
's process.env use out of hot code. by STRML · Pull Request #86 · facebook/fbjs
将来的には、Reactはdevとprodのbundleをそれぞれ配布しREACT_ENV
でどちらを使うか設定出来るようになるかも とのこと。
(NODE_ENV
も引き続き見る感じ)
パフォーマンス計測
- サーバを立てて
ab -n 100 -c 10
とかで計測 - 参考:
- ボトルネック
- データ取得周り
- データ取得してrenderが完了するまでレスポンスが返せない
- サーバ CPU/メモリ
- renderToStringがCPU boundな処理
- クライアント CPU/メモリ
- データ取得周り
デバッグ
node --inspect server.js
で Chromeでプロファイルを取ってみる
- CPU Profileを取る
- "Chart"表示にすれば見慣れたタイムラインでプロファイルを読める
最適化の種類
- Cache
- LRUなキャッシュがよく使われる
- ユーザー属性はキャッシュしない工夫が必要
- Partial Rendering(Above The Fold Render)
- Renderの変更
-
renderToString
が同期で重いのがボトルネック - render自体を非同期で行う試み
-
gabrielbull/react-router-server: Server Side Rendering library for React Router v4.
-
renderToString
を非同期(AsyncRendererというコンポーネントでラップしてる)
-
-
ちなみに一番最初にmemcachedではなく、Redisを使いました。しかし、Redisにおいてオブジェクトのサイズが10kbを超えて、特にevictionが発生するとパフォーマンスが急激に落ちます。そのため、途中memcachedに切り替えました。
現実的な解として
- renderToStringが遅い問題をどうにかする必要がある
- レンダリングするコンポーネントを減らす
- 結果をキャッシュして返す
- の2つが主と解となり両方を組み合わせる事が多い
- キャッシュ
- ユーザ情報を含んだrender結果をキャッシュすると問題がおきやすい
- ユーザ情報だけサーバではrenderしないようにするコンポーネントの実装が必要になる
TODO:
-
renderToString
をClusterで並列化するような事例もある? -
キャッシュするにはユーザ情報をコンポーネントから隠すスイッチがいる
- これをミスなく上手くやる方法ってあるのかな? うっかり事故りそう
- ReactJS SSR Profiling and Caching – WalmartLabs – Medium
- Component Cache-ability
目安
レスポンスまで100ms以下
- cache: 30ms
キャッシュされたページのレスポンスタイムは50ms以下になっています。
アメブロ2016 ~ Isomorphic JavaScriptの詳しい話 | CyberAgent Developers Blog | サイバーエージェント デベロッパーズブログ
事例
- Reactサーバーサイドレンダリングに関するリソースまとめ - Qiita
- アメブロ2016 ~ Isomorphic JavaScriptの詳しい話 | CyberAgent Developers Blog | サイバーエージェント デベロッパーズブログ
- アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~ | CyberAgent Developers Blog | サイバーエージェント デベロッパーズブログ
- Using Electrode to Improve React Server Side Render Performance By Up To 70%
-
React with Reduxによる大規模商用サービスの開発 / nodefest2016 // Speaker Deck
- リクルートテクノロジーズのフロントエンド開発 2016 - from scratch
- redux-async-loaderでのデータ取得
- React Router v4に移行はできてない
- WIP: chore:[semver-major] upgrade react-router v4 by yosuke-furukawa · Pull Request #5 · recruit-tech/redux-async-loader
- LWJGL/lwjgl3-www: The LWJGL 3 website.
未来
-
Add package builds for new server renderer and enable tests by sebmarkbage · Pull Request #9710 · facebook/react
- ReactでStreamRenderが実装中
- renderToStringの負荷がもう少し分散するはず
- いわゆるreact-dom-streamの公式実装