Protocol spec of MQKS - Message Queue Kept Simple. Client connects to server and sends one text line per request: {request_id} {action} {data} Server may respond from zero to many times for the same "request_id": {request_id} ok {data} When server detects an error, it logs "error_id" with details, and responds to client: {request_id} error {error_id} If any action gets optional "--confirm" flag as _first_ argument, it responds instantly with "ok" or "error". See "consume" example below. Unique "request_id"-s are used in role of "msg_id", "consumer_id", etc - to reduce complexity and increase performance. So this protocol requires: * Avoid space character in any IDs like {request_id}, {queue}, {event}. * You can use space characters inside {data} of "publish" and "ping" actions, inside {code} of "_eval" action. * Avoid newline and horizontal tabulation control characters in any IDs, {data} and {code}. * If you use JSON to serialize {data}, it will automatically replace these control characters with "\n" and "\t" respectively. Hello world ----------- server/mqksd & nc localhost 25000 Alice consume greetings hi hello Bob consume greetings hi hello Charlie consume greetings-and-byes hi hello bye good-bye Dave publish hello world Responses: (either Alice or Bob) ok Dave event=hello world Charlie ok Dave event=hello world Actions ------- Request: {msg_id} publish {event} {data} Example: m1 publish e1 d1 Responses: {none} Comment: Client publishes new message to server. Server puts copies of this message to zero or more queues that were subscribed to this event. Request: {consumer_id} consume {queue} [{event} ... {event}] [--add {event} ... {event}] [--delete-queue-when-unused[={seconds}]] [--manual-ack] Example: c1 consume --confirm q1 e1 e2 --delete-queue-when-unused=5 --manual-ack Responses: {consumer_id} ok {msg_id} event={event}[,retry={n}] {data} Example: c1 ok --update q1 e1 e2 --delete-queue-when-unused=5.0 --manual-ack c1 ok c1 ok m1 event=e1 d1 c1 ok m1 event=e1,retry=1 d1 Comment: Client starts consuming messages from queue. May replace subscriptions of the queue (if any) with new list of events. May add some events to existing subscriptions. Client may ask server to delete this queue when it is unused by consumers (for some seconds). When client disconnects, server deletes all consumers of this client. When client reconnects, client restarts all its consumers. Consumer gets --update on any rebind of its queue - to avoid old events on reconnect. If consumer with manual-ack disconnects, all not-acked messages are automatically rejected by server - returned to the queue. Request: {request_id} rebind {queue} [{event} ... {event}] [--remove {event} ... {event}] [--remove-mask {event_mask} ... {event_mask}] [--add {event} ... {event}] Example: rb1 rebind q1 e3 e4 e5.id1.a1 e5.id2.a2 rb2 rebind q1 --remove e3 --add e6 e7 rb3 rebind q1 --remove-mask e5.*.a1 e5.*.a2 --add e8 Comment: Replace subscriptions of the queue with new list of events, or remove some and add some other events or remove events by event masks. Event mask works only for events with dots "." and mask "*" can be applied to many chars before, after or between dots: *.updated == user.updated, comment.updated, ... document.* == document.created, document.removed, ... user.*.connected == user.123.connected, user.57e82b3931d9d614f0247ac7.connected, ... *.*.deleted == post.123.deleted, category.subcategory.deleted Request: {request_id} ack {consumer_id} {msg_id}|--all Example: a1 ack c1 m1 Responses: {none} Comment: Acknowledge this (or all) messages were processed by this consumer. Request: {request_id} reject {consumer_id} {msg_id}|--all Example: rj1 reject c1 m1 Responses: {none} Comment: Reject this (or all) messages - to return them to the queue with incremented "retry" counter. Request: {request_id} delete_consumer {consumer_id} Example: dc1 delete_consumer c1 Responses: {none} Comment: Delete the consumer. Server will not send messages to this consumer any more. Client will not restart this consumer on reconnect. Request: {request_id} delete_queue {queue} Example: dq1 delete_queue q1 Responses: {none} Comment: Delete the queue instantly. Server will not copy messages to this queue any more. Request: {request_id} ping {data} Example: pg1 ping service1 Responses: {request_id} ok {data} Example: pg1 ok service1 Comment: Ping-pong. Used by MQKS client for keep-alive. Request: {request_id} _eval [--worker={n}] {code} Example: ev1 _eval --worker=0 len(state.queues) Responses: {request_id} ok {data} Example: ev1 ok 42 Comment: Backdoor to get any stats. See "stats.py"