高パフォーマンスの Web アプリケーションの実践
導入
fibjs は、主に Web バックエンド開発のために設計された高性能アプリケーション サーバー フレームワークです。これは Google v8 JavaScript エンジンに基づいて構築されており、従来のコールバックとは異なる同時実行ソリューションを選択します。fibjs はファイバーを使用して、フレームワーク層での非同期呼び出しによって引き起こされるビジネスの複雑さを分離し、開発の難易度を大幅に軽減し、ユーザー空間での頻繁な非同期処理によって引き起こされるパフォーマンスの問題を軽減します。
fibjs の設計はパフォーマンスに細心の注意を払っており、内蔵のネットワーク IO および HTTP モジュールはイベント駆動型のノンブロッキング I/O モデルを採用しているため、開発者は信頼性の高いサーバー アプリケーションを簡単に実装できます。また、最下層は C++ で実装されているため、fibjs は非常に優れたパフォーマンスを備え、大量の同時アクセスに簡単に対処し、非常に安定した信頼性の高いサービスを提供できます。
同時に、fibjs は、TCP プロトコルに基づく全二重通信プロトコルである WebSocket もサポートしており、ブラウザとサーバーの間に中断のない接続を確立し、リアルタイムの双方向データ送信を可能にし、データをサポートできます。あらゆる形式で送信できます。WebSocket を使用すると、より優れた通信効果をもたらすリアルタイム通信アプリケーションを簡単に実装できます。
つまり、fibjs は高性能・高信頼性を重視するだけでなく、WebSocket などのリアルタイム通信機能も備えており、高速な Web アプリケーションの開発に非常に適したフレームワークです。
開発環境のセットアップ
fibjs の開発を始める前に、開発環境を準備する必要があります。この章では、fibjs のインストール方法、fibjs ツールを使用してプロジェクトを初期化する方法、および IDE 統合開発環境の使用方法を紹介します。
fibjs をインストールする
オペレーティング システムが異なると、fibjs のインストール方法が若干異なります。
Linux および macOS ユーザーの場合、次のコマンドを使用して fibjs をインストールできます。
1curl -s https://fibjs.org/download/installer.sh | sh
macOS を使用していて Homebrew パッケージ マネージャーを使用している場合は、次のコマンドを使用してインストールすることもできます。
1brew install fibjs
Windows ユーザーの場合は、fibjs 公式 Web サイトからインストーラーをダウンロードし、指示に従ってインストールする必要があります。
fibjs –init を使用して新しいプロジェクトを作成する
fibjs をインストールした後、fibjs ツールを使用して新しいプロジェクトをすばやく作成できます。次のコマンドを使用して、基本的なプロジェクト テンプレートを作成します。
1fibjs --init
このコマンドは、プロジェクトの基本情報と依存関係情報を保存するために使用される package.json を含む新しいプロジェクト構造を現在のディレクトリに作成します。
Web アプリケーションの作成
Web アプリケーション開発は、現在 fibjs の最も一般的に使用されるアプリケーション シナリオです。fibjs は、Web アプリケーションをより迅速に構築するのに役立つ一連のツールとモジュールを提供します。
HTTP サーバーの作成
- まず http モジュールをインポートします。
- http.Server をインスタンス化し、リクエストをリッスンします。
- サーバーは start 関数を通じて起動されます。
1
2
3
4
5
6const http = require('http');
const server = new http.Server(8080, (req) => {
req.response.write('Hello World!');
});
server.start();
URLパラメータとリクエスト本文を解析する
URL パラメーターとリクエスト本文の解析は非常に重要であり、さまざまなサーバー側アプリケーションで使用されます。fibjs では、受信 URL パラメーターは req.query を通じて直接解析でき、リクエスト本文は req.body を通じて読み取られます。
1
2
3
4
5
6
7
8const http = require('http');
const server = new http.Server(8080, (req) => {
var name = req.query.get('name');
var msg = name ? `Hello ${name}!` : 'Hello world!';
req.response.write(msg);
});
server.start();
インターフェースのアクセス制御を実装する
インターフェイスを介したユーザー アクセスを制限することは、非常に一般的なシナリオです。以下に簡単な例を示します。
1
2
3
4
5
6
7
8
9
10const http = require('http');
const server = new http.Server(8080, (req) => {
if (req.headers.get('auth') === 'ok') {
req.response.write('Hello World!');
} else {
req.response.write('Access Denied!');
}
});
server.start();
ルーティング処理を追加する
ルーティングは、Web アプリケーションにおける最も重要な概念の 1 つであり、受信したリクエストを特定のルールに従ってプロセッサに分配することを指します。fibjs では、独自のルーティング モジュールを作成して http サーバーにバインドし、カスタマイズされたルート解析を通じて URL マッチングと対応する処理を実行できます。
1
2
3
4
5
6
7
8
9const http = require('http');
const { Router } = require('mq');
var router = new Router();
router.get('/hello/:name', function (req, name) {
req.response.write('hello, ' + name);
});
var svr = new http.Server(8080, router);
svr.start();
上記の例は、より単純な構文を使用して実装することもできます。
1
2
3
4
5
6
7
8const http = require('http');
var svr = new http.Server(8080, {
'/hello/:name': function (req, name) {
req.response.write('hello, ' + name);
}
});
svr.start();
エラー処理とログ記録
fibjs では、try-catch ブロックを通じて論理例外をキャプチャし、デバッグや記録のためにログ ファイルに出力できます。致命的な例外の場合は、処理のために上位フレームワークに直接スローできます。
1
2
3
4
5
6
7
8
9
10const console = require('console');
const http = require('http');
const server = new http.Server(8080, (req) => {
try {
// ...
} catch (e) {
console.log(e.message, e.stack);
}
});
クロスドメインリクエスト
fibjs では、enableCrossOrigin メソッドを使用してクロスドメイン リクエストを許可できます。http サーバーを作成してクロスドメイン要求を許可する方法のサンプル コードを次に示します。
1
2
3
4
5
6
7
8const http = require('http');
const server = new http.Server(8080, (req) => {
req.response.write('Hello World!');
});
server.enableCrossOrigin(); // enable cross domain request
server.start();
上の例では、ポート 8080 を使用して http サーバーを作成しました。EnableCrossOrigin() メソッドは、クロスオリジンリクエストを許可します。
EnableCrossOrigin を使用してクロスドメイン要求を許可する場合、パラメーターallowHeaders を渡すことで、受信を許可するクロスドメイン ヘッダーを指定できます。デフォルトでは、allowHeaders 値は Content-Type です。
サンプルコードは次のとおりです。
1
2// enable "Content-Type" and "Authorization" headers in cross domain request
server.enableCrossOrigin("Content-Type, Authorization");
上記のコードでは、allowHeaders の値は「Content-Type, Authorization」です。これは、サーバーが 2 つのクロスドメイン ヘッダー「Content-Type」と「Authorization」を受信できることを意味します。リクエストに他のヘッダーが含まれている場合、サーバーによって拒否されます。
EnableCrossOrigin を使用してクロスドメイン ヘッダーを受信する機能を設定する場合、クロスドメイン リクエストを送信するときに対応するリクエスト ヘッダーも設定する必要があることに注意してください。そうしないと、リクエスト ヘッダーもサーバーによって拒否されます。
Webソケット
WebSocket プロトコルは、TCP プロトコルをベースにした全二重通信プロトコルで、ブラウザとサーバーの間に中断のない接続を確立し、リアルタイムの双方向データ送信を実現し、あらゆる形式のデータ送信をサポートできます。fibjs では、WebSocket サポート モジュールが対応する API インターフェイスを提供し、WebSocket サーバーとクライアントの開発を実現できます。
fibjs ネイティブ WebSocket モジュールを使用して WebSocket サーバー側を実装する
サーバー側では、アップグレード機能を通じて HTTP リクエストを WebSocket 接続に変換できます。http サーバー オブジェクトを作成する場合、ws.upgrade(callback) を使用して http.start() メソッドに渡し、http リクエストを WebSocket に変換できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var ws = require('ws');
var http = require('http');
var server = new http.Server(8080, {
'/ws': ws.upgrade(function(conn, req) {
console.log('a client connected.');
// listening for message events
conn.onmessage = function(evt) {
console.log('received message: ', evt.data);
// echo the message back to client
conn.send('Server: ' + evt.data);
};
// listening for close events
conn.onclose = function(code, reason) {
console.log('closed.');
};
// listening for error events
conn.onerror = function(err) {
console.log(err);
};
})
});
server.start();
上記の例では、クライアントによって送信されたメッセージ イベントと、サーバーとクライアント間の接続終了イベントを監視できます。サーバーはクライアント メッセージを受信すると、同じメッセージをクライアントに送り返します。この時点で、単純な WebSocket ポイントツーポイント通信が実装されます。
データストレージとの対話を実装する
WebSocket を通信に使用する場合、単にメッセージを送受信するだけでなく、データの永続的な保存やクエリなどの操作も考慮する必要があります。このとき、データベースを使用する必要があり、fibjs に組み込まれている db モジュールを使用してデータベースと対話できます。
サンプルコードは次のとおりです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33var ws = require("ws");
var http = require("http");
var db = require("db");
// open a mysql connection
var mysql = db.openMySQL("mysql://root:password@localhost/dbname");
var server = new http.Server(8080, {
"/ws": ws.upgrade(function(conn, req) {
console.log("a client connected.");
// listening for message events
conn.onmessage = function(evt) {
console.log("received message: ", evt.data);
// use execute to query the data
var rs = mysql.execute("SELECT * FROM user WHERE name=?", evt.data.toString());
conn.send(JSON.stringify(rs));
};
// listening for close events
conn.onclose = function(code, reason) {
console.log("closed.");
};
// listening for error events
conn.onerror = function(err) {
console.log(err);
};
})
});
server.start();
上記の例では、まず db モジュールの openMySQL メソッドを使用して MySQL データベース接続オブジェクト mysql を作成し、次にクライアントからのメッセージをリッスンした後、execute メソッドを使用して SQL クエリを直接実行し、レコードを取得します。条件を満たします。最後に、クエリ結果は WebSocket プロトコルを通じてクライアントに返されます。
実際の開発では、例外処理とデータのセキュリティを十分に行う必要があることに注意してください。
要約すると、db モジュールを介して、WebSocket プロトコルと組み合わせてデータベースと簡単かつ簡単に対話し、リアルタイムの高パフォーマンスの Web アプリケーションを実装できます。
WebSocket クライアント/サーバー通信を実装する
クライアント側では、WebSocket インスタンスを作成して URL を指定することで WebSocket サーバーに接続し、サーバーにメッセージを送信できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24var ws = require('ws');
// create a WebSocket object and connect to ws://localhost:8080/ws
var conn = new ws.Socket('ws://localhost:8080/ws');
// listening for open events
conn.onopen = function() {
conn.send('hello');
}
// listening for message events
conn.onmessage = function(evt) {
console.log('received message:', evt.data);
}
// listening for close events
conn.onclose = function(code, reason) {
console.log('closed.');
}
// listening for error events
conn.onerror = function(err) {
console.log(err);
}
上記のクライアント コードでは、WebSocket インスタンスを作成し、その URL を指定します。接続が正常に確立されたら、サーバーにメッセージを送信できます。サーバーはクライアント メッセージを受信すると、同じメッセージをクライアントに送り返します。この時点で、単純な WebSocket ポイントツーポイント通信が実装されます。
WebSocketのメリットと利用シーン
WebSocket プロトコルは、サーバーがクライアントにデータをアクティブにプッシュできる典型的な双方向通信モデルを備えており、リアルタイム性と即時性が要求されるチャットやオンライン ゲームなどの実装によく使用されます。他の送信プロトコルと比較して、WebSocket プロトコルには次の利点があります。
• 高いリアルタイム性能、双方向通信をサポート • シンプルなプロトコル仕様、使いやすい • 多数の同時接続を処理可能 • 長時間接続をサポートし、ネットワーク伝送時間を短縮
WebSocket の最も一般的な使用シナリオには、Web チャット、ゲームの戦い、オンライン再生、インスタント メッセージングなどがあります。
要約すると、WebSocket サポート モジュールを使用すると実装が非常に簡単になり、開発者は独自の Web アプリケーションを迅速に構築できます。
単体テスト
テストフレームワークとテスト方法
ソフトウェア開発プロセスでは、テストは非常に重要なリンクであり、単体テストはその重要な部分です。単体テストにより、コードが設計と要件を満たしているかどうかを効果的に検証し、コードの変更時に発生するエラーを回避できます。一般に、単体テストの原則は、各関数とメソッドをテストして、各関数とメソッドの入力と出力が正しいことを確認することです。
テスト フレームワークは、テスト ケースの作成、実行、検証に使用されるコード ベースであり、テスト ケースの管理、実行、レポート機能を提供します。JavaScript と Node.js では、人気のある単体テスト フレームワークには Mocha、Jest、Jasmine などがあります。fibjs には、独自のテスト フレームワークであるテスト モジュールもあります。
単体テストのプロセスでは、一般的に使用されるテスト方法には、ブラック ボックス テストとホワイト ボックス テストがあります。
ブラックボックステストとは、関数内部の実装内容を考慮せず、関数の入出力のみを考慮するテスト手法です。ブラックボックステストは、要件分析と設計仕様に基づいて、テストケースの分析と実行を通じて、プログラムに論理エラー、境界エラー、セキュリティ上の問題などがないかどうかを判断します。利点は、テストプロセスが簡単で、テスト結果が信頼できることですが、欠点は、テストがすべてのプログラムパスをカバーできないことです。
ホワイトボックス テストは、条件ステートメント、ループ ステートメント、再帰、コード カバレッジなど、関数の内部実装の詳細を考慮するテスト方法です。これらのテストでは、共有データとコード間の相互作用で発生する可能性のある問題を特定できます。ホワイトボックステストの利点は、すべてのプログラムパスをカバーできることですが、欠点は、テストプロセスがより複雑で、テスト結果が環境や実装方法に影響されることです。
テストモジュールを使用してテストケースを作成する
fibjs では、テスト モジュールを使用して Web サーバーのテスト ケースを作成できます。簡単な例を次に示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14var test = require('test');
test.setup();
var http = require('http');
describe('Web server test', () => {
it('should return hello world', () => {
var r = http.get('http://localhost:8080/hello');
assert.equal(r.statusCode, 200);
assert.equal(r.data.toString(), 'Hello World');
});
});
test.run();
この例では、describe 関数と it 関数を使用してテスト モジュールとテスト ケースをそれぞれ定義し、assert 関数を使用してアサーションの検証を行います。
description 関数では、複数の it 関数を定義して、それぞれ異なるシナリオをテストできます。各 IT 関数では、http.get 関数を使用して HTTP GET リクエストをシミュレートし、リクエスト応答を取得し、assertTrue やassertEqual などのアサーション検証を実行できます。
テスト ケースを作成することで、関数やモジュールの正確性を効果的にテストし、製品の品質を確保し、コードの保守性も向上させることができます。
ホットアップデート
ホット アップデートとは、サービスを停止せずにサーバー コードを更新することを指します。プログラム開発プロセスでは、迅速に反復するために、コードの調整や新しい機能が必要になることがよくあります。ホット アップデートを使用すると、新しいコードを使用して、サービスを停止することなく反復作業をより効率的に完了できます。
fibjs では、SandBox モジュールを使用してスムーズなホット アップデートを実現できます。SandBox モジュールは、安全な実行環境を提供し、グローバル変数やその他の関数をシミュレートできます。具体的な実装については、次の手順を参照してください。
- 更新する必要があるコード ファイル (web.js など) を読み込みます。
- SandBox を通じて、新しいセキュリティ モジュールを作成し、モジュールに web.js をロードして、セキュリティ モジュールを生成します。生成されたセキュリティ モジュールを介して、実行中のサービスのハンドラーを再マウントします。
- サーバーは以前のリクエストの処理を継続し、新しいリクエストは新しいハンドラーにマウントされます。
以下は、SandBox モジュールを使用してスムーズなホット アップデートを実装するサンプル コードです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const fs = require('fs');
const http = require('http');
const { SandBox } = require('vm');
let FILE_PATH = './web.js';
let handler = new SandBox().require(FILE_PATH).handler;
const server = new http.Server(8080, handler);
server.start();
fs.watch(FILE_PATH, (event, filename) => {
handler = new SandBox().require(FILE_PATH).handler;
server.handler = handler;
console.log(`[${new Date().toLocaleString()}] server reloaded.`);
});
このコードでは、プログラムの起動時にまず web.js にコードを読み込み、次に SandBox インスタンスを作成して、そのインスタンスにコードを読み込みます。その後、新しい HTTP サーバーを作成し、ハンドラー内のメソッドを使用してリクエストを処理しました。
コードでは、fs.watch を使用して web.js ファイルの変更を監視し、ファイルが変更されたらコードをリロードし、ハンドラーの実装を更新します。
パフォーマンスの最適化
開発プロセスでは、パフォーマンスの問題に直面することがよくあります。コードを最適化し、パフォーマンスを向上させることは、開発者にとって必須のスキルの 1 つです。fibjs では、CPU プロファイラーを使用して、プログラムの実行ステータスを分析し、コードを最適化できます。
fibjs では、コマンド ライン パラメータ --prof を使用して fibjs を起動し、CPU プロファイラを起動するだけです (デフォルトの間隔は 1000 ミリ秒)。より高精度のログ分析が必要な場合は、 --prof-interval パラメーターを使用してログ間隔を設定できます。例えば:
1
2$ fibjs --prof test.js # 启动 CPU Profiler,默认以 1000ms 为间隔
$ fibjs --prof --prof-interval=10ms test.js # 启动 CPU Profiler,以 10000us(即 10ms)为间隔
fibjs の実行が終了すると、現在のディレクトリにソース ファイル名ディレクトリが生成され、このディレクトリにはログ ファイルといくつかの補助ファイルが含まれます。ログ ファイルのデフォルト名は fibjs-xxxx.log です。xxxx はタイムスタンプです。--log オプションを使用してログ ファイル名を指定できます。この時点で、次を--prof-process
使用して生成されたログを処理できます。
1fibjs --prof-process fibjs-xxxx.log prof.svg
操作が完了したら、ブラウザを使用して prof.svg を開き、このログのフレーム グラフを表示します。 クリックするとフルサイズの画像が表示されます。フルサイズの画像では、マウスを操作して詳細を表示できます。情報: svg 教授。
生成されたフレーム グラフでは、各カラー ブロックが記録ポイントを表します。カラー ブロックが長いほど、記録される回数が多くなります。各線は呼び出しスタックの層を表し、層が多いほど、より多くの層が呼び出されます。スタックが呼び出されます。カラーブロックが下になるほど、機能が独創的になります。
カラーブロックには赤と青の2種類があります。fibjs プロファイラでは、赤は JavaScript 操作を表し、青は io 操作またはネイティブ操作を表します。解決する必要がある問題に応じて、重点を置く必要がある領域は異なります。たとえば、CPU 使用率が高い問題を解決する必要がある場合は、赤色のブロックに注意を払う必要があり、アプリケーションの CPU 使用率は低いが応答が遅い場合は、青色のブロックに注意を払う必要があります。上部近くのカラーブロックが大きいほど、より多くの注意と最適化が必要になります。
より多くの CPU リソースを消費する関数を調整したり、IO などを非同期で実装したり、コードを書くときに最適化したりすることができます。
導入とオンライン
プロジェクトを実稼働環境で実行するには、プロジェクトをコンパイルしてデプロイする必要があります。ここでは、package.json ファイルを使用してコンパイルとデプロイを構成する方法を紹介します。
プロジェクトでは、package.json を使用してプロジェクトの依存関係を管理し、コンパイルとデプロイメントを構成できます。簡単なサンプル package.json を例として挙げます。
1
2
3
4
5
6
7{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"fib-pool": "^1.0.0"
}
}
プロジェクトをコンパイルしてデプロイする必要がある場合は、ターミナルでプロジェクト ディレクトリに入り、次のコマンドを実行するだけです。
1fibjs --install
このコマンドは、プロジェクトが依存するモジュールを自動的にインストールします。その後、次のコマンドを使用してプロジェクトを開始できます。
1fibjs app.js
👉【ドメイン名のルーティング】