サーバーレンダリングの見方とその結果

みなさんこんにちは! 1年の間に、Reactに切り替えて、ユーザーがクライアントテンプレートを待たずに、できるだけ早くページを見られるようにする方法について考えました。 この目的のために、サーバー側レンダリング(SSR-サーバー側レンダリング)を実行してSEOを最適化することにしました。これは、すべての検索エンジンがJSを実行できるわけではなく、実行に時間を費やすことができるものであり、各サイトのクロール時間は制限されているためです。













サーバーレンダリングとは、クライアントにHTMLを用意するためにサーバー側でJavaScriptコードを実行することです。 これは、特に低速のマシンおよび低速のインターネットで、ユーザーが認識するパフォーマンスに影響します。 JSがダウンロード、解析、実行されるまで待つ必要はありません。 ブラウザは、JSaを待たずにすぐにHTMLのみをレンダリングできます。ユーザーはすでにコンテンツを読むことができます。

したがって、受動的な待機フェーズが削減されます。 レンダリング後、ブラウザは完成したDOMを通過するだけでよく、レンダリングされたものと一致することを確認します

クライアントで、イベントリスナーを追加します。 このプロセスは水和と呼ばれます。 ハイドレーションプロセス中に、サーバーからのコンテンツとブラウザーによって生成されたコンテンツとの間に矛盾がある場合、コンソールで警告が表示され、クライアントで追加のレンダリングが行われます。 これはすべきではなく、サーバーとクライアントのレンダリング結果が一致することを確認する必要があります。 それらが分岐する場合、これはバグとして扱われるべきです。これはサーバーレンダリングの利点を無効にするからです。 いずれかの要素が発散する場合、 suppressHydrationWarning={true}



追加suppressHydrationWarning={true}









さらに、注意点が1つあります。サーバーにはwindow



がありません。 それにアクセスするコードは、サーバー側で呼び出されないライフサイクルメソッドで実行する必要があります。 つまり、 UNSAFE_componentWillMount()で window



を使用することはできません。また、フックの場合はuselayouteffectを使用することもできません。







本質的に、サーバー側のレンダリングプロセスは、バックエンドからinitialStateを取得し、 renderToString()



を介して実行し、完成したinitialStateとHTMLを出力としてrenderToString()



し、クライアントに送信することです。







hh.ruでは、クライアントJSからの旅行は、PythonのAPIゲートウェイでのみ許可されます。 これは、安全性と負荷分散のためです。 Pythonは既にデータに必要なバックエンドに行き、それを準備してブラウザに渡します。 Node.jsはサーバーレンダリングにのみ使用されます。 したがって、データを準備した後、Pythonはノードへの追加の旅行を必要とし、結果を待ってクライアントに応答を送信します。







最初に、HTTPで動作するサーバーを選択する必要がありました。 私たちはコアに立ち寄った。 await



た最新の構文がawait



。 モジュール性は軽量のミドルウェアであり、必要に応じて個別にインストールするか、簡単に独立して作成できます。 サーバー自体は軽量で高速です。 はい、コアと同じ開発チームが速達で書いたのと同じように書かれており、彼らの経験は魅了されます。







サービスの展開方法を学んだ後、KOAで最も簡単なコードを作成し、200を提供することができ、それを製品にアップロードしました。 このように見えた:







 const Koa = require('koa'); const app = new Koa(); const SERVER_PORT = 9400; app.use(async (ctx) => { ctx.body = 'Hello World'; }); app.listen(SERVER_PORT);
      
      





hh.ruでは、すべてのサービスはdockerコンテナーにあります。 最初のリリースの前に、 Ansible Playbookを作成する必要があります。これを使用して、本番環境およびテストスタンドでサービスを展開します。 開発者とテスターはそれぞれ、prodと可能な限り類似した独自のテスト環境を持っています。 ほとんどの時間とエネルギーをプレイブックの作成に費やしました。 これは、2つのフロントエンドレンダリングがこれを実行したという事実が原因で発生しました。これは、hh.ruのノードでの最初のサービスです。 レンダリングが行われているサービスと並行してこれを行うには、サービスを開発モードに切り替える方法を把握する必要がありました。 ファイルをコンテナに配信します。 ビルドを待たずにドッカーコンテナが起動するように、ベアサーバーを起動します。 サーバーとそれを使用するサービスを構築および再構築します。 必要なRAMの量を決定します。







