2016-06-15 9 views
5

Ich habe kleine, aber CPU schwere App in Alpha-Phase in node.js, es ist ein kleines Spiel. Ich stoße auf Leistungsprobleme und ich muss es mindestens um den Faktor 20 beschleunigen, um zur Beta zu kommen. Und da die parallele Ausführung mich sehr weit bringen würde, entschied ich, dass es ein guter Anfang wäre, die Spielkarte zwischen Prozessen oder Threads zu teilen, die parallele Operationen darauf ausführen würden. Das ist ziemlich unmöglich in Node zu tun, also habe ich beschlossen, die fleischigen Teile in CL (SBCL + Linux) zu schreiben und eine Verbindung über Unix-Domain-Socket herzustellen.Verbinden Node.js als Client zu einem Common Lisp-Server

Der Plan ist:

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

Der Punkt ist, ich schnell Nachrichten zwischen node.js und SBCL in einer Angelegenheit ähnlich wie Socket.io passieren muß.


Hier ist, was nicht funktioniert hat (Sie diesen Teil überspringen kann)

Auf Knoten Seite, ich kann nicht schlicht socket.io verwenden, weil es, nicht Unix Domain Sockets unterstützen aber net Modul funktioniert, also kann ich zumindest tun socket.write('raw data') - besser als nichts für jetzt.

Auf CL-Seite habe ich versucht, woo Webserver (es unterstützt lokale Sockets) und ich konnte eine Verbindung von Knoten und übergeben Rohdaten um, aber es sind alle unnötigen HTTP-Teile beteiligt und woo läuft immer als Server; Es wartet auf GET/HTTP/1.1 ..... Ich habe zuerst keine Möglichkeit gefunden, eine Nachricht von woo zu initiieren. Außerdem ist es völlig undokumentiert und unkommentiert und beinhaltet viele FF-Aufrufe an C-Bibliotheken, die mir überhaupt nicht bekannt sind.

Also ich ging durch mehrere CL-Webserver, die nicht kompiliert, nicht unterstützt Unix-Sockets, wurden aufgegeben oder undokumentiert, schließlich in einfache SB-BSD-Sockets und schließlich nach IOLIB bewegt, aber ich kann immer noch ' t es herausfinden.


iolib sah vielversprechend aus, aber ich kann nicht, um es von Knoten zu verbinden.

Ich habe das bekam:

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

und ich bin immer

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

Ich weiß nicht, ob ich auf, dass so etwas akzeptieren-Verbindung oder hören nennen sollten Steckdose zuerst. Alles was ich versuchte, führte zu Fehlern. Auch wenn ich [RETRY-SYSCALL] in repl, verschwindet der Fehler für ca. 10 Sekunden aber kommt zurück. In dieser Zeit kann der Knoten immer noch keine Verbindung herstellen.

Das scheint komplizierter zu werden, als ich dachte. Ich habe schon ~ 6 Stunden Arbeit an iolib allein verloren, und ich habe nicht angefangen, auch auf die Nachrichten parsen, zu lernen, wie aus ihnen Ereignisse zu schaffen, zwischen JSON Konvertierung und s-EXPS etc ..


Meine Fragen sind:

  • , wie ich diese Verbindung in iolib gesetzt haben, so dass Knotens net herstellen kann?
  • Vorausgesetzt, ich kann wählen, welche Art von Verbindung wäre am besten für die Weitergabe von Ereignissen/Nachrichten geeignet? (Datagramm/Stream)
  • Gibt es einige Arbeitswerkzeuge, die ich nicht versucht habe?
  • Gibt es auch andere libs als iolib, die vielleicht besser/besser dokumentiert sind?
  • Gibt es bessere/leichtere/schnellere Ansätze für dieses Leistungs-/Nebenläufigkeitsproblem?
  • Irgendwelche anderen Ideen?

Ich bin in der Nähe, nur die Idee von CL Notwasserung und stattdessen so etwas wie In-Memory-Mongo mit mehreren Knoten Prozesse zu verwenden (..es ist nicht wirklich schnell Ton), aber ich liebe Lisp Es wäre großartig, Dinge wie lparallel im Backend zu haben. Ich habe mich seit gestern Morgen keinen Zentimeter mehr bewegt, ich kann die Bibliotheken einfach nicht herausfinden. Vielleicht sollte ich stattdessen clojure lernen.

PS: Ich würde normalerweise nicht nach "schreibe mir den Code" fragen, aber wenn eine gute Seele da ist, würde ich es wirklich schätzen, sogar im Pseudocode.

PPS: Jede radikal andere Ansätze sind ebenfalls willkommen. Bitte sprechen Sie Ihre Meinung :)

Danke fürs Lesen!

+2

Sie haben viele Fragen in diesen Fragen, einige im Zusammenhang mit Lisp, andere allgemeiner (wählen Sie TCP vs. UDP), so könnte es ein wenig zu breit sein. Auf der anderen Seite ist Ihre Frage interessant. Zögern Sie nicht, beim nächsten Mal fokussiertere Fragen zu veröffentlichen, wenn Sie brauchen. Es gibt Bibliotheken für die asynchrone Kommunikation (cl-async), JSON (cl-json, yason) usw., die stabil genug sind. Es könnte besser sein, jedes Problem einzeln nacheinander anzugehen. Viel Glück. – coredump

Antwort

2

Also am Ende, dachte ich es aus ...

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

Ich wechselte zu :type :stream und die meisten Probleme verschwunden. accept-connection muss am Socket angerufen werden, aber listen-to muss nicht. Ich musste einen Weg finden, Nachrichten selbst zu trennen, aber es war viel einfacher als ich dachte. Aus irgendeinem Grund, :type :datagram hat einfach nicht funktioniert, ich weiß nicht warum.

Und in Knoten:

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

So funktioniert dies, sonst falls jemand zusammen einige ähnliche Huhn Ideen der Verwendung von Lisp und Knoten hat.

5

Wenn ich Ihr Problem richtig verstehe, müssen Sie einen Server in Common Lisp einrichten. Lassen Sie mich eine frühere answer of mine wiederzuverwenden, die USOCKET verwendet:

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