開発者ドキュメント

Ubuntu16.04でPM2とNginxを使用してNode.jsTCPサーバーアプリケーションを開発する方法

序章

Node.js は、ChromeのV8Javascriptエンジン上に構築された人気のあるオープンソースのJavaScriptランタイム環境です。 Node.jsは、サーバー側およびネットワーキングアプリケーションの構築に使用されます。 TCP(伝送制御プロトコル)は、アプリケーション間でデータストリームの信頼性が高く、順序付けられ、エラーチェックされた配信を提供するネットワーキングプロトコルです。 TCPサーバーはTCP接続要求を受け入れることができ、接続が確立されると、両側でデータストリームを交換できます。

このチュートリアルでは、サーバーをテストするためのクライアントとともに、基本的なNode.jsTCPサーバーを構築します。 PM2 と呼ばれる強力なNode.jsプロセスマネージャーを使用して、サーバーをバックグラウンドプロセスとして実行します。 次に、 Nginx をTCPアプリケーションのリバースプロキシとして構成し、ローカルマシンからのクライアント/サーバー接続をテストします。

前提条件

このチュートリアルを完了するには、次のものが必要です。

ステップ1—Node.jsTCPアプリケーションを作成する

TCPソケットを使用してNode.jsアプリケーションを作成します。 これは、Node.jsの Net ライブラリを理解するのに役立つサンプルアプリケーションであり、生のTCPサーバーおよびクライアントアプリケーションを作成できます。

まず、Node.jsアプリケーションを配置するディレクトリをサーバー上に作成します。 このチュートリアルでは、アプリケーションを作成します ~/tcp-nodejs-app ディレクトリ:

  1. mkdir ~/tcp-nodejs-app

次に、新しいディレクトリに切り替えます。

  1. cd ~/tcp-nodejs-app

名前の付いた新しいファイルを作成します package.json あなたのプロジェクトのために。 このファイルには、アプリケーションが依存するパッケージがリストされています。 このファイルを作成すると、この依存関係のリストを他の開発者と共有しやすくなるため、ビルドの再現性が高まります。

  1. nano package.json

を生成することもできます package.json を使用して npm init コマンド。アプリケーションの詳細を入力するように求められますが、スタートアップコマンドなど、ファイルを手動で変更して追加する必要があります。 したがって、このチュートリアルではファイルを手動で作成します。

次のJSONをファイルに追加します。これは、アプリケーションの名前、バージョン、メインファイル、アプリケーションを起動するコマンド、およびソフトウェアライセンスを指定します。

package.json
{
  "name": "tcp-nodejs-app",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "license": "MIT"
}

The scripts フィールドでは、アプリケーションのコマンドを定義できます。 ここで指定した設定により、を実行してアプリを実行できます npm start 走る代わりに node server.js.

The package.json ファイルには、ランタイムと開発の依存関係のリストを含めることもできますが、このアプリケーションのサードパーティの依存関係はありません。

これで、プロジェクトディレクトリと package.json セットアップして、サーバーを作成しましょう。

アプリケーションディレクトリで、 server.js ファイル:

  1. nano server.js

Node.jsはと呼ばれるモジュールを提供します net これにより、TCPサーバーとクライアントの通信が可能になります。 をロードします net モジュール付き require()次に、サーバーのポートとホストを保持する変数を定義します。

server.js
const net = require('net');
const port = 7070;
const host = '127.0.0.1';

ポートを使用します 7070 このアプリの場合ですが、使用可能な任意のポートを使用できます。 使用しています 127.0.0.1 のために HOST これにより、サーバーがローカルネットワークインターフェイスでのみリッスンするようになります。 後で、リバースプロキシとしてこのアプリの前にNginxを配置します。 Nginxは、複数の接続と水平スケーリングの処理に精通しています。

次に、このコードを追加して、 createServer() からの機能 net モジュール。 次に、を使用して定義したポートとホストで接続のリッスンを開始します。 listen() の機能 net モジュール:

server.js
...
const server = net.createServer();
server.listen(port, host, () => {
    console.log('TCP Server is running on port ' + port +'.');
});

保存 server.js サーバーを起動します。

  1. npm start

次の出力が表示されます。

Output
TCP Server is running on port 7070

TCPサーバーはポートで実行されています 7070. プレス CTRL+C サーバーを停止します。

サーバーがリッスンしていることがわかったので、クライアント接続を処理するコードを記述しましょう。

クライアントがサーバーに接続すると、サーバーは connection イベント、私たちが観察します。 接続されたクライアントの配列を定義します。これを呼び出します sockets、およびクライアントが接続するときに、各クライアントインスタンスをこのアレイに追加します。

を使用します data 接続されたクライアントからのデータストリームを処理するイベント。 sockets 接続されているすべてのクライアントにデータをブロードキャストするための配列。

