Or see the list of project sponsors.
Documentation | 🥺 |
Docstrings | 😀 |
Tests | 🥺 |
Examples | 😀 |
RepositoryActivity | 😀 |
CI | 🥺 |
ZeroMQ is a networking library. It is not a message broker and it will not run tasks for you. Instead, it provides simple primitives for different network patterns.
With ZeroMQ you can easily implement these patterns: Request-Response, Pub-Sub, Push-Pull.
I found 3 CL systems implementing bindings to ZeroMQ:
I know, names of the repositories, CL systems and packages are all different. That is the HELL :(
There is also at least two different versions of the zmq
:
zmq
because msg-data-as-is
function is absent.Anyway, both of them are stale and didn't get updates 7-8 years. They are using the old 3.2 version of ZeroMQ. Today we'll talk about pzmq
.
PZMQ has some activity in the repository and uses ZeroMQ 4. It does not have docs but it has some examples, ported from the ZeroMQ Guide.
I slightly modified the examples code, to make the output more readable when client and server are running from one REPL.
This snippet shows the server's code. It listens on the 5555 port and blocks until a message received, then responds and waits for another message:
POFTHEDAY> (defun hwserver (&optional (listen-address "tcp://*:5555"))
(pzmq:with-context nil ; use *default-context*
(pzmq:with-socket responder :rep
(pzmq:bind responder listen-address)
(loop
(write-line "SERVER: Waiting for a request... ")
(format t "SERVER: Received ~A~%"
(pzmq:recv-string responder))
(sleep 1)
(pzmq:send responder "World")))))
The client does the opposite - it sends some data and waits for the response. Depending on the pattern you use, you have to set socket types. For the server, we used :rep (reply) and for client we are using :req (request).
POFTHEDAY> (defun hwclient (&optional (server-address "tcp://localhost:5555"))
(pzmq:with-context (ctx :max-sockets 10)
(pzmq:with-socket (requester ctx) (:req :affinity 3 :linger 100)
;; linger is important in case of (keyboard) interrupt;
;; see http://api.zeromq.org/3-3:zmq-ctx-destroy
(write-line "CLIENT: Connecting to hello world server...")
(pzmq:connect requester server-address)
(dotimes (i 3)
(format t "CLIENT: Sending Hello ~d...~%" i)
(pzmq:send requester "Hello")
(write-string "CLIENT: Receiving... ")
(write-line (pzmq:recv-string requester))))))
Here is what we'll see when running the server in the background and starting the client in the REPL:
POFTHEDAY> (defparameter *server-thread*
(bt:make-thread #'hwserver))
SERVER: Waiting for a request...
POFTHEDAY> (hwclient)
CLIENT: Connecting to hello world server...
CLIENT: Sending Hello 0...
CLIENT: Receiving... Hello
SERVER: Waiting for a request... World
CLIENT: Sending Hello 1...
CLIENT: Receiving... Hello
SERVER: Waiting for a request... World
CLIENT: Sending Hello 2...
CLIENT: Receiving... Hello
SERVER: Waiting for a request... World
NIL
What is next?
Read about Pub-Sub and Push-Pull patterns at the ZeroMQ Guide and try to port them on pzmq
.
Also, it would be cool to port all Common Lisp examples from the unsupported library to the pzmq
and to send a pull-request.
By the way, there is at least one cool project, which already uses pzmq
to connect parts written in Common Lisp and Python. It is recently reviewed common-lisp-jupyter library.
To conclude, this library definitely should be tried if you are going to implement a distributed application! Especially if it will interop with parts written in other languages than Common Lisp.