Node.js ランタイム

GraalVM は、変更されていない Node.js アプリケーションを実行できます。 GraalVM の Node.js ランタイムは、最新バージョンの Node.js に基づいており、Google V8 の代わりに GraalVM JavaScript エンジン (GraalJS) を実行します。一部の内部機能 (VM 内部統計、構成、プロファイリング、デバッグなど) はサポートされていないか、動作が異なる可能性があります。

アプリケーションは、ネイティブなものも含め、NPM パッケージを自由にインポートして使用できます。

Node.js を使い始める #

JDK 21 用の GraalVM 以降、GraalVM Node.js ランタイムは個別のディストリビューションとして利用できます。 Oracle GraalVM と GraalVM Community Edition の両方で、2 つのスタンドアロン ランタイム オプションが利用可能です。ネイティブ イメージでコンパイルされたランチャーまたは JVM ベースのランタイムです。 これらを区別するために、GraalVM Community Edition バージョンは名前に `-community` というサフィックスが付いています。 `graaljs-community-<version>-<os>-<arch>.tar.gz`、`graalnodejs-community-<version>-<os>-<arch>.tar.gz`。 JVM が付属するスタンドアロンの名前には、`-jvm` サフィックスが付いています。

GraalVM Node.js ランタイムを有効にするには、オペレーティング システム用の Oracle GraalVM または GraalVM Community Edition に基づく Node.js ディストリビューションをインストールします。

  1. GitHub リリース に移動し、オペレーティング システムに適したスタンドアロンを選択します。

  2. アーカイブを解凍します
     tar -xzf <archive>.tar.gz
    

    または、Finder でファイルを開きます。

  3. ランタイムがアクティブかどうかを確認するためにバージョンを確認します
     ./path/to/bin/node --version
    

Node.js アプリケーションの実行 #

Node.js インストールでは、`node` と `npm` ランチャーが提供されます

node [options] [filename] [args]

`npm` コマンドはデフォルトの Node.js コマンドと同等であり、GraalVM 固有の追加機能 (Java との相互運用性など) を備えています。 使用可能なオプションのリストは、`node --help` で取得できます。

`node` ランチャーを使用して Node.js アプリケーションを実行します。 例えば

  1. 次のように `npm install` を使用して `colors` および `ansispan` パッケージをインストールします
     npm install colors ansispan
    

    パッケージがインストールされたら、アプリケーションから使用できます。

  2. 次のコード スニペットを *app.js* という名前のファイルに追加し、Node.js パッケージをインストールしたのと同じディレクトリに保存します
     const http = require("http");
     const span = require("ansispan");
     require("colors");
    
     http.createServer(function (request, response) {
         response.writeHead(200, {"Content-Type": "text/html"});
         response.end(span("Hello Node.js!".green));
     }).listen(8000, function() { console.log("Node.js server running at http://127.0.0.1:8000/".red); });
    
     setTimeout(function() { console.log("DONE!"); process.exit(); }, 2000);
    
  3. 次のように `node` コマンドを使用して GraalVM Node.js ランタイムで実行します
     node app.js
    

Node.js 機能は、アプリケーションが `node` バイナリ ランチャーから起動されたときに使用できます。 Java コンテキストから Node.js アプリケーションを起動したり、NPM パッケージにアクセスしたりする場合には、特定の制限が適用されます。 Node.js vs. Java スクリプト コンテキスト を参照してください。

`npm` を使用したパッケージのインストール #

Node.js パッケージをインストールするには、`npm` ランチャーを使用します。 `npm` コマンドはデフォルトの NPM コマンドと同等であり、そのほとんどのオプションをサポートしています。

NPM パッケージは、次のようにインストールできます

npm install [package]

