2016-06-15 37 views
5

Ho una piccola ma pesante app per CPU in alpha stage in node.js, è un gioco di piccole dimensioni. Sto incontrando problemi di prestazioni e ho bisogno di accelerarlo di almeno un fattore di 20 per arrivare alla versione beta. E poiché l'esecuzione parallela mi avrebbe portato molto lontano, ho deciso che sarebbe stato un buon inizio condividere la mappa di gioco tra processi o thread che avrebbero eseguito operazioni parallele su di essa. Questo è piuttosto impossibile da fare nel nodo, così ho deciso di scrivere le parti carnose in CL (SBCL + Linux) e collegarmi ad esso tramite un unix dominio socket.Connetti Node.js come client a un server Common Lisp

Il piano è:

[players] <-> (node.js front server) <-> (SBCL performing game ticks) 

Il punto è, ho bisogno di passare i messaggi veloci tra node.js e SBCL in una questione simile a socket.io.


Ecco cosa non ha funzionato (si può saltare questa parte)

Sul lato nodo, non posso utilizzare socket.io pianura perché non supporta Unix Domain Socket, ma il modulo net sì, quindi posso almeno fare socket.write('raw data') - meglio di niente per ora.

Sul lato CL, ho provato a eseguire woo web server (supporta socket locali) e potevo connettermi dal nodo e passare i dati grezzi in giro, ma ci sono tutte le parti HTTP non necessarie coinvolte e woo è sempre in esecuzione come server; sta aspettando il GET/HTTP/1.1 ..... Non ho trovato il modo di avviare realmente un messaggio da woo prima. Inoltre, è totalmente non documentato e non commentato e coinvolge molte chiamate FF alle librerie C, che non conosco affatto.

Così ho passato molti altri server web CL che non sono stati compilati, non supporta socket unix, sono stati abbandonati o non documentati, eventualmente spostati in semplici sb-bsd-socket e infine in iolib, ma posso ancora ' lo capisco.


iolib sembrava promettente, ma non in grado di connettersi ad esso dal nodo.

ho questo:

(with-open-socket (socket :address-family :local 
          :type :datagram 
          ;; :connect :passive 
          :local-filename "/tmp/socket") 

    (format t "Socket created") 
    ;; (listen-on socket) 
    ;; (bind-address socket (make-address "/tmp/socket")) 
    (loop 
    (let ((msg (receive-from socket :size 20))) 
     (format t msg))))  

e sto ottenendo

#<Syscall "recvfrom" signalled error EWOULDBLOCK(11) "Resource temporarily unavailable" FD=6> 
    [Condition of type IOLIB/SYSCALLS:EWOULDBLOCK] 

Restarts: 
0: [IGNORE-SYSCALL-ERROR] Ignore this socket condition 
1: [RETRY-SYSCALL] Try to receive data again 
2: [RETRY] Retry SLIME interactive evaluation request. 
3: [*ABORT] Return to SLIME's top level. 
4: [ABORT] abort thread (#<THREAD "worker" RUNNING {10055169A3}>) 

Non so se devo chiamare qualcosa come accettare connessione o ascoltare-to su quel presa prima. Tutto ciò che ho provato ha causato errori. Inoltre, se I [RETRY-SYSCALL] in repl, l'errore scompare per circa 10 secondi ma torna indietro. In questo momento, il nodo non riesce ancora a connettersi.

Questo sembra essere più complicato di quanto pensassi. Ho già perso ~ 6 ore di lavoro su iolib da solo e non ho nemmeno iniziare il parsing dei messaggi, imparare a creare gli eventi da loro, la conversione tra JSON e s-EXPS ecc ..


Le mie domande sono:

  • come faccio a configurare questa connessione in modo iolib netta può collegarsi quel nodo?
  • Supponendo che io possa scegliere, quale tipo di connessione sarebbe più adatta per trasmettere eventi/messaggi? (datagramma/stream)
  • Ci sono alcuni strumenti di lavoro che non ho provato?
  • Inoltre, ci sono altre librerie di iolib forse più di alto livello/meglio documentate?
  • Esistono approcci migliori/più facili/più rapidi a questo problema di prestazioni/concorrenza?
  • Altre idee?

Sono vicino a quasi abbandonando l'idea di CL e di usare qualcosa di simile in memoria mongo con diversi processi di nodo invece (..è in realtà non suona veloce), ma amo Lisp , sarebbe bello avere cose come lparallel sul backend. Non mi sono spostato di un pollice da ieri mattina, non riesco a capire le libs. Forse dovrei invece imparare il clojure.

PS: Normalmente non chiederei "scrivimi il codice", ma se c'è qualcosa di buono, lo apprezzerei molto, anche in pseudocodice.

PPS: sono benvenuti anche approcci radicalmente diversi. Per favore, parla chiaro :)

