【问题标题】:Connect Node.js as client to a Common Lisp server将 Node.js 作为客户端连接到 Common Lisp 服务器
【发布时间】:2016-06-15 12:40:45
【问题描述】:

我在 node.js 的 alpha 阶段有一个小但 CPU 很重的应用程序,这是一个小游戏。我遇到了性能问题,我需要将其加速至少 20 倍才能进入测试版。而且由于并行执行会让我走得很远,我决定一个好的开始是在将对其执行并行操作的进程或线程之间共享游戏地图。这在 node 中几乎是不可能的,所以我决定在 CL (SBCL + Linux) 中编写内容丰富的部分,并通过 unix domain socket 连接到它。

计划是:

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

重点是,我需要在 node.js 和 SBCL 之间以类似于 socket.io 的方式快速传递消息。


以下是无效的(您可以跳过此部分)

在 Node 端,我不能使用普通的 socket.io,因为它不支持 Unix 域套接字,但 net 模块支持,所以我至少可以使用 socket.write('raw data') - 现在总比没有好。

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

所以我又经历了几个没有编译、不支持 unix 套接字、被废弃或未记录的 CL Web 服务器,最终转移到普通的 sb-bsd-sockets 并最终转移到 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))))    

我得到了

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

我不知道我是否应该首先在该套接字上调用诸如接受连接或侦听之类的东西。我尝试的所有方法都导致了错误。此外,如果我在 repl 中 [RETRY-SYSCALL],错误会消失大约 10 秒,但又会回来。在这个时候,节点仍然无法连接。

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


我的问题是

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

我几乎放弃了 CL 的想法,而是使用内存中的 mongo 之类的东西和几个节点进程(..听起来并不快),但我喜欢 lisp,在后端拥有像 lparallel 这样的东西会很棒。自昨天早上以来,我一英寸都没动,我就是想不通这些库。或许我应该学习 clojure。

PS:我通常不会要求“给我写代码”,但如果周围有好人,我会非常感激,即使是伪代码。

PPS:也欢迎任何完全不同的方法。请说出你的想法:)

感谢阅读!

【问题讨论】:

  • 你在这个问题中有很多问题,一些与 Lisp 有关,其他更笼统(选择 TCP 与 UDP),因此可能有点过于宽泛。另一方面,你的问题很有趣。如果您需要,请不要犹豫,下次发布更集中的问题。有足够稳定的异步通信库(cl-async)、JSON(cl-json、yason)等。一个接一个地单独解决每个问题可能会更好。祝你好运。

标签: node.js events common-lisp unix-socket


【解决方案1】:

如果我正确理解您的问题,您需要在 Common Lisp 中建立一个服务器。让我重用以前的answer of mine,它使用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*)))))

【讨论】:

    【解决方案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...
      )
    

    我切换到: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;
            }
        });
    });
    

    所以这行得通,以防其他人有一些类似的想法,即一起使用 lisp 和 node。

    【讨论】:

      猜你喜欢
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多