昨日、友人が Express プロジェクトの展開方法を尋ねました。そこで、困っている友人の参考のために、主にnodejsに基づいて開発されたサーバープログラムをデプロイする方法について説明するこの記事をまとめました。
この記事にはいくつかの部分が含まれています:
プロセス (プロセス) はコンピューターですオペレーティング システムの割り当てとタスクのスケジューリングの基本単位。タスク マネージャーを開くと、実際にはコンピューターのバックグラウンドで多数のプログラムが実行されており、それぞれのプログラムがプロセスであることがわかります。
最近のブラウザは基本的にマルチプロセスアーキテクチャを採用しています。Chromeブラウザを例に挙げると、「その他のツール」-「タスクマネージャー」を開くと、現在のブラウザのプロセス情報が表示されます。その他、ネットワークプロセス、GPUプロセスなどがあります。
マルチプロセス アーキテクチャにより、アプリケーションのより安定した動作が保証されます。ブラウザを例に挙げると、すべてのプログラムが 1 つのプロセスで実行されている場合、ネットワーク障害やページ レンダリング エラーが発生すると、ブラウザ全体がクラッシュします。マルチプロセスアーキテクチャにより、ネットワークプロセスがクラッシュしても既存のページの表示には影響せず、最悪の場合は一時的にネットワークにアクセスできなくなる。
スレッドは、オペレーティング システムがコンピューティング スケジューリングを実行できる最小単位です。これはプロセスに含まれており、プロセス内の実際の操作単位となります。たとえば、プログラムは複数の部門を持つ企業のようなものであり、各部門の協力により、会社は正常に運営されています。
JavaScript がシングルスレッド言語であることは誰もが知っています。このデザインは、初期の時代にJSが主にスクリプトの作成に使用され、ページのインタラクティブな効果を実現する責任があったためです。それがマルチスレッド言語として設計されている場合、第一にそれは必要ありません、そして第二に、複数のスレッドがDOMノードを共同で操作し、ブラウザが誰のアドバイスを聞くべきですか?もちろん、テクノロジーの発展に伴い、JS もマルチスレッドをサポートするようになりましたが、それは DOM 操作に関係のない一部のロジックを処理するためにのみ使用されます。
単一スレッドと単一プロセスは、実行中の Node.js プログラムのメイン スレッドがハングアップすると、プロセスもハングアップし、アプリケーション全体もハングアップします。さらに、最新のコンピューターのほとんどは、4 コアと 8 スレッド、および 8 コアと 16 スレッドのマルチコア CPU を備えており、これらは非常に一般的なデバイスです。単一プロセス プログラムとして、node.js はマルチコア CPU のパフォーマンスを無駄にします。
この状況に応じて、単一プロセスnode.jsプログラムをマルチプロセスアーキテクチャに変換するために、適切なマルチプロセスモデルが必要です。
node.jsにマルチプロセスアーキテクチャを実装するための2つの一般的なソリューションがあります。どちらも、子どものモジュール、つまりchild_process
モジュールとcluster
モジュールを使用します。
child_process
は、node.js の組み込みモジュールです。名前から、子プロセスに関連する処理を担当していることが推測できます。
このモジュールの具体的な使用法については詳しく説明しませんが、実際には 6 ~ 7 個のメソッドしかありませんが、それでも非常に理解しやすいものです。 fork
メソッドの 1 つを使用して、複数のプロセスと複数のプロセス間の通信を実装する方法を示します。
まず、準備されたデモ ケースのディレクトリ構造を見てみましょう。
http
モジュールを使用して http サーバーを作成します。 /sum
リクエストが受信されると、 child_process
モジュールを通じて子プロセスが作成され、同時に親プロセスに計算ロジックを実行するよう通知されます。子プロセスによって送信されたメッセージもリッスンする必要があります:
/ /child_process.js const http = require('http') const { fork } = require('child_process') const サーバー = http.createServer((req, res) => { if (req.url == '/sum') { // fork メソッドはモジュール パスを受け取り、子プロセスを開始し、子プロセス内でモジュールを実行します。 // childProcess は作成された子プロセスを表します let childProcess = fork('./sum.js') //子プロセスにメッセージを送信 childProcess.send('子プロセスが計算を開始します') // 親プロセスで子プロセスのメッセージを監視 childProcess.on('message', (data) => { res.end(データ + '') }) // Child Process ChildProcess.on( 'close'、()=> {の閉鎖イベントを聞く // 子プロセスが正常に終了するか、エラーを報告してハングアップした場合、ここに記録されます console.log('child process closes') childProcess.kill() }) //子プロセスのエラーイベントをリッスン childProcess.on('error', () => { console.log('子プロセスエラー') childProcess.kill() }) } if (req.url == '/hello') { res.end('こんにちは') } // 親プロセスをシミュレートしてエラーを報告する if (req.url == '/error') { throw new Error('親プロセスエラー') res.end('こんにちは') } }) サーバー.listen(3000, () => { console.log('サーバーは 3000 で実行中') })
sum.js
は、子プロセスによって実行されるタスクをシミュレートするために使用されます。子プロセスは、親プロセスによって送信されたメッセージをリッスンし、計算タスクを処理して、結果を親プロセスに送信します。
// sum.js 関数 getSum() { 合計 = 0 とします for (i = 0; i < 10000 * 1000 * 100; i++) { 合計 += 1 } 返還額 } //プロセスは、Node.jsのグローバルオブジェクトであり、現在のプロセスを表します。ここが子プロセスです。 // メインプロセスによって送信されたメッセージをリッスンします process.on('message', (data) => { console.log('メインプロセスからのメッセージ:', data) const 結果 = getSum() //計算結果を親プロセスに送信 process.send(result) })
ターミナルを開き、コマンドnode 1.child_process
を実行します。
ブラウザにアクセスします:
次に、子のプロセスがエラーを報告する状況をシミュレートします:
// sum.js 関数 getSum() { // .... } // 子プロセスが 5 秒間実行された後、シミュレーション プロセスがハングアップします setTimeout(() => { throw new Error('エラーレポート') }、1000 * 5) process.on('メッセージ', (データ) => { // ... })
ブラウザに再度アクセスし、5 秒後にコンソールを観察します。
子プロセスが終了し、別の URL: /hello
にアクセスします。
親プロセスはリクエストを正しく処理できることがわかります。これは、子プロセスによって報告されたエラーが親プロセスの操作に影響しないことを示しています。
次に、親プロセスがエラーを報告するシナリオをシミュレートし、 sum.js
モジュールのシミュレートされたエラー レポートをコメント アウトして、サービスを再起動し、ブラウザで/error
アクセスします。
親プロセスが切れたことを発見した後、node.jsプログラム全体が自動的に終了し、サービスが完全に崩壊し、回復の余地がありませんでした。
child_process
のfork
方法を介してnode.jsのマルチプロセスアーキテクチャを実装することは複雑ではないことがわかります。このネーミングから、プロセス間通信は主にsend
とon
メソッドを介して行われ、最下層はパブリッシュ/サブスクライブ モデルである必要があることもわかります。
しかし、それは深刻な問題を抱えています。子のプロセスは親のプロセスに影響しませんが、親のプロセスがエラーを発揮して電話を切ると、すべての子プロセスが「1つのポットで殺されます」。したがって、このソリューションは、複雑で時間のかかる操作を別のサブプロセスに分岐するのに適しています。より正確に言えば、この使用法はマルチ処理ではなく、マルチスレッドの実装を置き換えるために使用されます。
、 child_process
モジュールを使用してマルチプロセスを実装しますが、これは役に立たないようです。したがって、一般に、 cluster
モジュールを使用して、node.jsのマルチプロセスモデルを実装することをお勧めします。
cluster
クラスターを意味します。たとえば、過去には、会社にはフロントデスクが1つしかありませんでしたが、時には忙しすぎて訪問者を時間内に受け入れることができませんでした。現在、会社は4つのフロントデスクを割り当てています。クラスタリングとは、大まかに言うと、同じことを最適に実行できるように、それを実行するために合理的に異なる人が割り当てられることを意味します。
cluster
モジュールの使用も比較的簡単です。現在のプロセスがメイン プロセスの場合、CPU コアの数に基づいて適切な数のサブプロセスを作成し、サブプロセスのexit
イベントをリッスンします。サブプロセスが終了した場合は、新しいサブプロセスを再フォークします。 -プロセス。子どものプロセスでない場合、実際のビジネスは処理されます。
const http = require('http') const クラスター = require('クラスター') const cpus = require('os').cpus() if(cluster.ismaster){ // プログラムが開始されると、まずここに進み、CPU コアの数に応じて複数のサブプロセスを作成します (let i = 0; i < cpus.length; i++) { //子プロセスを作成するcluster.fork() } //チャイルドプロセスが切れると、クラスターモジュールは「Exit」イベントを放出します。この時点で、再度 fork を呼び出すことでプロセスが再開されます。 クラスタ.on('終了', () => { cluster.fork() }) } それ以外 { // fork メソッドが実行されて子プロセスが作成され、モジュールが再度実行されます。このとき、ロジックがここに来ます。 const server = http.createServer((req, res) => { console.log(process.pid) res.End( 'ok') }) server.listen(3000、()=> { console.log( 'サーバーは3000'で実行されています '、' pid: ' + process.pid) }) }
サービスを開始します。
ご覧のとおり、 cluster
モジュールは多くの子プロセスを作成しており、各子プロセスは同じWebサービスを実行しているようです。
現時点では、これらの子プロセスは同じポートをリッスンしていないことに注意してください。 createServerメソッドによって作成されたサーバーは、各子プロセスにポート監視を担当し、リクエストを転送します。
上記のサービスをリクエストするリクエスト スクリプトを作成して、その効果を確認してみましょう。
// リクエスト.js const http = require('http') for (i = 0; i < 1000; i++) { http.get('http://localhost:3000') }
HTTPモジュールは、HTTPサーバーを作成するだけでなく、HTTPリクエストの送信にも使用できます。 Axiosは、ブラウザとサーバー環境をサポートしています。HTTPモジュールは、HTTPリクエストを送信します。
node
コマンドを使用してファイルを実行し、元のコンソールを確認します。
リクエストを特異的に処理するさまざまなサブプロセスのプロセスIDが印刷されます。
これは、 cluster
モジュールを介して実装されたnodd.jsのマルチプロセスアーキテクチャです。
もちろん、node.jsプロジェクトを展開する場合、 cluster
モジュールをそれほど乾燥させたり使用したりしません。 PM2と呼ばれる非常に便利なツールがあります。これは、クラスターモジュールに基づいたプロセス管理ツールです。その基本的な使用法は、後続の章で紹介されます。
これまでのところ、 node.jsでマルチプロセスの知識を紹介する記事の一部を費やしました。この記事のスペースが限られており、正確/詳細な説明がないため、この記事は簡単な紹介のみを示しています。このコンテンツに初めて接触した場合は、それをよく理解していないかもしれませんので、心配しないでください。後で詳細な記事があります。
この記事は、Expressを使用して開発されたサンプルプログラムを準備しました。アクセスするにはここをクリックしてください。
主にインターフェイスサービスを実装します/api/users
、10個のユーザーデータをmockjs
トし、ユーザーリストを返すために使用されます。同時に、エラーの状況をシミュレートするためにタイマーが開始されます:
const express = reques( 'express') const mock = require( 'mockjs') const app = Express() app.get( "/api/users"、(req、res)=> { const userList = Mock.mock({ 'userList|10': [{ 'id|+1': 1、 'name': '@cname'、 '電子メール': '@電子メール' }] }) setTimeout(()=> { throw new Error('サーバー障害') }、5000) Res.Status(200) Res.Json(userlist) }) app.listen(3000, () => { console.log("サービスが開始されました: 3000") })
それをローカルでテストし、端末でコマンドを実行します:
node server.js
ブラウザを開き、ユーザーリストインターフェイスにアクセスします。
5 秒後にサーバーがハングします。
PM2を使用してアプリケーションを管理するときに、この問題を後で解決できます。
ディスカッション:Expressプロジェクトは、通常、Vue/Reactプロジェクトを完了した後、
実際、プログラムの最終的な実行環境はブラウザであり、ブラウザには次のようなさまざまな互換性の問題とパフォーマンスの問題があるため、フロントエンドのプロジェクトはパッケージ化する必要があります
.vue
.jsx
.ts
express.jsまたはkoa.jsを使用して開発されたプロジェクトはありません。これらの問題。さらに、 Node.js は CommonJS モジュール仕様を採用しており、同時にモジュールは使用時にのみインポートされます。ファイルにパッケージ化すると、この利点は実際に無駄になります。したがって、node.jsプロジェクトの場合、パッケージ化する必要はありません。
この記事では、CentOSシステムを例として使用して、
スイッチングノードバージョンを容易にするために、NVMを使用してノードを管理します。
NVM(ノードバージョンマネージャー)は、 node.jsのバージョン管理ツールです。それを通して、ノードを複数のバージョン間で任意に切り替えることができ、バージョンの切り替えが必要なときに繰り返しダウンロードおよびインストール操作を回避できます。
NVMの公式リポジトリはgithub.com/nvm-sh/nvmです。インストールスクリプトはgithubusercontent
サイトに保存されているため、多くの場合、アクセスできません。そこで、Giteeで新しいミラーリポジトリを作成して、Giteeからインストールスクリプトにアクセスできるようにしました。
curl
コマンドを介してインストールスクリプトをダウンロードし、 bash
を使用してスクリプトを実行します。スクリプトは、nvmのインストールを自動的に完了します
。
その後インストールが完了したら、NVMを使用する新しいウィンドウを開きます:
[
root@ecs-221238〜]#nvm -V0.39.1は、
バージョン番号を正常に印刷でき、NVMが正常にインストールされていることを示します。
、NVMを使用してノードをインストールおよび管理できます。
使用可能なノードバージョンの表示:
#nvm ls-remote
インストールノード:
#nvmインストール18.0.0
インストールされたノードバージョン:
[root@ecs-221238〜] #nvmリスト - > v18.0.0 デフォルト - > 18.0.0( - > v18.0.0) iojs -> N/A (デフォルト) 不安定 -> N/A (デフォルト) ノード -> 安定版 (-> v18.0.0) (デフォルト) stable-> 18.0( - > v18.0.0)(デフォルト)
使用
するバージョンを選択します
。 Centosでは、デフォルトでルートユーザーとしてログインするため、問題はありません。使用時に不明なエラーが発生した場合、ソリューションを検索するか、問題がアクセス許可によって引き起こされるかどうかを確認することができます。
ノードをインストールすると、NPMは自動的にインストールされます。ノードとNPMのバージョン番号を確認してください:
[root@ecs -221238〜] #node -v v18.0.0 [root@ecs -221238〜]#npm -v 8.6.0の
デフォルトNPM画像ソースは
公式アドレスです。
https://registry.npmjs.org/
国内のTaobaoミラーに切り替える出典:
[root@ecs-221238〜]#npm config setレジストリhttps://registry.npmmirror.com
この時点で、サーバーはノードをインストールしました。環境とNPMが構成されています。
、Github/GitLab/Gitee リポジトリからサーバーにダウンロードする方法や、ftp ツールを使用してローカルにアップロードする方法など、さまざまな方法があります。手順は非常に単純で、再び実証されません。
デモプロジェクトは/www
ディレクトリに配置されています。
一般的に、クラウドサーバーはリモートログイン用にポート22を開くだけです。 80や443などの一般的に使用されるポートは開いていません。さらに、準備したエクスプレスプロジェクトは、ポート3000で実行されます。したがって、まずクラウド サーバーのコンソールに移動し、セキュリティ グループを見つけて、いくつかのルールを追加し、ポート 80 と 3000 を開く必要があります。
、リアルタイム監視と自動再起動にnodemon
使用して、開発効率を改善できます。生産環境では、Big KillerであるPM2を使用する必要があります。
を
グローバルにインストールし
pm2 -v
。
-V5.2.0
プロジェクトディレクトリに切り替えて、依存関係を最初にインストールします:
CD /www /express-demo NPMインストールして
から、 pm2
コマンドを使用してアプリケーションを開始します。
PM2 app.jsを開始-i max //またはPM2 Start Server.js -I 2
PM2管理アプリケーションには、フォークとクラスターの2つのモードがあります。アプリケーションを開始するとき、-iパラメーターを使用してインスタンスの数を指定することにより、クラスターモードが自動的にオンになります。この時点で、負荷分散機能が利用可能です。
-I:インスタンス、インスタンスの数。特定の数字を作成するか、
PM2
自動的にチェックしてから、できるだけ多くのプロセスを開始できます。
アプリケーションが開始されました。 PM2は、デーモンプロセスの形式でアプリケーションを管理します。
ローカルブラウザでインターフェイスにアクセスします。
クラスターモードは、マルチプロセスおよびマルチインスタンスモデルです。 PM2の後見のために、以前に見たcluster
モジュールの使用法と同じように、プロセスが死んだとしても、プロセスはすぐに再起動されます。
サーバー端末に戻り、 pm2 logs
コマンドを実行してPM2ログを表示します。
ID 1のアプリケーションインスタンスがハングアップし、PM2がインスタンスをすぐに再起動することがわかります。ここのIDは、プロセスIDではなくアプリケーションインスタンスのIDであることに注意してください。
この時点で、Express プロジェクトの簡単なデプロイメントが完了しました。 PM2ツールを使用することにより、基本的にプロジェクトが安定して確実に実行できるようにすることができます。
ここには、参照用のPM2ツールの一般的に使用されるコマンドの要約があります。
#forkモードPM2 start app.js - 名前アプリ#アプリケーションの名前をアプリに設定します #Clusterモード#ロードバランシングを使用して4つのプロセスを開始するPM2 App.js -I 4を開始 #利用可能なCPUに応じて、ロードバランシングを使用して4つのプロセスを開始します PM2 App.jsを開始-I 0 #上記のコマンドPM2の効果に相当しますapp.js -i maxを開始 # 3 つの追加プロセスでアプリを拡張 pm2 スケール アプリ +3 #アプリを2つのプロセスに拡張または縮小するPM2スケールアプリ2 #アプリケーションステータスの表示#すべてのプロセスのステータスを表示PM2リスト #raw json形式のすべてのプロセスのリストを印刷しますpm2 jlist #美しいJSONを使用して、すべてのプロセスのリストを印刷するPM2 PrettyList #特定のプロセスに関するすべての情報を表示するPM2は0を説明します #ダッシュボードを使用してすべてのプロセスを監視するPM2モニット #log管理#すべてのアプリケーションログPM2ログをリアルタイムで表示する #リアルタイムのアプリアプリケーションログを表示するPM2ログアプリ #JSON形式を使用してログをリアルタイムで表示し、古いログを出力せず、新しく生成されたログPM2ログのみを出力します-JSON #Application Management#すべてのプロセスを停止するPM2すべてを停止します #すべてのプロセスを再起動PM2すべてを再起動します #指定されたIDPM2停止0でプロセスを停止します #指定されたID PM2を再起動してプロセスを再起動します0 #deleteプロセスPM2をid 0 delete 0で削除します #すべてのプロセスを削除するPM2すべての
コマンドを試して自分で試して、効果を確認します。
ここでは、 monit
コマンドの特別なデモを示します。このコマンドは、ターミナルでパネルを起動して、アプリケーションの実行ステータスをリアルタイムで表示できます。pm2 によって管理されるすべてのアプリケーションは、上矢印と下矢印を使用して切り替えることができます。
PM2には上記のコマンドよりもはるかに多く、非常に強力な機能があります。実際のプロジェクトの展開では、ログファイル、ウォッチモード、環境変数などを構成する必要があります。毎回手作業でコマンドを入力するのは非常に退屈なので、PM2はアプリケーションを管理および展開するための構成ファイルを提供します。
次のコマンドを介して構成ファイルを生成できます。
[root@ecs-221238 express-demo] file /www/express-demo/ecosystem.config.js生成
ecosystem.config.js
れます
。 アプリ:[{ 名前:「app1」、 スクリプト:「./App.js」 }] }app.config.js:
const path = require( 'path')
app.config.js
、構成ファイルを自分で作成することもできます。
module.exports = { // 1つの構成ファイルが複数のnode.jsアプリケーションを同時に管理できます//アプリは配列です。各アイテムはアプリケーションアプリの構成です。 //アプリケーション名:「Express-Demo」、 //アプリケーションエントリファイルスクリプト: "./server.js"、 //アプリケーションを開始するための2つのモードがあります。デフォルトはフォークです。 exec_mode: 'cluster'、 //インスタンスを作成するアプリケーションインスタンスの数: 'max'、 //ファイルが監視されたときに監視をオンにし、アプリケーションを自動的に再起動します:true、 //一部のディレクトリ ファイルへの変更を無視します。 //ログディレクトリはプロジェクトパスに配置されているため、アプリケーションが開始されるとログが再起動すると、ログが再起動されます。ループIngrore_watch:[ 「node_modules」、 「ログ」 ]、 //エラーログストレージパスerr_file:path.resolve(__ dirname、 'logs/error.log')、 //ログストレージパスを印刷OUT_FILE:PATH.RESOLVE(__ DIRNAME、 'logs/out.log')、 //ログファイルの各ログの前に日付形式を設定しますlog_date_format: "yyyy-mm-dd hh:mm:ss"、 }] }
PM2を使用してノードアプリケーションを管理するように構成ファイルを使用します
。PM2app.config.jsを開始すると
、PM2が管理するアプリケーションがプロジェクトディレクトリにログを配置し(デフォルトはPM2のインストールディレクトリにあります)、ファイルの変更を監視できます。 、自動的にサービスを再起動します。