Grazie per la lettura!

+2

Hai molte domande in queste domande, alcune relative a Lisp, altre più generali (scegli TCP vs UDP), quindi potrebbe essere un po 'troppo ampio. D'altra parte, la tua domanda è interessante. Non esitate a postare domande più mirate la prossima volta, se necessario. Esistono librerie per la comunicazione asincrona (cl-async), JSON (cl-json, yason) ecc. Che sono abbastanza stabili. Potrebbe essere meglio affrontare ogni problema separatamente, uno dopo l'altro. In bocca al lupo. – coredump

risposta

2

Così, alla fine, ho capito ...

(with-open-socket (socket :address-family :local 
          :type :stream 
          :connect :passive 
          :local-filename "/tmp/node-sbcl.sock") 

    (log-message :info "Waiting for client...") 
    (setf *client* (accept-connection socket :wait t)) 
    (log-message :info "Accepted client's connection.") 

    ;; ...lunch with *client* + the bits for parsing json and separating messages... 
) 

sono passato a :type :stream e la maggior parte dei problemi sono scomparsi. accept-connection deve essere chiamato sul socket, ma non lo è listen-to. Ho dovuto scrivere un modo per separare i messaggi da solo, ma è stato molto più facile di quanto pensassi. Per qualche ragione, :type :datagram non ha funzionato, non so perché.

E nel nodo:

var JsonSocket = require('json-socket'); 
var net = require('net'); 
var sbcl = new JsonSocket(net.Socket()); 


sbcl.connect("/tmp/node-sbcl.sock"); 
sbcl.on('connect', function(){ 
    console.log('Connected to SBCL, YAY!'); 

    console.log('Sending hi!'); 

    sbcl.sendMessage({'cmd': "heyThere"}); 

    sbcl.on('message', function(message){ 
     if(!message.cmd) { 
      console.log("We've received msg from SBCL with no CMD!!!'"); 
      return; 
     } 

     switch(message.cmd){ 
     case 'heyNode': console.log('SBCL says hi...'); break; 
     } 
    }); 
}); 

Quindi questo funziona, nel caso in cui qualcun altro ha alcune idee di pollo simili utilizzando LISP e nodo insieme.

5

Se ho capito correttamente il problema, è necessario stabilire un server in Common Lisp. Mi permetta di riutilizzare una precedente answer of mine che utilizza USOCKET:

(use-package :usocket) 

(defun some-server (hostname port)  
    ;; create a server which establishes an event-loop 
    (socket-server hostname ; a string 
       port  ; a number 

       ;; tcp-handler (see API for details) 
       ;; This function is called each time a client connects, 
       ;; and provides a bidirectional stream to communicate with it. 
       ;; The function executes in a context where some useful special 
       ;; variables are bound. 
       ;; The connection is closed automatically on exit. 

       (lambda (stream) 
        ;; format to stream to client 
        (format stream 
          "~D~%" 
          ;; add all elements of the host, 
          ;; a vector of 4 integers 
          (reduce #'+ *remote-host*)))))