2016-06-15 44 views
5

Mam małą, ale ciężką aplikację CPU w fazie alfa w node.js, jest to mała gra. Występują problemy z wydajnością i muszę przyspieszyć to co najmniej 20 razy, aby przejść do wersji beta. A ponieważ równoległe wykonanie doprowadziłoby mnie bardzo daleko, zdecydowałem, że dobrym początkiem będzie udostępnienie mapy gry pomiędzy procesami lub wątkami, które będą wykonywać na niej operacje równoległe. W węźle nie jest to możliwe, więc postanowiłem napisać mięsiste części w CL (SBCL + Linux) i połączyć się z nim poprzez gniazdo domeny unix.Połącz Node.js jako klienta z serwerem Common Lisp

Plan jest taki:

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

Chodzi o to, muszę zdać szybkich komunikatów pomiędzy node.js i SBCL w materii podobnej do socket.io.


Oto co nie działa (można pominąć tę część)

Na stronie Node, nie mogę używać zwykłego socket.io ponieważ nie obsługuje Unix domeny gniazda, ale moduł net, więc mogę przynajmniej zrobić socket.write('raw data') - lepiej niż nic na razie.

Po stronie CL, próbowałem uruchomić woo web server (obsługuje on lokalne gniazda) i mogłem połączyć się z nim z węzła i przekazać surowe dane dookoła, ale są wszystkie niepotrzebne części HTTP zaangażowane i woo zawsze działa jako serwer; to czeka na GET/HTTP/1.1 ..... Nie znalazłem sposobu, aby zainicjować wiadomość od pierwszego. Ponadto, jest to całkowicie nieudokumentowane i nieczytelne i wymaga wielu wywołań FF do bibliotek C, których w ogóle nie znam.

Więc przeszedłem przez kolejne serwery CL, które się nie skompilowały, nie obsługiwały gniazd Uniksa, zostały porzucone lub nieudokumentowane, ostatecznie przeniesione do zwykłych gniazd sb-bsd, a na końcu do iolib, ale nadal mogę " t wymyślić.


iolib wyglądał obiecująco, ale nie mogę się z nim połączyć z węzła.

mam to:

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

i Dostaję

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

nie wiem, czy powinienem nazwać coś takiego zaakceptować gniazdkiem lub słuchać na tym, aby najpierw gniazdo. Wszystkie próby spowodowały błędy. Ponadto, jeśli I [RETRY-SYSCALL] w repl, błąd zniknie na około 10 sekund, ale wraca. W tym czasie węzeł nadal nie może się połączyć.

To wydaje się być bardziej skomplikowane, niż myślałem. Ja już straciłem ~ 6 godzin pracy na iolib sam i nawet nie rozpocznie się parsowania wiadomości, uczenie się, jak tworzyć wydarzenia z nich, konwersja między JSON i s-EXPS etc ..


Moje pytania są:

  • w jaki sposób ustawić to połączenie w iolib tak że węzeł na netto można podłączyć?
  • Zakładając, że mogę wybrać, jaki typ połączenia najlepiej nadaje się do przekazywania zdarzeń/wiadomości? (datagram/strumień)
  • Czy są jakieś narzędzia robocze, których nie próbowałem?
  • Czy są też inne biblioteki niż iolib, które są prawdopodobnie bardziej zaawansowane/lepiej udokumentowane?
  • Czy są jakieś lepsze/łatwiejsze/szybsze podejścia do tego problemu wydajności/współbieżności?
  • Jakieś inne pomysły?

jestem blisko tak wodowania ideę CL i użyć coś w pamięci Mongo z kilku procesów węzłów zamiast (..To nie brzmią naprawdę szybko), ale uwielbiam LISP Byłoby wspaniale mieć takie rzeczy, jak na przykład na zapleczu. Po wczorajszym poranku nie poruszyłem się o cal, po prostu nie mogę wymyślić bibliotek. Być może powinienem nauczyć się clojure.

PS: Normalnie nie prosiłbym o "napisz mi kodu", ale jeśli w pobliżu jest jakaś dobra dusza, byłbym bardzo wdzięczny, nawet w pseudokodach.

PPS: Wszelkie radykalnie różne podejścia są również mile widziane. Proszę, zabierz głos!

Dzięki za przeczytanie!

+2

Masz wiele pytań w tym pytaniu, niektóre związane z Lisp, inne bardziej ogólne (wybierz TCP vs. UDP), więc może być trochę zbyt szeroki. Z drugiej strony twoje pytanie jest interesujące. Jeśli zajdzie taka potrzeba, nie wahaj się, aby w przyszłości umieścić bardziej szczegółowe pytania. Istnieją biblioteki do komunikacji asynchronicznej (cl-async), JSON (cl-json, yason) itp., Które są wystarczająco stabilne. Lepiej byłoby rozwiązać każdy problem osobno, jeden po drugim. Powodzenia. – coredump

Odpowiedz

2

Więc w końcu, I zdobione ...

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

przeszedłem do :type :stream i większość problemów zniknął. accept-connection musi być wywołany na gnieździe, ale listen-to nie może. Musiałem napisać osobny sposób na oddzielne wiadomości, ale było to znacznie łatwiejsze niż myślałem. Z jakiegoś powodu, :type :datagram po prostu nie działa, nie wiem dlaczego.

A w węźle:

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

Tak to działa, w przypadku gdy ktoś inny ma jakieś podobne pomysły kurczaka korzystania LISP i węzeł razem.

5

Jeśli dobrze rozumiem Twój problem, musisz ustanowić serwer w Common Lisp. Pozwól mi ponowne poprzedni answer of mine który wykorzystuje 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*)))))