As modern software is exploding (especially in the cloud and at the edge) the need to deliver realtime data, updates, and events at scale across deployed instances is ever increasing.
A challenge we had to solve for our own fullstack authorization as a service solution (authorizon.com)
That's why we've created ⚡ FastAPI-websocket-RPC and 🗞️ FastAPI-websocket-PubSub.
Often you'd like to trigger events and/or access code on the client side (and not just on the server). Explicitly exposed RPC methods are the easiest way to go about it.
Each side (both client and server) can expose Python methods like this:
class RpcCalculator(RpcMethodsBase): async def add(self, a, b): return a + b
And the other-side can call them (and wait for a response with a return value) like this:
response = await channel.other.add(a=1,b=2) print(response.result) # 3
Similarly Pub/Sub is the easiest way to have different services share updates.
Once a server is up:
app = FastAPI() endpoint = PubSubEndpoint() endpoint.register_route(app, "/pubsub")
clients can then subscribe
async with PubSubClient(server_uri="ws://localhost/pubsub") as client: # Callback to be called upon event being published on server async def on_event(data): print("We got an event! with data- ", data) # Subscribe for the event client.subscribe("my_event_topic", on_event)
async with PubSubClient(server_uri="ws://localhost/pubsub") as client: endpoint.publish(["my_event_topic"], data=["my", "data", 1])
With ease 😇
Polling systems are the worst - constantly creating overhead, and are always delayed and suffering from consistency issues. Websockets are ideal for traversing the cloud and the internet. Thanks to running on top of an initially outgoing HTTP connection, all the common issues one would face with firewalls and routing for most bi-directional channels are inherently solved. When push comes to shove - software should always push rather than pull 😜.
With Python, asyncio, Starlette, and Pydantic at its core, FastAPI provides great performance (still not as good as GO, but comparable/superior to Node.js) and enjoys the dev-speed and maintainability of typed Python. Its dependency injection mechanism allows our RPC and PubSub to easily enjoy all the benefits of the underlying HTTP (Such as authorization, cookies, routing) with easy configuration.
While we haven't created official benchmarks yet, we have successfully run these packages in production with 100s of events per second, per server instance, with no issue. In addition instances can scale horizontally with ease (thanks to the broadcasting mechanism).
First, RabbitMQ is great - but it's not a cure-all;
FastAPI WS Pub/Sub allows to decouple the backbone Pub/Sub (such as RabbitMQ, Kafka, or Redis) from the API layer - which is more lightweight. Bringing tons of benefits:
- Allowing to scale-out the lightweight layer without having to scale-out the backbone (which is often much more resource intensive)
- Being able to work with the native API (e.g. REST, GraphQL) in tandem (being part of the same framework or even process)
- Choosing whichever backbone Pub/Sub channel you'd like, and being able to easily switch (no lock-in).
- Code level control - this solution embeds into your Python code, you don't have to look at it as a blackbox (unless you want to).