GraalVM Node.js の `npm` コマンドは NPM とほぼ互換性があるため、パッケージは想定どおり *node_modules/* ディレクトリにインストールされます。

`npm` パッケージをグローバルにインストールする #

Node パッケージは、`npm` と `-g` オプションを使用してグローバルにインストールできます。 デフォルトでは、`npm` はグローバル パッケージ (実行可能ファイルへのリンク) を `node` 実行可能ファイルがインストールされているパス (通常は *node/bin/*) にインストールします。 そのディレクトリは、グローバル パッケージがインストールされる場所です。 特にコマンド ライン インターフェースを定期的に使用する場合は、そのディレクトリを `$PATH` に追加することをお勧めします。

別のオプションは、`$PREFIX` 環境変数を設定するか、`npm install` を実行するときに `--prefix` オプションを指定することにより、`npm` のグローバル インストール ディレクトリを指定することです。 たとえば、次のコマンドはグローバル パッケージを * /foo/bar/* ディレクトリにインストールします

npm install --prefix /foo/bar -g <package>

`prefix` の詳細については、公式 NPM ドキュメント を参照してください。

Java との相互運用性 #

Node.js ランタイムは JVM に埋め込むことはできず、個別のプロセスとして起動する必要があります。

  1. 次のコードを *HelloPolyglot.java* という名前のファイルに保存してコンパイルします
     import org.graalvm.polyglot.*;
     import org.graalvm.polyglot.proxy.*;
    
     public class HelloPolyglot {
    
         static String JS_CODE = "(function myFun(param){console.log('hello '+param);})";
    
         public static void main(String[] args) {
             System.out.println("Hello Java!");
             try (Context context = Context.create()) {
                 Value value = context.eval("js", JS_CODE);
                 value.execute(args[0]);
             }
         }
     }
    
  2. 次に、このコードを *app.js* という名前のファイルに保存します
     var HelloPolyglot = Java.type("HelloPolyglot");
    
     HelloPolyglot.main(["from node.js"]);
    
     console.log("done");
    
  3. `node` で実行します
     node --vm.cp=. app.js
    

    次の出力が表示されます

     Hello Java!
     hello from node.js
     done
    

Node.js と JVM の両方が同じプロセスで実行され、相互運用性は上記と同じ `Value` クラスを使用して機能します。

`node` ランチャーの実行と、Java `Context` から Node.js NPM モジュールまたは ECMAScript モジュールにアクセスする場合の違いについては、NodeJSVSJavaScriptContext を参照してください。

Node.js でのマルチスレッド #

GraalJS の基本的なマルチスレッド モデルは、Node.js アプリケーションにも適用されます。 Node.js では、ワーカー スレッドを作成して JavaScript コードを並列実行できますが、JavaScript オブジェクトはワーカー間で共有できません。 逆に、GraalVM Java 相互運用性を使用して作成された Java オブジェクト (たとえば、`Java.type()` を使用) は、Node.js ワーカー間で共有できます。 これにより、マルチスレッド Node.js アプリケーションは Java オブジェクトを共有できます。

GraalVM Node.js ユニット テスト には、マルチスレッド Node.js アプリケーションの例がいくつか含まれています。 最も注目すべき例は、次の方法を示しています

  1. Node.js ワーカースレッドは Java コードを実行できます.
  2. Java オブジェクトは Node.js ワーカースレッド間で共有できます.
  3. JavaScript `Promise` オブジェクトを使用して、ワーカーからのメッセージを `await` し、Java オブジェクトを使用して promise をワーカー メッセージにバインドできます.

よくある質問 #

GraalVM の Node.js ランタイムは、元の Node 実装と互換性がありますか? #

GraalVM の Node.js ランタイムは、元の Node.js (V8 エンジンに基づく) とほぼ互換性があります。 これにより、多数の `npm` ベースのモジュールが互換性を持つようになります。 実際、テストした 100k の `npm` モジュールのうち、94% 以上がすべてのテストに合格しています。それでも、いくつかの違いのソースを考慮する必要があります

  • **セットアップ:** GraalVM の Node.js は、`node` 実行可能ファイル、`npm` などを含む、元の Node のセットアップをほとんど模倣しています。 ただし、すべてのコマンドライン オプションがサポートされているわけではありません (またはまったく同じように動作するわけではありません)。 モジュールでは、ネイティブ モジュールが *v8.h* ファイルに対して (再) コンパイルされている必要がある場合があります。

    JDK 21 用の GraalVM 以降、GraalVM Node.js ランタイムは個別のディストリビューションとして利用できます。 Node.js を使い始める を参照してください。

  • **内部:** GraalVM の Node.js は JVM 上に実装されているため、V8 に基づく Node.js とは内部アーキテクチャが異なります。 これは、一部の内部メカニズムの動作が異なり、V8 の動作を正確に複製できないことを意味します。 これはユーザー コードに影響を与えることはほとんどありませんが、V8 の内部に依存するネイティブに実装されたモジュールに影響を与える可能性があります。

  • **パフォーマンス:** GraalVM の Node.js は JVM 上に実装されているため、パフォーマンス特性は元のネイティブ実装とは異なります。 GraalVM のピーク パフォーマンスは多くのベンチマークで V8 に匹敵しますが、通常、ピークに達するまで ( *ウォームアップ* と呼ばれる) に時間がかかります。 (ピーク) パフォーマンスを測定する際は、Graal コンパイラに追加の時間を与えてください。

  • **互換性:** GraalVM の Node.js ランタイムは、次のアプローチを使用して Node.js コードとの互換性をチェックし、維持します

    • node-compat-table: GraalVM の Node.js は、*node-compat-table* モジュールを使用して他のエンジンと比較され、Node.js コードを壊す可能性のある非互換性が強調表示されます。
    • *mocha* を使用したモジュールの自動大量テスト: 大量のモジュールをテストするために、GraalVM の Node.js ランタイムは、mocha テスト フレームワークを使用する 95k モジュールに対してテストされます。 mocha を使用すると、テストを実行し、テスト結果を理解するプロセスを自動化できます。
    • 一般的なモジュールの手動テスト: 選択された `npm` モジュール リストは、手動テスト セットアップでテストされます。 これらの非常に関連性の高いモジュールは、より高度な方法でテストされます。

NPM パッケージをグローバルにインストールできますか? #

Node パッケージは、GraalVM の Node.js 実装の両方で、`npm` と `-g` オプションを使用してグローバルにインストールできます。

元の Node.js 実装には、バイナリとグローバルにインストールされたパッケージとそのコマンドライン ツールを配置するためのメイン ディレクトリ (*node/bin/*) が 1 つありますが、GraalVM の Node.js はバイナリを */path/to/graaljs/bin/* ディレクトリに配置します。 GraalVM Node.js ランタイムに NPM パッケージをグローバルにインストールする場合、コマンド ライン インターフェース ツールなどの実行可能ファイルへのリンクは、JavaScript 固有のディレクトリに配置されます。 グローバルにインストールされたパッケージが正しく機能するためには、*/path/to/graaljs/bin* を `$PATH` に追加する必要がある場合があります。

別のオプションは、`$PREFIX` 環境変数を設定するか、`npm install` を実行するときに `--prefix` オプションを指定することにより、`npm` のグローバル インストール ディレクトリを指定することです。

詳細については、`npm` パッケージをグローバルにインストールする を参照してください。

お問い合わせ