Merge in RR/pyinfra from pika_encapsulation to master
Squashed commit of the following:
commit 251fea4094062a72f5f0b1f8a54f959a1d7309ec
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 15:02:25 2022 +0100
Adjusted readme and config to the actual changes.
commit d00a0aadc02be8ab1343dbaa2a8df82418e77673
Merge: ded29bb c34a1ce
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 14:06:21 2022 +0100
Merge branch 'master' of ssh://git.iqser.com:2222/rr/pyinfra into pika_encapsulation
commit ded29bb8d0a78c1d5fd172edb74f0b120da05d5f
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 14:02:37 2022 +0100
blackkkkkkkkkkkkkkkk
commit de3899c69f4e83fa5b8dc2702a18be66661201e9
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 13:59:43 2022 +0100
Judgement Day - The Great Refactoring
commit 48eb2bb792dd45539e7db20949fedae03316c2b3
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 13:30:41 2022 +0100
blacky
commit 38fb073291c6989fcc5560ef9f97e7dafd9570be
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 13:29:46 2022 +0100
update mock client/publish
commit 8744cdeb6d21adab81fb82c7850a4ada31a1a3f9
Author: Julius Unverfehrt <julius.unverfehrt@iqser.com>
Date: Tue Mar 15 13:29:19 2022 +0100
quickfix consumer.consume_and_publish bug
commit 0ce30cafbf15142e3f773d0e510b7d22927ee86d
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Tue Mar 15 11:42:04 2022 +0100
updated serving logic for new queue, visior and storage logic
commit 5ac5fbf50bf9d8a6b6466cd04d82d21525250b4c
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Tue Mar 15 11:41:18 2022 +0100
changed callback signature to expect a dict with a key 'data'
commit 4c7c4b466e2b5228a6098c9130cd6c3334315153
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Tue Mar 15 09:46:39 2022 +0100
set dead letter queue related params
commit f8b60aad16eb0f5c91bed857ac3b352c4bcb7468
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 20:22:57 2022 +0100
added republishing logic
commit a40f07139af9121d8b72efa52b6b2588e7d233e8
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 19:10:08 2022 +0100
removed obsolete import
commit d3605b0913f8b155961b8c8e90e7c1d442a1da4f
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 19:09:50 2022 +0100
removed obsolete code
commit 9130b9fc753c63bfe4abd3cc26b31caf5c5bf2cb
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 19:05:59 2022 +0100
test refac
commit 1205adecdbd42a46dcc264a46086aab94c285b94
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 14:49:59 2022 +0100
removed obsolete import
commit 59d0de97e39646b3fa6c6ca91644dbf8adbd0de3
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 14:49:21 2022 +0100
applied black
commit f78a6894bc99b42d53a7c265ffd8c7869e9d606d
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 14:49:03 2022 +0100
applied black
commit 32740a914f8be15eed954389f6ddbb565e1567e6
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 14:47:47 2022 +0100
removed obsolete code
commit f21cbc141efc10d0e779f683dcc452e6c889dd6b
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 14:47:27 2022 +0100
consumer with visitor test fixed (added connection.close() in case of pika queue manager)
commit c415dcd8e6375e015b959a9c3cdee61b8ffe7d60
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Mon Mar 14 13:56:54 2022 +0100
consumer with visitor test WIP 2
... and 15 more commits
143 lines
4.4 KiB
Python
143 lines
4.4 KiB
Python
import json
|
|
import logging
|
|
|
|
import pika
|
|
|
|
from pyinfra.config import CONFIG
|
|
from pyinfra.exceptions import ProcessingFailure
|
|
from pyinfra.queue.queue_manager.queue_manager import QueueHandle, QueueManager
|
|
|
|
logger = logging.getLogger("pika")
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logger.setLevel(CONFIG.service.logging_level)
|
|
|
|
|
|
def monkey_patch_queue_handle(channel, queue) -> QueueHandle:
|
|
|
|
empty_message = (None, None, None)
|
|
|
|
def is_empty_message(message):
|
|
return message == empty_message
|
|
|
|
queue_handle = QueueHandle()
|
|
queue_handle.empty = lambda: is_empty_message(channel.basic_get(queue))
|
|
|
|
def produce_items():
|
|
|
|
while True:
|
|
message = channel.basic_get(queue)
|
|
|
|
if is_empty_message(message):
|
|
break
|
|
|
|
method_frame, properties, body = message
|
|
channel.basic_ack(method_frame.delivery_tag)
|
|
yield json.loads(body)
|
|
|
|
queue_handle.to_list = lambda: list(produce_items())
|
|
|
|
return queue_handle
|
|
|
|
|
|
def get_connection():
|
|
|
|
credentials = pika.PlainCredentials(username=CONFIG.rabbitmq.user, password=CONFIG.rabbitmq.password)
|
|
|
|
kwargs = {"host": CONFIG.rabbitmq.host, "port": CONFIG.rabbitmq.port, "credentials": credentials}
|
|
|
|
parameters = pika.ConnectionParameters(**kwargs)
|
|
|
|
connection = pika.BlockingConnection(parameters=parameters)
|
|
|
|
return connection
|
|
|
|
|
|
def get_n_previous_attempts(props):
|
|
return 0 if props.headers is None else props.headers.get("x-retry-count", 0)
|
|
|
|
|
|
def attempts_remain(n_attempts, max_attempts):
|
|
return n_attempts < max_attempts
|
|
|
|
|
|
class PikaQueueManager(QueueManager):
|
|
def __init__(self, input_queue, output_queue, dead_letter_queue=None):
|
|
super().__init__(input_queue, output_queue)
|
|
|
|
self.connection = get_connection()
|
|
self.channel = self.connection.channel()
|
|
|
|
if not dead_letter_queue:
|
|
dead_letter_queue = CONFIG.rabbitmq.queues.dead_letter
|
|
|
|
args = {"x-dead-letter-exchange": "", "x-dead-letter-routing-key": dead_letter_queue}
|
|
|
|
self.channel.queue_declare(input_queue, arguments=args, auto_delete=False)
|
|
self.channel.queue_declare(output_queue, arguments=args, auto_delete=False)
|
|
|
|
def republish(self, body, n_current_attempts, frame):
|
|
self.channel.basic_publish(
|
|
exchange="",
|
|
routing_key=self._input_queue,
|
|
body=body,
|
|
properties=pika.BasicProperties(headers={"x-retry-count": n_current_attempts}),
|
|
)
|
|
self.channel.basic_ack(delivery_tag=frame.delivery_tag)
|
|
|
|
def publish_request(self, request):
|
|
logger.debug(f"Publishing {request}")
|
|
self.channel.basic_publish("", self._input_queue, json.dumps(request).encode())
|
|
|
|
def reject(self, body, frame):
|
|
logger.exception(f"Adding to dead letter queue: {body}")
|
|
self.channel.basic_reject(delivery_tag=frame.delivery_tag, requeue=False)
|
|
|
|
def publish_response(self, message, callback, max_attempts=3):
|
|
|
|
logger.debug(f"Publishing response for {message}.")
|
|
|
|
frame, properties, body = message
|
|
|
|
n_attempts = get_n_previous_attempts(properties) + 1
|
|
|
|
try:
|
|
response = json.dumps(callback(json.loads(body)))
|
|
self.channel.basic_publish("", self._output_queue, response.encode())
|
|
self.channel.basic_ack(frame.delivery_tag)
|
|
except ProcessingFailure:
|
|
|
|
logger.error(f"Message failed to process {n_attempts}/{max_attempts} times: {body}")
|
|
|
|
if attempts_remain(n_attempts, max_attempts):
|
|
self.republish(body, n_attempts, frame)
|
|
else:
|
|
self.reject(body, frame)
|
|
|
|
def pull_request(self):
|
|
return self.channel.basic_get(self._input_queue)
|
|
|
|
def consume(self):
|
|
logger.debug("Consuming")
|
|
return self.channel.consume(self._input_queue)
|
|
|
|
def consume_and_publish(self, visitor):
|
|
|
|
logger.info(f"Consuming with callback {visitor.callback.__name__}")
|
|
|
|
for message in self.consume():
|
|
self.publish_response(message, visitor)
|
|
|
|
def clear(self):
|
|
self.channel.queue_purge(self._input_queue)
|
|
self.channel.queue_purge(self._output_queue)
|
|
|
|
@property
|
|
def input_queue(self) -> QueueHandle:
|
|
return monkey_patch_queue_handle(self.channel, self._input_queue)
|
|
|
|
@property
|
|
def output_queue(self) -> QueueHandle:
|
|
return monkey_patch_queue_handle(self.channel, self._output_queue)
|