我应该如何从 Java 应用程序 运行 NodeJS?
How should I run NodeJS from a Java application?
我正在编写一个 Java 库,实际上是一个 Clojure 库,但是对于这个问题,重要的是它 运行 在 JVM 上。该库需要执行一些 Java 脚本。 I tried with Nashorn 但我遇到了一些可能难以克服的限制。作为替代方案,我想尝试 NodeJS。
我希望我的库是自包含的,而不是独立地依赖于系统 运行ning NodeJS,因此需要一个特定的部署机制来将 Java 和 NodeJS 工件放置在正确的位置由两个不同的网络服务器接收。但是,这种方法带来了一些问题。
我将通过 HTTP 与 NodeJS 通信,但我不希望 NodeJS 打开特定端口。我想找到一个随机未使用的,这样就不会发生冲突。我还想控制 NodeJS 日志的去向,以便将它们保存在我的应用程序的其余部分。最后,我的应用程序应该能够检测到 NodeJS 何时崩溃并重新运行它或报告错误信息。
解决这个问题的最佳方法是什么?是否有任何 Java 库可以帮助以这种方式管理子进程?我应该从 NodeJS 方面做任何特别的事情(我是 NodeJS 的新手,我以前从未使用过它)。
关于如何 运行 java 在 java 中编写脚本,有一个很好的答案 here。这样的事情对你的情况可行吗?如果没有,这里有一些资源:
- Random port in nodejs 您可以在构建期间点击另一个服务来查找开放端口,或者让您的节点应用程序根据它获取的端口向您的 java 服务器发出 http 请求。
- Winston 是我发现的最好的日志记录库,您在同一路径上记录应该没有任何问题。
- Forever and PM2是保持节点运行ning的公共节点进程管理器。我目前更喜欢永远(不知道为什么)
听起来您将在 node.js 中使用很多 cpu。如果是这种情况,您可能会想要使用 cluster module(这样 nodejs 就可以利用多个内核)。如果您阻止事件循环(基于 cpu 的处理将会阻止,那么您将只能为每个分叉进程执行 1 个并发请求)。
Nashorn 确实有一些我也 运行 遇到过的问题,例如查找有关其某些 API 的信息(文档还有很多不足之处),以及启动速度慢。
我建议改为:将您的服务器端呈现视为 服务 而不是 子进程.
换句话说,您可以 运行 一个 Node.js 实例在您的内部网络上的端口 10015
上,并且只允许本地连接到它(您也可以在任何地方发送日志想)。然后,您可以使用要呈现的页面向服务发出请求,例如 localhost:10015/posts/
并让 Node.js 应用程序在无头浏览器中呈现页面(使用 Phantom 或 Slimer 之类的东西) .
为了保持您的节点服务器正常运行,您可以使用 Forever
或 supervisord
,并且为了帮助您更快地获得关注,您可以查看 Prerender 团队所做的工作:
https://github.com/prerender/prerender-node
我处于类似的位置,我不得不从 python 脚本 运行 fortran,所以这是我的两分钱。 运行 您的 node.js 脚本在 Java 中带有终端命令,如下所示:
Runtime rt = Runtime.getRuntime();
String[] commands = {"node example.js", "args"};
Process proc = rt.exec(commands);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
通过设置,您可以将参数传递给您的节点程序并获得响应。虽然我不确定您将 node.js 程序用于什么目的,但不确定这是否有帮助。
我最后的解决方案是像这样使用 ProcessBuilder:
(defn create-process-builder [js-engine]
(doto (ProcessBuilder. ["node" (:path js-engine)
"--port-file" (:port-file js-engine)
"--default-ajax-host" (:default-ajax-host js-engine)
"--default-ajax-port" (str (:default-ajax-port js-engine))])
.inheritIO))
然后在里面调用start。 inheritIO 导致它的输出转到当前进程的输出,这有效地合并了 stdout 和 stderr。
在此之上,NodeJS 通过指定 0 作为端口号打开一个随机端口并将其写入文件:
(let [app (-> (express)
(.use (cookie-parser))
(.get "/" (fn [_req res] (.send res "Universal JavaScript engine for server side pre-rendering single page applications.")))
(.get "/render" render))
server (.createServer http app)]
(.listen server 0 (fn [] (.writeFile fs (:port-file options) (.-port (.address server)))))))))
然后由 Java 端打开(等待它出现):
(defn get-port-number [js-engine]
(or (with-timeout
(:start-timeout js-engine)
(loop [port-number (read-port-file js-engine)]
(if (string/blank? port-number)
(if (is-running? js-engine)
(do (Thread/sleep 100)
(recur (read-port-file js-engine)))
(throw (Exception. (str "While waiting for port number, process died: " (:path js-engine)))))
port-number)))
(throw (Exception. (str "Waited for " (:start-timeout js-engine) " for " (:path js-engine) " to start and report its port number but it timed out.")))))
我正在编写一个 Java 库,实际上是一个 Clojure 库,但是对于这个问题,重要的是它 运行 在 JVM 上。该库需要执行一些 Java 脚本。 I tried with Nashorn 但我遇到了一些可能难以克服的限制。作为替代方案,我想尝试 NodeJS。
我希望我的库是自包含的,而不是独立地依赖于系统 运行ning NodeJS,因此需要一个特定的部署机制来将 Java 和 NodeJS 工件放置在正确的位置由两个不同的网络服务器接收。但是,这种方法带来了一些问题。
我将通过 HTTP 与 NodeJS 通信,但我不希望 NodeJS 打开特定端口。我想找到一个随机未使用的,这样就不会发生冲突。我还想控制 NodeJS 日志的去向,以便将它们保存在我的应用程序的其余部分。最后,我的应用程序应该能够检测到 NodeJS 何时崩溃并重新运行它或报告错误信息。
解决这个问题的最佳方法是什么?是否有任何 Java 库可以帮助以这种方式管理子进程?我应该从 NodeJS 方面做任何特别的事情(我是 NodeJS 的新手,我以前从未使用过它)。
关于如何 运行 java 在 java 中编写脚本,有一个很好的答案 here。这样的事情对你的情况可行吗?如果没有,这里有一些资源:
- Random port in nodejs 您可以在构建期间点击另一个服务来查找开放端口,或者让您的节点应用程序根据它获取的端口向您的 java 服务器发出 http 请求。
- Winston 是我发现的最好的日志记录库,您在同一路径上记录应该没有任何问题。
- Forever and PM2是保持节点运行ning的公共节点进程管理器。我目前更喜欢永远(不知道为什么)
听起来您将在 node.js 中使用很多 cpu。如果是这种情况,您可能会想要使用 cluster module(这样 nodejs 就可以利用多个内核)。如果您阻止事件循环(基于 cpu 的处理将会阻止,那么您将只能为每个分叉进程执行 1 个并发请求)。
Nashorn 确实有一些我也 运行 遇到过的问题,例如查找有关其某些 API 的信息(文档还有很多不足之处),以及启动速度慢。
我建议改为:将您的服务器端呈现视为 服务 而不是 子进程.
换句话说,您可以 运行 一个 Node.js 实例在您的内部网络上的端口 10015
上,并且只允许本地连接到它(您也可以在任何地方发送日志想)。然后,您可以使用要呈现的页面向服务发出请求,例如 localhost:10015/posts/
并让 Node.js 应用程序在无头浏览器中呈现页面(使用 Phantom 或 Slimer 之类的东西) .
为了保持您的节点服务器正常运行,您可以使用 Forever
或 supervisord
,并且为了帮助您更快地获得关注,您可以查看 Prerender 团队所做的工作:
https://github.com/prerender/prerender-node
我处于类似的位置,我不得不从 python 脚本 运行 fortran,所以这是我的两分钱。 运行 您的 node.js 脚本在 Java 中带有终端命令,如下所示:
Runtime rt = Runtime.getRuntime();
String[] commands = {"node example.js", "args"};
Process proc = rt.exec(commands);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
通过设置,您可以将参数传递给您的节点程序并获得响应。虽然我不确定您将 node.js 程序用于什么目的,但不确定这是否有帮助。
我最后的解决方案是像这样使用 ProcessBuilder:
(defn create-process-builder [js-engine]
(doto (ProcessBuilder. ["node" (:path js-engine)
"--port-file" (:port-file js-engine)
"--default-ajax-host" (:default-ajax-host js-engine)
"--default-ajax-port" (str (:default-ajax-port js-engine))])
.inheritIO))
然后在里面调用start。 inheritIO 导致它的输出转到当前进程的输出,这有效地合并了 stdout 和 stderr。
在此之上,NodeJS 通过指定 0 作为端口号打开一个随机端口并将其写入文件:
(let [app (-> (express)
(.use (cookie-parser))
(.get "/" (fn [_req res] (.send res "Universal JavaScript engine for server side pre-rendering single page applications.")))
(.get "/render" render))
server (.createServer http app)]
(.listen server 0 (fn [] (.writeFile fs (:port-file options) (.-port (.address server)))))))))
然后由 Java 端打开(等待它出现):
(defn get-port-number [js-engine]
(or (with-timeout
(:start-timeout js-engine)
(loop [port-number (read-port-file js-engine)]
(if (string/blank? port-number)
(if (is-running? js-engine)
(do (Thread/sleep 100)
(recur (read-port-file js-engine)))
(throw (Exception. (str "While waiting for port number, process died: " (:path js-engine)))))
port-number)))
(throw (Exception. (str "Waited for " (:start-timeout js-engine) " for " (:path js-engine) " to start and report its port number but it timed out.")))))