将 Node.js 作为客户端连接到 Common Lisp 服务器
Connect Node.js as client to a Common Lisp server
我在 node.js 的 alpha 阶段有一个小但 CPU 重的应用程序,这是一个小游戏。我正在 运行 解决性能问题,我需要将其速度至少提高 20 倍才能进入测试版。由于并行执行会让我走得更远,我决定最好的开始是在将对其执行并行操作的进程或线程之间共享游戏地图。这在节点中几乎是不可能的,所以我决定在 CL (SBCL + Linux) 中编写内容部分并通过 unix 域套接字连接到它。
计划是:
[players] <-> (node.js front server) <-> (SBCL performing game ticks)
重点是,我需要在node.js和SBCL之间传递快速消息,这与socket.io类似。
以下是无效的(你可以跳过这部分)
在 Node 端,我不能使用纯 socket.io 因为它不支持 Unix 域套接字,但是 net
模块支持,所以我至少可以 socket.write('raw data')
- 总比没有好。
在 CL 方面,我尝试 运行 请求 Web 服务器(它支持本地套接字)并且我可以从节点连接到它并传递原始数据,但是涉及所有不必要的 HTTP 部分并且woo 总是 运行ning 作为服务器;它正在等待 GET / HTTP/1.1 ....
。我没有找到一种方法来实际启动来自 woo 的消息。此外,它完全没有记录和注释,并且涉及大量对 C 库的 FF 调用,我一点也不熟悉。
所以我经历了更多的 CL Web 服务器,它们没有编译,不支持 unix 套接字,被遗弃或没有记录,最终转移到普通的 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}>)
我不知道是否应该先在该套接字上调用类似 accept-connection 或 listen-to 的方法。我所做的一切都导致了错误。另外,如果我在 repl 中 [RETRY-SYSCALL]
,错误会消失大约 10 秒,然后又会回来。这时候node还是连接不上。
这似乎比我想象的要复杂。我已经在 iolib 上浪费了大约 6 个小时的工作,我什至没有开始解析消息,学习如何从它们创建事件,在 JSON 和 s-exps 之间转换等。
我的问题是:
- 如何在 iolib 中设置此连接以便节点的 net 可以连接?
- 假设我可以选择,哪种类型的连接最适合传递 events/messages? (数据报/流)
- 是否有一些我没有尝试过的工作工具?
- 此外,除了 iolib 之外,还有其他一些库可能更高级/更好地记录了吗?
- 是否有 better/easier/faster 解决此性能/并发问题的方法?
- 还有其他想法吗?
我几乎要放弃 CL 的想法,而是使用内存中 mongo 之类的东西来代替多个节点进程(..它不会听起来真的很快)但我喜欢 lisp,如果后端有 lparallel 之类的东西会很棒。自从昨天早上以来我就没有动过一英寸,我就是想不通这些库。也许我应该学习 clojure。
PS:我通常不会要求 "write me teh code",但如果周围有好心人,我会非常感激,即使是伪代码。
PPS:也欢迎任何截然不同的方法。请说出你的想法:)
感谢阅读!
如果我对你的问题理解正确,你需要在Common Lisp中建立一个服务器。让我重复使用之前的 answer of mine which uses 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*)))))
所以最后我想通了...
(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。
我在 node.js 的 alpha 阶段有一个小但 CPU 重的应用程序,这是一个小游戏。我正在 运行 解决性能问题,我需要将其速度至少提高 20 倍才能进入测试版。由于并行执行会让我走得更远,我决定最好的开始是在将对其执行并行操作的进程或线程之间共享游戏地图。这在节点中几乎是不可能的,所以我决定在 CL (SBCL + Linux) 中编写内容部分并通过 unix 域套接字连接到它。
计划是:
[players] <-> (node.js front server) <-> (SBCL performing game ticks)
重点是,我需要在node.js和SBCL之间传递快速消息,这与socket.io类似。
以下是无效的(你可以跳过这部分)
在 Node 端,我不能使用纯 socket.io 因为它不支持 Unix 域套接字,但是 net
模块支持,所以我至少可以 socket.write('raw data')
- 总比没有好。
在 CL 方面,我尝试 运行 请求 Web 服务器(它支持本地套接字)并且我可以从节点连接到它并传递原始数据,但是涉及所有不必要的 HTTP 部分并且woo 总是 运行ning 作为服务器;它正在等待 GET / HTTP/1.1 ....
。我没有找到一种方法来实际启动来自 woo 的消息。此外,它完全没有记录和注释,并且涉及大量对 C 库的 FF 调用,我一点也不熟悉。
所以我经历了更多的 CL Web 服务器,它们没有编译,不支持 unix 套接字,被遗弃或没有记录,最终转移到普通的 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}>)
我不知道是否应该先在该套接字上调用类似 accept-connection 或 listen-to 的方法。我所做的一切都导致了错误。另外,如果我在 repl 中 [RETRY-SYSCALL]
,错误会消失大约 10 秒,然后又会回来。这时候node还是连接不上。
这似乎比我想象的要复杂。我已经在 iolib 上浪费了大约 6 个小时的工作,我什至没有开始解析消息,学习如何从它们创建事件,在 JSON 和 s-exps 之间转换等。
我的问题是:
- 如何在 iolib 中设置此连接以便节点的 net 可以连接?
- 假设我可以选择,哪种类型的连接最适合传递 events/messages? (数据报/流)
- 是否有一些我没有尝试过的工作工具?
- 此外,除了 iolib 之外,还有其他一些库可能更高级/更好地记录了吗?
- 是否有 better/easier/faster 解决此性能/并发问题的方法?
- 还有其他想法吗?
我几乎要放弃 CL 的想法,而是使用内存中 mongo 之类的东西来代替多个节点进程(..它不会听起来真的很快)但我喜欢 lisp,如果后端有 lparallel 之类的东西会很棒。自从昨天早上以来我就没有动过一英寸,我就是想不通这些库。也许我应该学习 clojure。
PS:我通常不会要求 "write me teh code",但如果周围有好心人,我会非常感激,即使是伪代码。
PPS:也欢迎任何截然不同的方法。请说出你的想法:)
感谢阅读!
如果我对你的问题理解正确,你需要在Common Lisp中建立一个服务器。让我重复使用之前的 answer of mine which uses 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*)))))
所以最后我想通了...
(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。