A python client for an AMQP-based PubSub system. Specifically designed to work with Django model updates, but can be used with anything.
The package can be installed from PyPI:
pip install pubsubpy
The underlying kombu library can be used with both librabbitmq
and the pure Python amqp
libraries, and will prefer librabbitmq
if present. We recommend librabbitmq
for production use if it is available for your platform. Other AMQP transports are also available and may work, but have not been tested. See here for more details on potential AMQP transports: http://docs.celeryproject.org/projects/kombu/en/latest/userguide/connections.html#amqp-transports
Subscribers and publishers are managed by the PubSub
class. Initialize a PubSub
object with an amqp_url and model_exchange name, and the object exposes three main methods for managing pubsub:
subscribe()
, a decorator around subscriber functions;drain()
, a method which will consume all registered queues and process with subscribers;publish_model_event()
, which will publish a new event to the exchange.
amqp_url
: The url of the AMQP exchange service.model_exchange
: The exchange name on the service for model events.
from pubsub import PubSub
if settings.PUBSUB_AMQP_URL and settings.PUBSUB_MODEL_EXCHANGE:
pubsub_app = PubSub(settings.PUBSUB_AMQP_URL, settings.PUBSUB_MODEL_EXCHANGE)
model_name
and event_name
are used to compose the queue name for this type of message, obj
is the arbitrary data describing the update.
model_name
: string of model class name, e.g.booking
event_name
: string event name being published, e.g.updated
obj
: dictionary payload describing the update, e.g.{ 'foo': 'bar' }
pubsub_app.publish_model_event('booking','saved', instance.to_service_model())
Use this decorator to register a function as a handler for model events described by topic
. It will be registered as a callback and used to process queue events whenever subscription processing occurs (triggered by a call to drain()
).
The decorated function should take two positional arguments, for instance:
@pubsub_app.subscribe('booking.*')
def process_booking_event(body, message):
...
topic
: string of the topic name, e.g.booking.*
,booking.updated
body
: dict which is the payload of the message, currently the format is{"object": obj}
whereobj
is the value of theobj
parameter passed into the call topublish_model_event()
that created this updatemessage
: the entire message object (headers, metadata, body, etc) from the underlyingkombu
library
In most cases, the body
argument should be sufficient to process events, as message acking is done by the pubsub library.
@pubsub_app.subscribe('booking.*')
def update_booking(body, message):
print(body, message)
Process all outstanding messages with all registered subscription callbacks. Continue pulling from the queues and processing messages until queue is empty. Intended to run asynchronously (e.g. in a periodic celery task) to clear the queues when desired.
none
As a periodic celery task:
from my_app.pubsub import pubsub_app
@scheduled(minute='*')
@app.task
def pubsub_listen():
pubsub_app.drain()
- Install
pyenv
via O/S package manager - Install target Python versions (i.e.
pyenv install 2.7.14
) - Create project virtualenv (using one of the target pythons)
- Install all requirements files (
pip install -r requirements.txt
, etc)
Once your local env is set up, you can run the testsuite with pytest
.
You can also run the tests on all supported Python configurations with tox
.
- Subscribers could unwittingly share a queue if a subscriber is contained in a library that is included in multiple different services. The queue key is not unique across services.