開発モードでは、最終ビルドに含まれるファイルを変更するときに、自動再構築とそれに続くサービスの再起動の可能性を提供しました。 ノードは、実行可能コードをロードするために再起動する必要があります。 Webpackは変更とビルドを監視します 。 ESMを一般的なCommonJSに変換するには、Webpackが必要です。 再起動のために、彼らはnodemonを使用しました。これは、収集されたファイルを管理します。







次に、ルーティングサーバーについて説明しました。 適切なバランスをとるには、どのサーバーインスタンスが生きているかを知る必要があります。 これを確認するために、動作中のハートビートは数秒ごとに/status



なり、応答で200を受信することを期待しています。 サーバーが構成で指定された回数を超えて応答しない場合、サーバーはバランシングから削除されます。 これは簡単なタスクであることが判明し、数行とルーティングの準備ができました。







 export default async function(ctx, next) { if (routeMap[ctx.request.path]) { routeMap[ctx.request.path](ctx); } else { ctx.throw(NOT_FOUND, getStatusText(NOT_FOUND)); } next(); }
      
      





そして、正しいURLで200と答えます。







 export default (ctx) => { ctx.status = 200; ctx.body = '200'; };
      
      





その後、プリミティブサーバーを作成し、 <script>



状態を返し、HTMLを準備しました。







サーバーの動作を制御する必要がありました。 これを行うには、ログと監視を固定する必要があります。 ログはJSONで記述されていませんが、主にJavaの他のサービスのログ形式をサポートします。 Log4jsベンチマークによって選択されました-それは高速で、設定が簡単で、必要な形式で書き込みます。 監視サポートを簡素化するには、共通のログ形式が必要です。ログを解析するために余分な正規表現を記述する必要はありません。 ログに加えて、引き続きsentryにエラーを書き込みます。 ロガーのコードは提供しませんが、基本的に設定は非常に簡単です。







その後、正常なシャットダウンを提供する必要がありました-サーバーが病気になったとき、またはリリースがロールしたとき、サーバーはそれ以上の着信接続を受け入れず、すべての要求を実行します。 ノードには多くの既製のソリューションがあります。 彼らはhttp- gracefulShutdown(app.listen(SERVER_PORT))



-shutdownを取りました。やらなければならないことは、 gracefulShutdown(app.listen(SERVER_PORT))



呼び出しをラップすることだけでしたgracefulShutdown(app.listen(SERVER_PORT))









この時点で、本番環境向けのソリューションが得られました。 どのように機能するかを確認するために、1ページで5%のユーザーのサーバーレンダリングをオンにしました。 メトリックを調べたところ、携帯電話のFMPが大幅に改善されたことがわかりました。デスクトップの場合、値は変更されていません。 彼らはパフォーマンスのテストを開始し、1台のサーバーが〜20 RPSを保持していることがわかりました(この事実はJavistsにとって非常に面白かったです)。 これがそうである理由を見つけました:









 if (cluster.isMaster) { cluster.on('exit', (worker, exitCode) => { if (exitCode !== SUCCESS) { cluster.fork(); } }); for (let i = 0; i < serverConfig.cpuCores; i++) { cluster.fork(); } } else { runApp(); }
      
      





このコードでは、マスタープロセスが開始され、サーバーに割り当てられたCPUの数に従ってプロセスが開始されます。 子プロセスの1つがクラッシュした場合-終了コードが0



はない(サーバーをオフにしている)場合、マスタープロセスはそれを再起動します。

また、サーバーに割り当てられたCPUの数ほどパフォーマンスが向上します。







上で書いたように、ほとんどの時間はオリジナルのプレイブックの作成に費やされました-約3週間。 SSR全体を書くのに約2週間かかり、約1か月かけてゆっくりと思いつきました。 これはすべて、エンタープライズエクスペリエンスノードjsなしで、2つの戦線の力によって行われました。 最も重要なことは、SSRを行うことを恐れないでくださいNODE_ENV=production



を指定することを忘れないでください、それについて複雑なことは何もありません。 ユーザーとSEOに感謝します。








All Articles