FastAPI Microservice Patterns: Asynchronous communication
By: Florian Kromer
Tags: Python, Skaffold, Fastapi, Design Patterns, Microservices

FastAPI Microservice Patterns: Asynchronous communication

What’s the problem?

In microservice based applications the business logic is spread across several services. To solve business problems the services need to communicate with each other. But how do services in a microservice architecture communicate?

Solution alternatives

For inter-service communication one can choose one of the following alternatives:

The remote procedure invocation pattern and messaging pattern are heavily used. When one uses the messaging pattern dependent on the technology and protocol configuration it may be required to consider the idempotent consumer pattern (Problem which is solved: How does a message consumer handle duplicate messages correctly?) as well. When to use what pattern and what advantages and disadvantages the patterns have is not in the scope of this post. To learn more about this I highly recommend to read the chapter Interprocess communication in a microservice architecture from the book Microservice Patterns: With examples in Java.

One solution: Messaging pattern

This post is about the messaging pattern which can be implemented in various ways. In this post the pattern is not implemented in combination with the idempotent consumer pattern which, as already mentioned earlier solves the problem of how to handle the reception of duplicate messages. The communication is either 1-to-1 or 1-to-n, means messages published by a publisher service on a single channel can be received by one or several consumer services. Independent of the technology used like e.g. MQTT or Apache Kafka the common concept is a message broker which acts as intermediary for messages published by publisher services and consumed by consumer services.

In addition to forwarding the messages between a message publisher and all consumers which have subscribed messages from the corresponding message topic the message broker handles other things as well. The messages are buffered by the broker until they have been transmitted to the consumers. The message brokers may support different behaviors w.r.t. this message buffering and forwarding. MQTT e.g. defines different quality of service (QoS) levels. Dependent on how the broker is configured a message is transmitted at at most once (0), at least once (1) or exactly once (2).

The at most once configuration is the least reliable on cause one would have to detect missing messages. Of course missing messages can be detected but there is no guarantee that all messages are published and obviously no way to restore missing messages. In this configuration the implementation of the idempotent consumer pattern would not make sense.

The at least once configuration prevents from missing messages but leads potentially to duplicate messages which have be handled with an implementation of the idempotent consumer pattern.

The exactly once configuration guarantees that every message is delivered (published as well as received) exactly once. There is neither a need to implement a detection for missing messages nor to implement the idempotent consumer pattern.

Pattern implementation

The example implementation of the messaging pattern used MQTT as messaging technology. The package used for integrating MQTT functionality into FastAPI is fastapi-mqtt. The package uses the package gmqtt under the hood to provide asynchronous MQTT client functionality. The MQTT client functionality could be implemented by using another Python MQTT client library like e.g. the Eclipse Paho MQTT Python Client as well. I’ve chosen fastapi-mqtt cause it abstracts away some boilerplate code and keeps the examples as minimalist as possible.

The post FastAPI Microservice Patterns: Local Development Environment — Skaffold, docker, kubectl and minikube in a nutshell describes how to setup the local development environment and how to get the source code of the pattern implementations. The sub directory messaging of the source code repository contains the example implementation of the pattern. The logic does not make sense but makes it obvious how the pattern works. To follow the explanations install a MQTT client like e.g MQTT-Explorer.

Eclipse Mosquitto is used as MQTT broker and deployed with Kubernetes The file mqtt-broker/deployment.yaml contains it’s deployment meta-data. skaffold.yaml refers the manifest:

...

deploy:

kubectl:

manifests:

- mqtt-broker/deployment.yaml

...

A service-b acts as a message publisher. A service-b acts as a message publisher as well as a message subscriber. Both services setup and tear-down the connection to the message broker which is accessible as mqtt-broker in the corresponding decorated functions startup() and shutdown() :

from fastapi_mqtt import FastMQTT, MQQTConfig ...

mqtt_config = MQQTConfig(host = "mqtt-broker") mqtt = FastMQTT(

config=mqtt_config

) ... @app.on_event(“startup”)

async def startup():

await mqtt.connection() @app.on_event(“shutdown”)

async def shutdown():

await mqtt.client.disconnect()

service-a ( service-a/app/main.py ) provides a REST endpoint /send/{payload} which publishes a user specified payload on MQTT topic /from-service-a :

@app.post(“/send/{payload}”)

async def request_answer(payload: str):

await mqtt.publish(“/from-service-a”, payload)

service-b ( service-b/app/main.py ) subscribes to messages on topic /from-service-a

@mqtt.on_connect()

def connect(client, flags, rc, properties):

mqtt.client.subscribe(“/from-service-a”)

and defines a generic message handler which is executed whenever a message is received

@mqtt.on_message()

async def message(client, topic, payload, qos, properties):

await mqtt.publish(“/from-service-b”, f”{payload} forwarded by service-b”)

In this example service-b simply republishes the message on topic /from-service-b .

Run the services with minikube start followed by skaffold dev --port-forward . Run MQTT Explorer and connect to the local MQTT broker by inserting the meta-data and pushing button “Connect”.

MQTT Explorer connection setup

After connecting to the broker you should see something like below:

MQTT Explorer