このコードをに追加します server.js これらの機能を実装するためのファイル:

server.js

...

let sockets = [];

server.on('connection', function(sock) {
    console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
    sockets.push(sock);

    sock.on('data', function(data) {
        console.log('DATA ' + sock.remoteAddress + ': ' + data);
        // Write the data back to all the connected, the client will receive it as data from the server
        sockets.forEach(function(sock, index, array) {
            sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + '\n');
        });
    });
});

これはサーバーにリッスンするように指示します data 接続されたクライアントによって送信されたイベント。 接続されたクライアントがサーバーにデータを送信すると、接続されているすべてのクライアントにデータをエコーバックします。 sockets 配列。

次に、のハンドラーを追加します close 接続されたクライアントが接続を終了したときにトリガーされるイベント。 クライアントが切断するたびに、クライアントをから削除する必要があります sockets アレイなので、ブロードキャストしなくなります。 接続ブロックの最後に次のコードを追加します。

server.js

let sockets = [];
server.on('connection', function(sock) {

    ...

    // Add a 'close' event handler to this instance of socket
    sock.on('close', function(data) {
        let index = sockets.findIndex(function(o) {
            return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
        })
        if (index !== -1) sockets.splice(index, 1);
        console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
    });
});

これがの完全なコードです server.js:

server.js
const net = require('net');
const port = 7070;
const host = '127.0.0.1';

const server = net.createServer();
server.listen(port, host, () => {
    console.log('TCP Server is running on port ' + port + '.');
});

let sockets = [];

server.on('connection', function(sock) {
    console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
    sockets.push(sock);

    sock.on('data', function(data) {
        console.log('DATA ' + sock.remoteAddress + ': ' + data);
        // Write the data back to all the connected, the client will receive it as data from the server
        sockets.forEach(function(sock, index, array) {
            sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + '\n');
        });
    });

    // Add a 'close' event handler to this instance of socket
    sock.on('close', function(data) {
        let index = sockets.findIndex(function(o) {
            return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
        })
        if (index !== -1) sockets.splice(index, 1);
        console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
    });
});

ファイルを保存してから、サーバーを再起動します。

  1. npm start

マシン上で完全に機能するTCPサーバーが実行されています。 次に、サーバーに接続するクライアントを作成します。

ステップ2—Node.jsTCPクライアントを作成する

Node.js TCPサーバーが実行されているので、サーバーに接続してサーバーをテストするためのTCPクライアントを作成しましょう。

作成したNode.jsサーバーはまだ実行中であり、現在のターミナルセッションをブロックしています。 クライアントを開発する間、それを実行し続けたいので、新しいターミナルウィンドウまたはタブを開きます。 次に、新しいタブからサーバーに再度接続します。

  1. ssh sammy@your_server_ip

接続したら、に移動します tcp-nodejs-app ディレクトリ:

  1. cd tcp-nodejs-app

同じディレクトリに、という新しいファイルを作成します client.js:

  1. nano client.js

クライアントは同じものを使用します net で使用されるライブラリ server.js TCPサーバーに接続するファイル。 このコードをファイルに追加して、IPアドレスを使用してサーバーに接続します 127.0.0.1 ポートで 7070:

client.js
const net = require('net');
const client = new net.Socket();
const port = 7070;
const host = '127.0.0.1';

client.connect(port, host, function() {
    console.log('Connected');
    client.write("Hello From Client " + client.address().address);
});

このコードは、最初にTCPサーバーへの接続を試みて、作成したサーバーが実行されていることを確認します。 接続が確立されると、クライアントは送信します "Hello From Client " + client.address().address を使用してサーバーに client.write 関数。 サーバーはこのデータを受信し、クライアントにエコーバックします。

クライアントがサーバーからデータを受信したら、サーバーの応答を出力する必要があります。 このコードを追加して、 data イベントを実行し、コマンドラインに対するサーバーの応答を出力します。

client.js
client.on('data', function(data) {
    console.log('Server Says : ' + data);
});

最後に、次のコードを追加して、サーバーからの切断を適切に処理します。

client.js
client.on('close', function() {
    console.log('Connection closed');
});

を助けて client.js ファイル。

次のコマンドを実行して、クライアントを起動します。

  1. node client.js

接続が確立され、サーバーがデータを受信して、クライアントにエコーバックします。

client.js Output
Connected Server Says : 127.0.0.1:34548 said Hello From Client 127.0.0.1

サーバーが実行されているターミナルに戻ると、次の出力が表示されます。

server.js Output
CONNECTED: 127.0.0.1:34550 DATA 127.0.0.1: Hello From Client 127.0.0.1

サーバーとクライアントアプリの間にTCP接続を確立できることを確認しました。

プレス CTRL+C サーバーを停止します。 次に、他のターミナルセッションに切り替えて、を押します。 CTRL+C クライアントを停止します。 これで、このターミナルセッションをサーバーから切断して、元のターミナルセッションに戻ることができます。

