我应该如何从Java应用程序运行NodeJS?
我正在编写一个Java库,实际上是一个Clojure库,但是对于这个问题,重要的是它在JVM上运行。 这个库需要执行一些JavaScript。 我尝试过与Nashorn,但我遇到一些限制,可能太难以克服。 作为替代,我想尝试NodeJS。
我希望自己的库是独立的,不依赖于独立运行NodeJS的系统,因此需要特定的部署机制将Java和NodeJS工件放置在正确的位置,由两个不同的networking服务器提取。 但是这种方法带来了一些问题。
我将通过HTTP与NodeJS通话,但是我不希望NodeJS打开特定的端口。 我想find一个随机的未使用的,所以没有碰撞。 我也想控制从NodeJS的日志去,以保持与我的应用程序的其余部分。 最后,我的应用程序应该能够检测到NodeJS何时崩溃并重新运行它或报告错误的信息。
什么是最好的方法来解决这个问题? 有没有任何Java库可以帮助用这种方式pipe理subprocess? 特别是我应该从NodeJS方面做的任何事情(我对NodeJS很陌生,以前从未使用过)。
我最终的解决scheme是使用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))
然后调用它开始。 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中运行JavaScript。 这样的事情可以为你的情况吗? 如果不是这里有一些资源:
- nodejs中的随机端口您可以在构build过程中再次打开另一个服务来查找打开的端口,或者让您的节点应用程序根据其抓取的端口向您的java服务器发出http请求。
- 温斯顿是我find的最好的日志库,你不应该有任何问题login到相同的path。
- Forever和PM2是保持节点运行的公共节点进程pipe理器。 我现在更喜欢永远(不知道为什么)
这听起来像你会在节点内使用很多的CPU。 如果是这种情况,你可能会想使用集群模块 (所以nodejs可以利用多个核心)。 如果你阻塞了事件循环(基于cpu的处理,那么你将只能对每个分叉进程执行1个并发请求)。
Nashorn确实也遇到了一些我也碰到的问题,比如find关于他们的一些API的信息(文档有很多不足之处),以及慢启动。
我会build议,而不是:把你的服务器端渲染作为一个服务,而不是一个subprocess 。
换句话说,你可以在内部networking上运行一个Node.js实例,并且只允许本地连接(你也可以在任何你想要的地方发送日志)。 然后,您可以使用要呈现的页面向服务发出请求,例如localhost:10015/posts/
并让该Node.js应用程序在无头浏览器(使用Phantom或Slimer等内容)中呈现页面。
为了保持你的Node服务器,你可以使用Forever
或者supervisord
,并且帮助你更快地获得动力,你可以看看Prerender团队做了什么: https : //github.com/prerender/prerender-node
我一直处于类似的地位,我必须从python脚本运行fortran,所以这是我的两分钱。 用Java中的terminal命令运行你的node.js脚本,如下所示:
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); }
通过设置,您可以将parameter passing给您的节点程序并获得响应。 虽然我不确定你在使用你的node.js程序,所以不知道这是否有帮助。