Node.js 运行时

GraalVM可以运行未经修改的Node.js应用程序。GraalVM的Node.js运行时基于最新版本的Node.js,并运行GraalVM JavaScript引擎(GraalJS)而非Google V8。某些内部功能(例如,VM内部统计、配置、性能分析、调试等)不受支持,或支持但行为可能有所不同。

应用程序可以自由导入和使用NPM包,包括原生包。

Node.js入门 #

从GraalVM for JDK 21开始,GraalVM Node.js运行时作为单独的发行版提供。Oracle GraalVM和GraalVM社区版都提供两种独立的运行时选项:Native Image编译的启动器或基于JVM的运行时。为了区分它们,GraalVM社区版在名称中带有后缀-communitygraaljs-community-<version>-<os>-<arch>.tar.gzgraalnodejs-community-<version>-<os>-<arch>.tar.gz。带有JVM的独立版本在名称中带有-jvm后缀。

要启用GraalVM Node.js运行时,请为您的操作系统安装基于Oracle GraalVM或GraalVM社区版的Node.js发行版。

  1. 导航到GitHub发布页面并为您的操作系统选择所需的独立版本。

  2. 解压归档文件
     tar -xzf <archive>.tar.gz
    

    或者,在Finder中打开文件。

  3. 检查版本以查看运行时是否激活
     ./path/to/bin/node --version
    

运行Node.js应用程序 #

Node.js安装提供了nodenpm启动器

node [options] [filename] [args]

npm命令等同于默认的Node.js命令,并具有额外的GraalVM特定功能(例如,与Java的互操作性)。可用选项列表可以通过node --help获取。

使用node启动器执行Node.js应用程序。例如

  1. 如下使用npm install安装colorsansispan
     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二进制启动器启动时,Node.js功能可用。从Java上下文启动Node.js应用程序或访问NPM包时会受到某些限制,请参阅Node.js与JavaScript上下文

使用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模块之间的差异,请参阅Node.js与JavaScript上下文

Node.js多线程 #

GraalJS的基本多线程模型也适用于Node.js应用程序。在Node.js中,可以创建Worker线程来并行执行JavaScript代码,但JavaScript对象不能在Worker之间共享。相反,使用GraalVM Java互操作性创建的Java对象(例如,使用Java.type())可以在Node.js Worker之间共享。这使得多线程Node.js应用程序能够共享Java对象。

GraalVM Node.js单元测试包含多个多线程Node.js应用程序示例。最值得注意的示例展示了如何

  1. Node.js worker线程可以执行Java代码.
  2. Java对象可以在Node.js worker线程之间共享.
  3. JavaScript Promise对象可用于await来自worker的消息,使用Java对象将promise绑定到worker消息.

常见问题 #

GraalVM的Node.js运行时与原始Node实现兼容吗? #

GraalVM的Node.js运行时与原始Node.js(基于V8引擎)基本兼容。这使得大量的基于npm的模块能够兼容。事实上,在我们测试的10万个npm模块中,超过94%通过了所有测试。尽管如此,仍需考虑几个差异来源

  • 设置: GraalVM的Node.js大体上模仿了Node的原始设置,包括node可执行文件、npm等。然而,并非所有命令行选项都受支持(或行为完全相同)。模块可能要求原生模块针对v8.h文件(重新)编译。

    从GraalVM for JDK 21开始,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测试框架的9.5万个模块进行了测试。使用mocha可以自动化执行测试和理解测试结果的过程。
    • 流行模块的手动测试:在手动测试设置中测试精选的npm模块列表。这些高度相关的模块以更复杂的方式进行测试。

NPM包可以全局安装吗? #

Node包可以使用npm-g选项全局安装,这在GraalVM的Node.js实现中同样适用。

原始Node.js实现有一个主目录(node/bin/)用于存放二进制文件和全局安装的包及其命令行工具,而GraalVM的Node.js将二进制文件放在/path/to/graaljs/bin/目录中。在GraalVM Node.js运行时上全局安装NPM包时,可执行文件(例如,用于命令行界面工具的链接)会放在JavaScript特定的目录中。为了使全局安装的包正常运行,您可能需要将/path/to/graaljs/bin添加到您的$PATH中。

另一种选择是通过设置$PREFIX环境变量,或者在运行npm install时指定--prefix选项来指定npm的全局安装目录。

有关更多详细信息,请参阅全局安装npm

联系我们