Feat #58 - fix backpressure logic and update tests
This commit is contained in:
@@ -14,8 +14,9 @@ from amqp.adapter.data_message_factory import DataMessageFactory
|
||||
from amqp.adapter.logging_utils import logging_info, logging_warning
|
||||
from amqp.adapter.service_message_factory import ServiceMessageFactory
|
||||
from amqp.config.amq_configuration import AMQConfiguration
|
||||
from amqp.model.model import DataMessage
|
||||
from amqp.model.model import AMQRoute, DataMessage
|
||||
from amqp.rabbitmq.rabbit_mq_client import RabbitMQClient
|
||||
from amqp.router.router_consumer import RouterConsumer
|
||||
from amqp.service.amq_message_handler import DataMessageHandler
|
||||
from amqp.service.tracing import TracingConfig, initialize_telemetry
|
||||
|
||||
@@ -45,7 +46,8 @@ class AMQService:
|
||||
self.data_message_factory = DataMessageFactory.get_instance(
|
||||
self.amq_configuration.amq_adapter.generator_id
|
||||
)
|
||||
self.rabbit_mq_client = RabbitMQClient(self.amq_configuration)
|
||||
self.router = RouterConsumer(self.amq_configuration)
|
||||
self.rabbit_mq_client = RabbitMQClient(self.amq_configuration, self.router)
|
||||
self.tracer = initialize_telemetry(
|
||||
tracing_config=TracingConfig(
|
||||
service_name=amq_configuration.amq_adapter.service_name,
|
||||
@@ -57,8 +59,17 @@ class AMQService:
|
||||
self.amq_configuration.amq_adapter.service_name
|
||||
)
|
||||
self.backpressure_thread: Optional[Thread] = None
|
||||
self.backpressure_handler: Optional[BackpressureHandler] = None
|
||||
|
||||
# =======================================================================
|
||||
# =======================================================================
|
||||
# ====================== A P I M e t h o d s =========================
|
||||
# =======================================================================
|
||||
# =======================================================================
|
||||
def run(self):
|
||||
"""
|
||||
Start the AMQ service, initializing RabbitMQ connection and message handlers.
|
||||
"""
|
||||
# self.init(loop) will initialize RabbitMQ connection
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.loop.run_until_complete(self.init(self.loop, self.service_adapter))
|
||||
@@ -68,9 +79,57 @@ class AMQService:
|
||||
logging.info("***********************************************")
|
||||
self.loop.run_forever()
|
||||
|
||||
def backpressure(self, current: int, maximum: int = -1):
|
||||
"""
|
||||
Send Backpressure event to the Management Service. The Backpressure logic determines the type of the event
|
||||
based on the current and maximum values.
|
||||
- OVERLOAD event is triggered immediately when the current value exceeds the maximum threshold, which is
|
||||
typically set to 80% of the maximum value.
|
||||
- IDLE event is triggered when the current value drops below 20% of the maximum value and remains there for
|
||||
a IDLE_WINDOW period of time (see BackpressureHandler).
|
||||
- UPDATE event is sent if neither OVERLOAD nor IDLE conditions are met, indicating that the backpressure is
|
||||
within acceptable limits.
|
||||
:param current: Current value of the backpressure metric.
|
||||
:param maximum: Maximum value of the backpressure metric. If undefined or set to lt 0, the maximum is set
|
||||
according to the configuration value 'cm.backpressure.threshold'.
|
||||
"""
|
||||
if self.backpressure_handler is not None:
|
||||
if maximum < 0:
|
||||
maximum = self.amq_configuration.backpressure.threshold_threads
|
||||
self.backpressure_handler.update_backpressure_value(current, maximum)
|
||||
|
||||
def rpc(self, service_name: str, fq_method_name: str, **kwargs) -> Future:
|
||||
"""
|
||||
Send a Remote Procedure Call (RPC) message to the AMQ service.
|
||||
:param service_name: The service name to receive the RPC message.
|
||||
:param fq_method_name: The fully qualified method name to execute the RPC message.
|
||||
:param kwargs: Additional keyword arguments to be passed to the RPC method.
|
||||
:return: A Future that resolves when the RPC response is received.
|
||||
"""
|
||||
route: Optional[AMQRoute] = self.router.find_route_by_service(service_name)
|
||||
if route is not None:
|
||||
message: DataMessage = self.data_message_factory.create_rpc_message(
|
||||
fq_method_name=fq_method_name,
|
||||
swarm_id=self.amq_configuration.amq_adapter.swarm_task_id,
|
||||
**kwargs,
|
||||
)
|
||||
result: Future = self.amq_data_message_handler.send_rpc(route, message)
|
||||
self.set_outstanding_future(message, result)
|
||||
return result
|
||||
|
||||
result = asyncio.Future()
|
||||
result.set_exception(
|
||||
NameError(f"No route exists. Service '{service_name}' not found in routing table.")
|
||||
)
|
||||
return result
|
||||
|
||||
# =======================================================================
|
||||
# ====================== Internal Methods ============================
|
||||
# =======================================================================
|
||||
|
||||
async def init(self, loop, service_adapter):
|
||||
_reply_to_exchange: AbstractRobustExchange = await self.rabbit_mq_client.init(loop)
|
||||
backpressure_handler = BackpressureHandler(
|
||||
self.backpressure_handler = BackpressureHandler(
|
||||
channel=self.rabbit_mq_client.channel,
|
||||
loop=loop,
|
||||
config=self.amq_configuration,
|
||||
@@ -84,11 +143,12 @@ class AMQService:
|
||||
self.rabbit_mq_client,
|
||||
self.amq_configuration,
|
||||
loop=loop,
|
||||
backpressure_handler=backpressure_handler,
|
||||
backpressure_handler=self.backpressure_handler,
|
||||
)
|
||||
await self.register_routes()
|
||||
await self.rabbit_mq_client.request_routes()
|
||||
self.backpressure_thread = backpressure_handler.start_backpressure_monitor()
|
||||
logging_info("Starting backpressure monitor thread")
|
||||
self.backpressure_thread = self.backpressure_handler.start_backpressure_monitor()
|
||||
|
||||
def cleanup(self):
|
||||
logging_info("AMQService.cleanup() rmqc=%s", self.rabbit_mq_client)
|
||||
@@ -122,7 +182,7 @@ class AMQService:
|
||||
)
|
||||
return None
|
||||
|
||||
def send_message(self, message: DataMessage, future: Future):
|
||||
def set_outstanding_future(self, message: DataMessage, future: Future):
|
||||
self.amq_data_message_handler.outstanding[str(message.id())] = future
|
||||
|
||||
def remove_outstanding_future(self, message_id: str):
|
||||
|
||||
Reference in New Issue
Block a user