class Specialist(HospitalWorker): """ Receives and processes specialized requests. Every specialist must be able to process exactly two out of three injury types (hip / knee / elbow). """ def __init__(self, specializations): """ Initializes connection structures, binds to each queue corresponding to specializations. """ super().__init__() self._requests_consumer = Consumer('hospital', 'topic', 'localhost') for spec in specializations: self._requests_consumer.add_queue(queue_name=spec, routing_key='hosp.' + spec, callback=self.process_request) self._requests_consumer.start(new_thread=True) def process_request(self, ch, method, properties, body): """ Simulates processing injury examination by sleeping random number of seconds (between 1 and 5) and sending back 'results' message. """ body = body.decode() request_id = properties.correlation_id target = properties.reply_to log = colored( 'processing request: ' + body + ' (request id: ' + request_id[:8] + ')', 'green') print(log, end='', flush=True) time_to_sleep = randint(1, 5) for _ in range(time_to_sleep): print(colored('.', 'green'), end='', flush=True) sleep(1) print('') message_opts = { 'properties': pika.BasicProperties(correlation_id=request_id) } message = body + ' done' self._producer.send_message(routing_key=target, message=message, **message_opts) ch.basic_ack(delivery_tag=method.delivery_tag)
class Administrator: """ Receives and logs every message sent within the whole system. Is allowed to broadcast messages to all other workers. """ def __init__(self): self._log_consumer = Consumer('hospital', 'topic', 'localhost') self._log_queue = self._log_consumer.add_queue( routing_key='#', callback=self.process_log) self._log_consumer.start(new_thread=True) self._info_producer = Producer('info', 'fanout', 'localhost') def send_info(self, message): print('sending info: ', message) self._info_producer.send_message(message=message) def process_log(self, ch, method, properties, body): body = body.decode() log = colored('LOG: ' + body, 'yellow') print(log) ch.basic_ack(delivery_tag=method.delivery_tag)
class Medic(HospitalWorker): """ Creates and sends specialized examination requests and receives the results. Every request must be of one of following types: - hip examination - knee examination - elbow examination """ def __init__(self): """ Initializes all data structures that will be used for receiving processed examination requests. """ super().__init__() self._results_consumer = Consumer('hospital', 'topic', 'localhost') self._results_queue = self._results_consumer.add_queue( callback=self.process_results) self._results_consumer.start(new_thread=True) # Set of unique ids for every sent request # that hasn't been processed yet self._pending_requests = set() self._requests_lock = threading.Lock() def send_request(self, patient_name, injury_type): """ Creates new request message and sends it to proper injury queue. Adds unique request id to pending requests set. """ request_id = str(uuid4()) routing_key = 'hosp.' + injury_type message = patient_name + ' ' + injury_type message_opts = { 'properties': pika.BasicProperties(reply_to=self._results_queue, correlation_id=request_id) } with self._requests_lock: self._pending_requests.add(request_id) self._producer.send_message(routing_key, message, **message_opts) log = colored( 'sending request: ' + message + ' (request id: ' + request_id[:8] + ')', 'blue') print(log) def process_results(self, ch, method, properties, body): """ Removes request_id corresponding to received results from pending requests set. """ body = body.decode() ignore = False request_id = properties.correlation_id with self._requests_lock: if request_id in self._pending_requests: self._pending_requests.remove(request_id) else: ignore = True if not ignore: log = colored( 'received results: ' + body + ' (request id: ' + request_id[:8] + ')', 'green') print(log) ch.basic_ack(delivery_tag=method.delivery_tag)