次のステップでは、PM2を使用してサーバーを起動し、バックグラウンドで実行します。

ステップ3—PM2でサーバーを実行する

クライアント接続を受け入れる稼働中のサーバーがありますが、フォアグラウンドで実行されています。 PM2を使用してサーバーを実行し、バックグランドで実行され、正常に再起動できるようにします。

まず、を使用してサーバーにPM2をグローバルにインストールします npm:

  1. sudo npm install pm2 -g

PM2をインストールしたら、それを使用してサーバーを実行します。 走る代わりに npm start サーバーを起動するには、 pm2 指図。 サーバーを起動します。

  1. pm2 start server.js

次のような出力が表示されます。

[secondary_label Output
[PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/sammy/tcp-nodejs-app/server.js in fork_mode (1 instance)
[PM2] Done.
┌────────┬──────┬────────┬───┬─────┬───────────┐
│ Name   │ mode │ status │ ↺ │ cpu │ memory    │
├────────┼──────┼────────┼───┼─────┼───────────┤
│ server │ fork │ online │ 0 │ 5%  │ 24.8 MB   │
└────────┴──────┴────────┴───┴─────┴───────────┘
 Use `pm2 show <id|name>` to get more details about an app

これで、サーバーはバックグラウンドで実行されます。 ただし、マシンを再起動すると、マシンは実行されなくなるので、そのためのsystemdサービスを作成しましょう。

次のコマンドを実行して、PM2のsystemdスタートアップスクリプトを生成してインストールします。 必ずこれを実行してください sudo そのため、systemdファイルは自動的にインストールされます。

  1. sudo pm2 startup

次の出力が表示されます。

Output
[PM2] Init System found: systemd Platform systemd ... [PM2] Writing init configuration in /etc/systemd/system/pm2-root.service [PM2] Making script booting at startup... [PM2] [-] Executing: systemctl enable pm2-root... Created symlink from /etc/systemd/system/multi-user.target.wants/pm2-root.service to /etc/systemd/system/pm2-root.service. [PM2] [v] Command successfully executed. +---------------------------------------+ [PM2] Freeze a process list on reboot via: $ pm2 save [PM2] Remove init script via: $ pm2 unstartup systemd

PM2は現在systemdサービスとして実行されています。

PM2が管理しているすべてのプロセスを一覧表示できます。 pm2 list 指図:

  1. pm2 list

リストにアプリケーションが表示され、IDは次のようになります。 0:

Output
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤ │ server │ 0 │ fork │ 9075 │ online │ 0 │ 4m │ 0% │ 30.5 MB │ sammy │ disabled │ └──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘

前の出力では、次のことに気付くでしょう。 watching 無効になっています。 これは、アプリケーションファイルに変更を加えたときにサーバーをリロードする機能です。 開発には役立ちますが、本番環境ではその機能は必要ありません。

実行中のプロセスに関する詳細情報を取得するには、 pm2 show コマンドの後にIDが続きます。 この場合、IDは 0:

  1. pm2 show 0

この出力には、稼働時間、ステータス、ログファイルパス、および実行中のアプリケーションに関するその他の情報が表示されます。

Output
Describing process with id 0 - name server ┌───────────────────┬──────────────────────────────────────────┐ │ status │ online │ │ name │ server │ │ restarts │ 0 │ │ uptime │ 7m │ │ script path │ /home/sammy/tcp-nodejs-app/server.js │ │ script args │ N/A │ │ error log path │ /home/sammy/.pm2/logs/server-error-0.log │ │ out log path │ /home/sammy/.pm2/logs/server-out-0.log │ │ pid path │ /home/sammy/.pm2/pids/server-0.pid │ │ interpreter │ node │ │ interpreter args │ N/A │ │ script id │ 0 │ │ exec cwd │ /home/sammy/tcp-nodejs-app │ │ exec mode │ fork_mode │ │ node.js version │ 8.11.2 │ │ watch & reload │ ✘ │ │ unstable restarts │ 0 │ │ created at │ 2018-05-30T19:29:45.765Z │ └───────────────────┴──────────────────────────────────────────┘ Code metrics value ┌─────────────────┬────────┐ │ Loop delay │ 1.12ms │ │ Active requests │ 0 │ │ Active handles │ 3 │ └─────────────────┴────────┘ Add your own code metrics: http://bit.ly/code-metrics Use `pm2 logs server [--lines 1000]` to display logs Use `pm2 monit` to monitor CPU and Memory usage server

アプリケーションのステータスにエラーが表示された場合は、エラーログパスを使用してエラーログを開き、確認してエラーをデバッグできます。

  1. cat /home/tcp/.pm2/logs/server-error-0.log

サーバーコードに変更を加えた場合は、次のように、アプリケーションのプロセスを再起動して変更を適用する必要があります。

  1. pm2 restart 0

PM2は現在アプリケーションを管理しています。 次に、Nginxを使用してリクエストをサーバーにプロキシします。

ステップ4—Nginxをリバースプロキシサーバーとして設定する

アプリケーションが実行され、リッスンしています 127.0.0.1、これは、ローカルマシンからの接続のみを受け入れることを意味します。 Nginxをリバースプロキシとして設定し、着信トラフィックを処理してサーバーに転送します。

これを行うには、Nginxの stream{}およびstream_proxy機能を使用してTCP接続をNode.jsサーバーに転送するように、Nginx構成を変更します。

メインのNginx構成ファイルを次のように編集する必要があります stream TCP接続転送を構成するブロックは、最上位ブロックとしてのみ機能します。 UbuntuのデフォルトのNginx構成は、サーバーブロックを http ファイルのブロック、および stream ブロックをそのブロック内に配置することはできません。

ファイルを開く /etc/nginx/nginx.conf エディターで:

  1. sudo nano /etc/nginx/nginx.conf

構成ファイルの最後に次の行を追加します。

/etc/nginx/nginx.conf

...

stream {
    server {
      listen 3000;
      proxy_pass 127.0.0.1:7070;        
      proxy_protocol on;
    }
}

これは、ポートでTCP接続をリッスンします 3000 ポートで実行されているNode.jsサーバーにリクエストをプロキシします 7070. アプリケーションが別のポートでリッスンするように設定されている場合は、プロキシパスのURLポートを正しいポート番号に更新します。 The proxy_protocol ディレクティブは、 PROXYプロトコルを使用してクライアント情報をバックエンドサーバーに送信するようにNginxに指示します。バックエンドサーバーは、必要に応じてその情報を処理できます。

ファイルを保存して、エディターを終了します。

Nginxの構成をチェックして、構文エラーが発生していないことを確認します。

  1. sudo nginx -t

次に、Nginxを再起動して、TCPおよびUDPプロキシ機能を有効にします。

  1. sudo systemctl restart nginx

次に、そのポートでサーバーへのTCP接続を許可します。 使用する ufw ポートでの接続を許可する 3000:

  1. sudo sudo ufw allow 3000

Node.jsアプリケーションが実行されていて、アプリケーションとNginxの構成が正しいと仮定すると、Nginxリバースプロキシを介してアプリケーションにアクセスできるようになります。

ステップ5—クライアント/サーバー接続のテスト

ローカルマシンからTCPサーバーに接続して、サーバーをテストしてみましょう。 client.js 脚本。 そのためには、をダウンロードする必要があります client.js ローカルマシンに開発したファイルを作成し、スクリプトのポートとIPアドレスを変更します。

まず、ローカルマシンで、 client.js を使用してファイル scp:

  1. [environment local
  2. scp sammy@your_server_ip:~/tcp-nodejs-app/client.js client.js

を開きます client.js エディター内のファイル:

  1. [environment local
  2. nano client.js

変更 port3000 を変更します host サーバーのIPアドレスに:

client.js
// A Client Example to connect to the Node.js TCP Server
const net = require('net');
const client = new net.Socket();
const port = 3000;
const host = 'your_server_ip';
...

ファイルを保存し、エディターを終了し、クライアントを実行してテストします。

  1. node client.js

以前に実行したときと同じ出力が表示されます。これは、クライアントマシンがNginxを介して接続し、サーバーに到達したことを示しています。

client.js Output
Connected Server Says : 127.0.0.1:34584 said PROXY TCP4 your_local_ip_address your_server_ip 52920 3000 Hello From Client your_local_ip_address

Nginxはサーバーへのクライアント接続をプロキシしているため、Node.jsサーバーはクライアントの実際のIPアドレスを認識しません。 NginxのIPアドレスのみが表示されます。 Nginxは、セキュリティに影響を与える可能性のあるシステムに変更を加えずに、実際のIPアドレスをバックエンドに直接送信することをサポートしていませんが、NginxでPROXYプロトコルを有効にしたため、Node.jsサーバーは追加の PROXY 実際のIPを含むメッセージ。 そのIPアドレスが必要な場合は、サーバーを調整して処理することができます PROXY 要求し、必要なデータを解析します。

これで、Node.js TCPアプリケーションがNginxリバースプロキシの背後で実行され、サーバーの開発をさらに進めることができます。

結論

このチュートリアルでは、Node.jsを使用してTCPアプリケーションを作成し、PM2を使用して実行し、Nginxの背後で提供しました。 また、他のマシンから接続するためのクライアントアプリケーションを作成しました。 このアプリケーションを使用して、データストリームの大きなチャンクを処理したり、リアルタイムメッセージングアプリケーションを構築したりできます。

モバイルバージョンを終了