2015-09-16 2 views
18

Я пишу библиотеку Java, на самом деле, библиотеку Clojure, но для этого вопроса важно, что она работает на JVM. Эта библиотека должна выполнить некоторый JavaScript. I tried with Nashorn, но я сталкиваюсь с некоторыми ограничениями, которые могут быть слишком сложными для преодоления. В качестве альтернативы, я хочу попробовать NodeJS.Как запустить NodeJS из приложения Java?

Я хочу, чтобы моя библиотека была автономной, чтобы не зависать от системы, выполняющей NodeJS самостоятельно, и, следовательно, требуя, чтобы определенный механизм развертывания помещал артефакты Java и NodeJS в нужные места, которые должны были быть захвачены двумя разными сетевыми серверами , Однако этот подход вызывает некоторые проблемы.

Я буду говорить с NodeJS по HTTP, но я не хочу, чтобы NodeJS открывал определенный порт. Я хочу найти случайный неиспользованный, чтобы не было столкновений. Я также хочу контролировать, где идут журналы из NodeJS, чтобы сохранить их с остальной частью моего приложения. Наконец, мое приложение должно иметь возможность обнаруживать, когда NodeJS разбился и повторно запустить его или сообщить об ошибке с информацией.

Каков наилучший способ приблизиться к этому? Существуют ли какие-либо библиотеки Java, чтобы помочь управлять дочерним процессом таким образом? Что-нибудь, в частности, я должен делать со стороны NodeJS (я очень новичок в NodeJS, я никогда не использовал его раньше).

+0

Я полагаю, это зависит от того, как ваша библиотека предназначена для потребления, но с запуском библиотеки для тяжелого серверного процесса для выполнения своей работы кажется запахом кода. –

+0

Вы слышали об аватаре? У меня также были некоторые проблемы, когда я не мог использовать некоторые модули nodejs, но аватар заполнил пробел: https://strongloop.com/strongblog/how-to-run-node-js-on-the-jvm-with-avatar- js-and-loopback/ – Tiago

+0

Как раз немного сузить ответ ... каковы ограничения, с которыми вы столкнулись в точности? Сценарий на стороне сервера с nashorn добавляет 6ms к avg. время ответа в вашем сообщении в конце, поэтому я думаю, что производительность не одна из них – cviejo

ответ

9

Мое решение в конце концов должен был использовать 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."))))) 
+0

Как насчет http://nodyn.io/? – Stanimir

+0

@Pablo Thought Я бы заметил, что Node.js, прослушивающий 0, может не всегда действовать так, как ожидалось. См. [Здесь] (http://stackoverflow.com/a/10266225/4133078) –

+0

@Stanimir Я не мог понять, насколько полно nodyn.io, это не кажется очень полным, и проект официально заброшен. – Pablo

6

Существует довольно хороший ответ here о том, как запустить javascript в java. Будет ли что-то подобное для вашего дела? Если не здесь некоторые ресурсы:

  • Random port in nodejs Вы могли поразить еще одну услугу, чтобы найти открытый порт во время сборки, или ваше приложение узла огня запроса HTTP на ваш Java сервер на основе порта он захватывает.
  • Winston - лучшая библиотека регистрации, которую я обнаружил, у вас не должно быть проблем с протоколом по тому же пути.
  • Forever и PM2 - это общие диспетчеры процессов узла, которые поддерживают работу узла. В настоящее время я предпочитаю навсегда (не знаю почему)

Похоже, что вы будете использовать множество процессоров внутри узла. Если это так, вы, вероятно, захотите использовать cluster module (так что nodejs может использовать несколько ядер). Если вы заблокируете цикл событий (в котором будет выполняться обработка на основе процессора, вы сможете выполнять только один одновременный запрос на разветвленный процесс).

+0

Об этом другом вопросе, на который вы указали, нет, я уже пробовал Nashorn, и этого недостаточно. Мне нужно что-то вроде NodeJS. – Pablo

4

У Nashorn есть некоторые проблемы, с которыми я столкнулся, например, поиск информации о некоторых из их API (документация оставляет желать лучшего) и медленной загрузки.

То, что я рекомендовал бы вместо этого: относиться к вашей стороне сервера визуализации как службы, а не процессребенка.

Другими словами, вы можете запустить экземпляр Node.js в своей внутренней сети на порту 10015 и разрешить только локальные подключения (вы также можете отправлять журналы везде, где хотите). Затем вы можете сделать запрос к службе со страницами, которые вы хотите отобразить, например localhost:10015/posts/, и иметь приложение Node.js для отображения страницы внутри браузера без браузера (используя что-то вроде Phantom или Slimer).

Чтобы ваш сервер узла, вы можете использовать Forever или supervisord, и, чтобы помочь вам получить тягу быстрее, вы можете взглянуть на то, что команда Предварительно обрабатывать сделал: https://github.com/prerender/prerender-node

4

Я был в таком же положении, где мне пришлось запускать fortran из скрипта python, так что вот мои два цента. Запустите 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, поэтому не уверен, что это полезно.

+0

Одна вещь, которую следует отметить с помощью этого метода, состоит в том, что для каждого процесса узла потребуется около 30 мс для процесса узла. –

+0

Кроме того, я понял, что OP не хочет зависеть от узла, установленного в системе – cviejo

Смежные вопросы