I'd like to introduce dsmq, the Dead Simple Message Queue, to the world.

Part mail room, part bulletin board, dsmq is a central location for sharing messages between processes, even when they are running on different computers.

Its defining characteristic is bare-bones simplicity.


        A screenshot of the dsmq GitHub repository:
        src/dsmq,
        .python-version,
        LICENSE,
        README.md,
        pyproject.toml,
        README,
        MIT license,
        Dead Simple Message Queue
        What it does
        Part mail room, part bulletin board, dsmq is a central location
        for sharing messages between processes, even when they are running
        on computers scattered around the world.
        Its defining characteristic is bare-bones simplicity.

What dsmq does

A message queue lets different processes talk to each other, within or across machines. Message queues are a waystation, a place to publish messages and hold them until they get picked up by the process that needs them.

In dsmq, a program running the message queue starts up first (the server). It handles all the receiving, delivering, sorting, and storing of messages.

Other programs (the clients) connect to the server. They add messages to a queue or read messages from a queue. Each queue is a separate topic.

Why message queues?

Message queues are invaluable for distributed systems of all sorts, but my favorite application is robotics. Robots typically have several (or many) processes doing different things at different speeds. Communication between processes is a fundamental part of any moderately complex automated system. When ROS (the Robot Operating System) was released, one of the big gifts it gave to robot builders was a reliable way to pass messages.

Why another message queue?

There are lots of message queues in the world, and some are quite well known—Amazon SQS, RabbitMQ, Apache Kafka to name a few. It's fair to ask why this one was created.

The official reason for dsmq's existence is that all the other available options are pretty heavy. Take RabbitMQ for instance, a popular open source message queue. It has hundreds of associated repositories and it's core rabbitmq-server repo has many thousands of lines of code. It's a heavy lift to import this to support a small robotics project, and code running on small edge devices may struggle to run it at all.

RabbitMQ is also mature and optimized and dockerized and multi-platform and fully featured and a lot of other things a robot doesn't need. It would be out of balance to use it for a small project.

dsmq is only about 200 lines of Python, including client and server code. It's tiny. Good for reading and understanding front-to-back when you're integrating it with your project.

But the real reason is that I wanted to understand how a message queue might work and the best way I know to learn this is to build one.

How to use dsmq

Install it

pip install dsmq

or

uv add dsmq

Spin up a dsmq server

from dsmq import dsmq
dsmq.start_server(host="127.0.0.1", port=12345)

Connect a client to a dsmq server

mq = dsmq.connect_to_server(host="127.0.0.1", port=12345)

Add a message to a topic queue

topic = "greetings"
msg = "hello world!"
mq.put(topic, msg)

Read a message from a topic queue

topic = "greetings"
msg = mq.get(topic)

Run a demo

  1. Open 3 separate terminal windows.
  2. In the first, run src/dsmq/dsmq.py.
  3. In the second, run src/dsmq/example_put_client.py.
  4. In the third, run src/dsmq/example_get_client.py.

Alternatively, if you're on Linux just run src/dsmq/demo_linux.py. (Linux has some process forking capabilities that Windows doesn't and that macOS forbids. It makes for a nice self-contained multiprocess demo.)

How it works

sqlite_conn = sqlite3.connect("file:mem1?mode=memory&cache=shared")
for i_retry in range(_n_retries):
    try:
        cursor.execute("""...  
self.dsmq_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.dsmq_conn.connect((host, port)) 
Thread(target=_handle_client_connection, args=(socket_conn,)).start()
msg_str = "<no response>"
response = None
while response != "":
    msg_str = response
    response = self.mq.get("<topic>")
topic = "update"
msg_dict = {"timestep": 374, "value": 3.897}
msg_str = json.dumps(msg_dict)
dsmq.put(topic, msg_str)

msg_str = dsmq.get(topic)
msg_dict = json.loads(msg_str)
_message_length_offset = 1_000_000
_header_length = 23
_n_retries = 5
_first_retry = 0.01  # seconds
_time_to_live = 600.0  # seconds 

Dead Simple

Dead simple is an aesthetic. It says that the ideal is achieved not when nothing more can be added, but when nothing more can be taken away. It is the aims to follow the apocryphal advice of Albert Einstein, to make a thing as simple as possible, but no simpler.

Dead simple is like keystroke golfing, but instead of minimizing the number of characters, it minimizes the number of concepts a developer or a user has to hold in their head.

I've tried to embody it in dsmq. dsmq has fewer lines of code than RabbitMQ has repositories. And that tickles me.