2015-09-16 12 views
18

Sto scrivendo una libreria Java, in realtà, una libreria Clojure, ma per questa domanda, ciò che conta è che funzioni sulla JVM. Questa libreria ha bisogno di eseguire alcuni JavaScript. I tried with Nashorn ma incontro alcune limitazioni che potrebbero essere troppo difficili da superare. In alternativa, voglio provare NodeJS.Come devo eseguire NodeJS da un'applicazione Java?

Voglio che la mia libreria sia autonoma, non dipenda dal sistema che esegue NodeJS in modo indipendente e quindi richiede un particolare meccanismo di distribuzione per collocare le risorse Java e NodeJS nelle posizioni giuste per essere prelevate dai due diversi server di rete . Questo approccio, tuttavia, porta alcuni problemi.

Parlerò con NodeJS su HTTP ma non voglio che NodeJS apra una porta specifica. Voglio trovare uno inutilizzato casuale quindi non ci sono collisioni. Voglio anche controllare dove vanno i log di NodeJS, in modo da conservarli con il resto della mia applicazione. Infine, la mia app dovrebbe essere in grado di rilevare quando NodeJS si arresta in modo anomalo e rieseguirlo o segnalare un errore con le informazioni.

Qual è il modo migliore per avvicinarsi a questo? Esistono librerie Java per aiutare a gestire il processo figlio in questo modo? Qualunque cosa in particolare dovrei fare dal lato NodeJS (sono molto nuovo a NodeJS, non l'ho mai usato prima).

+0

Suppongo che dipenda dal modo in cui la libreria è destinata a essere consumata, ma disporre di una libreria per avviare un processo server pesante per eseguire il proprio lavoro sembra un odore di codice. –

+0

Hai sentito parlare di avatar? Ho anche avuto alcuni problemi in cui non ho potuto utilizzare alcuni moduli nodejs, ma l'avatar ha colmato il vuoto: https://strongloop.com/strongblog/how-to-run-node-js-on-the-jvm-with-avatar- js-and-loopback/ – Tiago

+0

Solo per limitare un po 'la risposta ... quali sono le limitazioni che hai incontrato esattamente? Lo scripting lato server con nashorn aggiunge 6 ms alla media. tempo di risposta nel tuo post alla fine, quindi suppongo che la performance non sia una di quelle – cviejo

risposta

9

La mia soluzione, alla fine, è stato quello di utilizzare ProcessBuilder come questo:

(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)) 

e quindi chiamare avviare in esso. inheritIO fa in modo che l'output di esso passi all'output del processo corrente che unisce efficacemente stdout e stderr.

In cima a quello NodeJS apre una porta casuale specificando 0 come numero di porta e lo scrive in un file:

(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))))))))) 

che poi si apre con il lato Java (in attesa che appaia):

+0

Che ne dici di http://nodyn.io/? – Stanimir

+0

@Pablo Pensavo che notare che Node.js in ascolto su 0 potrebbe non funzionare sempre come previsto. Vedi [qui] (http://stackoverflow.com/a/10266225/4133078) –

+0

@Stanimir Non sono riuscito a capire quanto sia completo nodyn.io, non sembra molto completo e il progetto è ufficialmente abbandonato. – Pablo

6

C'è una bella risposta here su come eseguire javascript in java. Qualcosa del genere sarebbe fattibile per il tuo caso? Se no qui ci sono alcune risorse:

  • Random port in nodejs si potrebbe colpire ancora un altro servizio per trovare una porta aperta durante la costruzione, o che hanno il nodo applicazione sparare una richiesta HTTP al server Java basato sulla porta che afferra.
  • Winston è la migliore libreria di registrazione che ho trovato, non dovresti avere problemi ad accedere allo stesso percorso.
  • Forever e PM2 sono i process manager di nodo comune che mantengono il nodo in esecuzione. Al momento preferisco per sempre (non so perché)

Sembra che userete molta CPU nel nodo. Se questo è il caso, probabilmente vorrai usare lo cluster module (in modo che nodejs possa utilizzare più core). Se si blocca il ciclo degli eventi (quale sarà l'elaborazione basata sulla CPU, allora sarà possibile eseguire solo 1 richiesta concorrente per processo forato).

+0

A proposito di quell'altra domanda che hai indicato, no, ho già provato Nashorn e non è abbastanza. Ho bisogno di qualcosa come NodeJS. – Pablo

4

Nashorn ha alcuni problemi che ho incontrato, come trovare informazioni su alcune delle loro API (la documentazione lascia molto a desiderare) e l'avvio lento.

Quello che mi sento di raccomandare invece: trattare il vostro rendering lato server come un servizio di e non un processo bambino.

In altre parole, è possibile eseguire un'istanza Node.js sulla rete interna su dire porta 10015 e consentire solo connessioni locali ad esso (è inoltre possibile inviare registri ovunque si desideri). Quindi puoi fare una richiesta al servizio con le pagine che vuoi rendere, come ad esempio localhost:10015/posts/ e avere l'app Node.js che rende la pagina all'interno di un browser senza testa (usando qualcosa come Phantom o Slimer).

per mantenere il vostro server di nodo, è possibile utilizzare Forever o supervisord, e per aiutarvi a ottenere la trazione più veloce, si potrebbe guardare a ciò che il team di Prerender ha fatto: https://github.com/prerender/prerender-node

4

Sono stato in una posizione simile dove ho dovuto eseguire fortran da uno script python, quindi ecco i miei due centesimi. Eseguire lo script node.js con un comando da terminale in Java in questo modo:

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); 
} 

Con l'installazione è possibile passare parametri al vostro programma di nodo e ottenere risposte. Anche se non sono sicuro di cosa stai usando il tuo programma node.js, quindi non sono sicuro che sia utile.

+0

Una cosa da notare con questo metodo è che ci vorranno circa 30ms per avviare un processo del nodo con ogni chiamata. –

+0

Inoltre, ho capito che l'OP non vuole dipendere dal fatto che il nodo sia installato sul sistema – cviejo