将Node.js作为客户端连接到Common Lisp服务器

enr*_*rey 6 events common-lisp unix-socket node.js

我在node.js的alpha阶段有一个小但CPU重的应用程序,这是一个小游戏.我遇到了性能问题,我需要将其加速至少20倍才能达到测试版.而且由于并行执行会让我走得很远,所以我认为良好的开端是在进程和线程之间共享游戏映射,以便在其上执行并行操作.这在节点中是不可能的,所以我决定在CL(SBCL + Linux)中编写多余的部分并通过unix域套接字连接到它.

计划是:

[players] <-> (node.js front server) <-> (SBCL performing game ticks)
Run Code Online (Sandbox Code Playgroud)

关键是,我需要在类似于socket.io的事情中在node.js和SBCL之间传递快速消息.


这是什么不起作用(你可以跳过这部分)

在节点方面,我不能使用普通的socket.io,因为它不支持Unix域套接字,但是net模块确实如此,所以我至少可以做socket.write('raw data')- 现在好于什么.

在CL方面,我试图运行woo web服务器(它支持本地套接字),我可以从节点连接到它并传递原始数据,但是涉及所有不必要的HTTP部分,而且woo总是作为服务器运行; 它在等待GET / HTTP/1.1 .....我没有找到一种方法来首先从woo实际发起消息.此外,它完全没有文档和未注释,并涉及到很多对C库的FF调用,我对此并不熟悉.

所以我经历了几个没有编译的CL网络服务器,不支持unix套接字,被放弃或未记录,最终转移到普通的sb-bsd-socket,最后转移到iolib,但我仍然无法弄明白.


iolib看起来很有前途,但我无法从节点连接到它.

我有这个:

(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))))    
Run Code Online (Sandbox Code Playgroud)

而且我正在

#<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}>)
Run Code Online (Sandbox Code Playgroud)

我不知道我是否应该首先在该套接字上调用accept-connection或listen-to之类的东西.我尝试过的都导致了错误.另外,如果我[RETRY-SYSCALL]在repl中,错误消失了大约10秒但是又回来了.在这个时候,节点仍然无法连接.

这似乎比我想象的要复杂得多.我已经在iolib上独自完成了大约6个小时的工作,我甚至没有开始解析消息,学习如何从它们创建事件,在JSON和s-exps等之间进行转换.


我的问题是:

  • 如何在iolib中设置此连接,以便节点的网络可以连接?
  • 假设我可以选择哪种类型的连接最适合传递事件/消息?(数据报/流)
  • 我有没有尝试过一些工具?
  • 此外,还有一些其他的库而不是iolib可能更高级/更好地记录吗?
  • 是否有更好/更容易/更快的方法来解决此性能/并发问题?
  • 还有其他想法吗?

我接近于抛弃CL的想法,并使用内存mongo之类的东西来代替几个节点进程(...实际上听起来不是很快)但是我喜欢lisp,有点像lparallel这样的东西会很棒后端.我从昨天早上起就没有动过一英寸,我只是无法弄清楚libs.也许我应该学习clojure.

PS:我通常不会要求"给我写代码",但如果有一些好的灵魂,我会非常感激,即使是伪代码.

PPS:也欢迎任何完全不同的方法.请大声说出来:)

谢谢阅读!

cor*_*ump 5

如果我正确理解您的问题,您需要在Common Lisp中建立一个服务器.让我重用我之前使用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*)))))
Run Code Online (Sandbox Code Playgroud)


enr*_*rey 2

所以最后我想通了...

(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...
  )
Run Code Online (Sandbox Code Playgroud)

我切换到后:type :stream,大多数问题都消失了。accept-connection必须在套接字上调用,但listen-to不能。我必须自己编写一种方法来分隔消息,但这比我想象的要容易得多。由于某种原因,:type :datagram只是不起作用,我不知道为什么。

并在节点中:

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;
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

所以这是可行的,以防其他人有一些类似的将 lisp 和 Node 一起使用的鸡鸡想法。