feat/#63 - add iterator instead of RabbitMQ callback
Unit test coverage / pytest (pull_request) Failing after 1m49s
Build and Publish Docker Image / build-and-push (push) Successful in 2m29s
Unit test coverage / pytest (push) Failing after 1m45s
/ build-and-push (push) Successful in 1m52s
CI for pypl publish / publish-lib (push) Successful in 1m44s

This commit is contained in:
Stanislav Hejny
2025-07-27 09:50:25 +01:00
parent 66132db13a
commit 07f6f4569c
11 changed files with 174 additions and 59 deletions
+21
View File
@@ -0,0 +1,21 @@
# ====================================================================
# CleverMicro AMQ Adapter settings
# ====================================================================
[CleverMicro-AMQ]
cm.amq-adapter.service-name=otdemo-2
cm.amq-adapter.generator-id=1
cm.amq-adapter.route-mapping=otdemo-2:otdemo-exchange:otdemo-queue-2:otdemo-2.#:5:0
cm.amq-adapter.require-authenticated-user=false
cm.amq-adapter.callbacks-enabled=False
# CleverMicro AMQ Adapter dispatcher, settings related to RabbitMQ connection and optional REST forwarding
cm.dispatch.use-dlq=true
cm.dispatch.amq-host=localhost
cm.dispatch.amq-port=5672
cm.dispatch.amq-port-tls=5671
cm.dispatch.download-dir=/tmp/downloaded_files
cm.backpressure.threshold=1
cm.backpressure.time-window=150
cm.backpressure.cpu-overload-duration=300
cm.backpressure.cpu-idle-duration=300
+5 -28
View File
@@ -2,21 +2,11 @@
# CleverMicro AMQ Adapter settings
# ====================================================================
[CleverMicro-AMQ]
# CleverMicro AMQ Adapter settings that identify this AMQ Adapter instance to other adapters
# A name to identify this service for the AMQ Adapter
cm.amq-adapter.service-name=otdemo
# The CleverMicro AMQ Adapter instance ID, used to identify this instance in the CleverMicro AMQ Adapter cluster
cm.amq-adapter.generator-id=1
# Route mapping. For route detail description see section 3.1 in Onboarding Guide for Python:
# https://docs.cleverthis.com/en/architecture/microservices/onboarding/python
# NOTE: this is a comma-separated list of route mappings (multiple, comma-separated routes are allowed here)
# cm-dev route
cm.amq-adapter.route-mapping=otdemo:otdemo-exchange:otdemo-queue-1:api.cm.dev.cleverthis.localhost.test.python.frontend.#:5:0,otdemo-2:otdemo-exchange:otdemo-queue-2:otdemo-2.#:5:0
# local route
#cm.amq-adapter.route-mapping=otdemo::otdemo:api.dev.localhost.otdemo.#:5:0
# Indicate if AMQ Adapter should respond with HTTP 401 Unauthorized if the request does not contain valid JWT token
cm.amq-adapter.route-mapping=otdemo:otdemo-exchange:otdemo-queue-1:api.cm.dev.cleverthis.localhost.test.python.frontend.#:5:0
cm.amq-adapter.require-authenticated-user=false
cm.amq-adapter.callbacks-enabled=True
# CleverMicro AMQ Adapter dispatcher, settings related to RabbitMQ connection and optional REST forwarding
cm.dispatch.use-dlq=true
@@ -24,21 +14,8 @@ cm.dispatch.amq-host=localhost
cm.dispatch.amq-port=5672
cm.dispatch.amq-port-tls=5671
cm.dispatch.download-dir=/tmp/downloaded_files
# Not used for CleveSwarm nor CleverBRAG or in tight coupling mode.
# applicable only in loose coupling mode, specify the destination where to forward the REST request
#cm.dispatch.service-host=cleverthis-service-name.localhost
#cm.dispatch.service-port=8080
# Backpressure monitor settings
# Define maximum number of messages of being processed in parallel before triggering backpressure OVERLOAD alert
cm.backpressure.threshold=1
# Define maximum CPU usage (%) before triggering backpressure OVERLOAD alert
cm.backpressure.threshold-cpu-overload=90
# Define maximum CPU usage (%) before triggering backpressure IDLE alert
cm.backpressure.threshold-cpu-idle=10
# Define the time window between the Backpressure reports, in seconds
cm.backpressure.time-window=15
# Define the time window length, in seconds, during which, if CPU usage is consistently above the threshold, the system will trigger OVERLOAD alert
cm.backpressure.cpu-overload-duration=30
# Define the time window length, in seconds, during which, if no request is made to the Service, the system will trigger IDLE alert
cm.backpressure.cpu-idle-duration=30
cm.backpressure.time-window=150
cm.backpressure.cpu-overload-duration=300
cm.backpressure.cpu-idle-duration=300
+43 -15
View File
@@ -1,3 +1,10 @@
import asyncio
from amqp.config.amq_configuration import AMQConfiguration
from amqp.model.model import AMQRoute, DataMessage
from otdemo.otdemo_adapter import OTDemoAmqpAdapter
def print_document_id_static(document_id: str) -> str:
print(f"STATIC Document ID: {document_id}")
return "STATIC PRINTED!"
@@ -5,21 +12,42 @@ def print_document_id_static(document_id: str) -> str:
class Backend:
def __init__(self, name: str, description: str):
self.name = name
self.description = description
def __repr__(self):
return f"Backend(name={self.name}, description={self.description})"
def __str__(self):
return f"{self.name}: {self.description}"
def __eq__(self, other):
if not isinstance(other, Backend):
return False
return self.name == other.name and self.description == other.description
def print_document_id(self, document_id: str) -> str:
print(f"CLASS Document ID: {document_id}")
return "CLASS PRINTED!"
async def processing_loop():
while (
not otadapter.amq_service.future.done()
): # Wait until AMQ adapter initializes RabbitMQ connection
await asyncio.sleep(0.1)
# IMPORTANT - in the next line the string arg must match the first token in route-mapping property
route: AMQRoute = otadapter.amq_service.router.find_route_by_service("otdemo-2")
generator = otadapter.amq_service.data_message_generator(route) # this is a generator function
print(f"Data message generator created: {generator}")
message = await generator.asend(None) # First value must be None to start the generator
while True:
print(f"Received message: {message}")
if message is None:
break
data_message: DataMessage = message
# This is a message processing part. You may use data_message.method() or .domain() or .path()
# to access values provided by caller in the send command() call.
document_id = data_message._base64body.decode("utf-8")
print(
f"DATA_MESSAGE Attributes: {data_message.method()} {data_message.domain()} {data_message.path()}"
)
if data_message.path() == "print_document_id_static":
print_document_id_static(document_id)
elif data_message.path() == "print_document_id":
backend = Backend()
backend.print_document_id(document_id)
message = await generator.asend(True)
if __name__ == "__main__":
otadapter = OTDemoAmqpAdapter(AMQConfiguration("otdemo.backend.properties"), [])
# Run the processing loop in the AMQ service event loop
asyncio.run_coroutine_threadsafe(processing_loop(), otadapter.amq_service.loop)
otadapter.thread.join() # if the code above is not running a loop, need to join the thread to keep program running