Typed Python Kafka Manager - easy publish/listen to kafka topics embraced with strong types
Project description
Kavari
Easy, automated Kafka publish/subscription with strong types
This tool is to make usage of kafka super easy and safe,
utilizing best practices and power given by confluent_kafka
While it may be fast & fun to use weak types in Python for rapid development, that when it comes to expose any data outside the app, it is very reasonable to structure the message in predictable manner - providing stable contract of exposed data structure for external consumers. Also, because modern applications usually are hosted in a cloud - there is a necessity to implement additional readiness for host migration/failover scenarios, when pod manager can move host from one physical place to another. That results with specific situations when:
- kafka might not achievable temporarily
- consumers might be taken down in the middle of processing a message(s)
- rebalancing partitions
All of these adds a lot of code for the basic implementation for the final product.
This small library covers these, additionally providing some simplicity flavor on top.
Publishing message
Create a message type, that defines the payload (our strong typed message format)
class TestKafkaMessage(KafkaMessage):
topic = "test_topic"
def __init__(self, payload: str):
super().__init__()
self.payload: str = payload
def get_partition_key(self) -> str:
return "1"
And then publish it on the topic, just by calling:
msg: TestKafkaMessage = TestKafkaMessage("test_message")
kafka_manager.publish_message(msg, lambda msg, ex: print("Message published"))
Easy? I hope so! Now let's consume this message
Consuming message
Define the handler class
@kafka_message_handler(message_cls=TestKafkaMessage)
class TestKafkaMessageConsumer(KafkaMessageConsumer):
def __init__(self):
self.received_message: str | None = None
def handle(self, message_data: str) -> None:
self.received_message = message_data
That's (almost) it! Once consumer become available via provider (any DI for example) each message is handled out of the box in separated thread, to isolate background kafka messaging processing from other part of the application (e.g. REST API)
I hope you like the concept!
To achieve full power of this lib, you need to configure it
Configuration
Install it via:
- PIP:
pip install kavari - Poetry:
poetry add kavari
Create a kafka_manager (example below is for a DI container, but you can use it without it)
from kavari import kavari_create, FibonacciRetryPolicy, KafkaManager
class Container(DeclarativeContainer):
kafka_manager: Singleton[KafkaManager] = Singleton(
lambda: kavari_create(
bootstrap_servers="bootstrap_location:2973",
group_id="unique_group_identifier",
publishing_retry_policy=FibonacciRetryPolicy(max_attempts=10),
logger=logger,
auto_commit=False,
auto_offset_reset="earliest"
)
)
There are 3 necessary steps to finish the configuration:
- Configure message consumers provider:
Provider is a method that will deliver initialized instance of message consumer when specific key is given.
The example manual consumer provider will look like follows:def consumer_provider(key: typing.Any) -> kavari.KafkaMessageConsumer: if key == MyFirstMessageConsumer.__class__: return MyFirstMessageConsumer()
This interface is prepared to be compatible withdependency_injector.Container.resolvemethod. - Start the message consumers loop when application starts.
- Stop the message consumers loop just before the application go down.
For instance (FastAPI + dependency_injector):
@asynccontextmanager
async def lifespan(app: FastAPI):
# this part is called on application start
container.logger().info("Initiating startup & background jobs")
# consumer provider is called to get particular type of the consumer, making
# the autoresolve feature working out of the box
container.kafka_manager().set_consumer_provider(container.resolve)
container.kafka_manager().start_consumer_loop()
yield
# this part is called when application is tearing down
container.logger().info("Stopping background jobs")
container.kafka_manager().stop_consumer_loop()
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kavari-0.2.1.tar.gz.
File metadata
- Download URL: kavari-0.2.1.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
645d34e1b994ea7100cd0ef38059e52efb9f8863e94a9d53cf05860a248f7b9d
|
|
| MD5 |
9e40120f958f98c5167fede41bb41e41
|
|
| BLAKE2b-256 |
82fa20ccc5e08f3a9de82ba17b5b5b6aafcd4921339315cd11371cf0f751a022
|
File details
Details for the file kavari-0.2.1-py3-none-any.whl.
File metadata
- Download URL: kavari-0.2.1-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
210737e80a660fac566f877357cd0f5c0e117d1ab27c6909451b472d692621b5
|
|
| MD5 |
5bbeb610de10d7b1ce1d943576641e39
|
|
| BLAKE2b-256 |
2c19f31d8373ad5ab01871b0e5a8218a1dc1defa8e14468841b3d614142dd6b3
|