def _call_service_method(self, info, args, kwargs): """ Calls method and returns the tuple with `Exception` instance or `None` and result or `None`. `info` can be tuple containing service name & method name or the actual callable method. """ try: if isinstance(info, tuple): service_name, fn_name = info fn = self._get_method(service_name, fn_name) else: fn_name = info.__name__ fn = info self._fire_hook('pre_call', fn_name, args, kwargs) result = (None, fn(*args, **kwargs)) args_str = ', '.join(map(repr, args)) kwargs_str = ', '.join( ['{}={}'.format(k, repr(v)) for k, v in kwargs.items()]) args_kwargs_str = ', '.join((args_str, kwargs_str)) log.debug('{}({})'.format(fn_name, args_kwargs_str)) self._fire_hook('post_success', fn_name, args, kwargs, result) return result except Exception as e: self._log_method_error(fn_name, e) self._fire_hook('post_error', fn_name, args, kwargs, e) return (str(e), None)
def _create_fanout_exchange(self, Consumer, channel): """ Creates a fanout queue to accept notifications. """ exchange_name = '{}_fanout'.format(self.exchange) log.debug('Declaring fanout exchange %s', exchange_name) exchange = kombu.Exchange(name=exchange_name, channel=channel, durable=False, type='fanout') exchange.declare() queue_name = 'fanout_callback_{}'.format(uuid.uuid4()) log.debug('Declaring fanout queue %s', queue_name) queue = kombu.Queue(name=queue_name, exchange=exchange, exclusive=True, durable=False, channel=channel) queue.declare() consumer = Consumer( # self.connection, queues=[queue], on_message=self._on_broadcast, no_ack=True # no_ack=True ) return consumer
def _create_service_queues(self, services, Consumer, channel): """ Creates necessary AMQP queues, one per service. """ log.debug('Declaring exchange %s', self.exchange) exchange = kombu.Exchange(self.exchange, channel=channel, durable=False) exchange.declare() queues = [] for service in services.values(): queue_name = '{}_service_{}'.format(self.exchange, service.name) log.debug('Declaring service queue %s', queue_name) queue = kombu.Queue( channel=channel, name=queue_name, exchange=exchange, routing_key=queue_name, exclusive=False, durable=False, ) queue.declare() queues.append(queue) consumer = Consumer(queues=queues, on_message=self._on_message, no_ack=False) return consumer
def _run_scheduled_with_local_timer(self, fn, timeout): """ Runs the method and reschedules it to be executed again. """ fn_name = fn.__name__ exception, _ = self._call_service_method(fn, (), {}) if not exception: log.debug('Scheduled function %s completed successfully.', fn_name) self._schedule_with_local_timer(fn, timeout)
def _validate_message(self, message): """ Checks and acknowledges a received message if it can be handled by any registered service. """ try: service_name = message.delivery_info['routing_key'][ self.queue_name_offset:] if service_name not in self.services: # pragma: no cover return message.ack() try: log.debug('Got invocation ..{}'.format( str(message.properties['correlation_id'])[-4:])) requested_codec, (fn_name, args, kwargs) = self._decode_message(message) except Exception as e: log.error(str(e)) else: result = self._call_service_method((service_name, fn_name), args, kwargs) log.debug('Publishing response for invocation ..{}'.format( str(message.properties['correlation_id'])[-4:])) # Node.channel_lock.acquire() try: with producers[self.connection].acquire( block=True) as producer: producer.publish( requested_codec.encode(result), exchange=self.exchange, routing_key=message.properties['reply_to'], correlation_id=message.properties['correlation_id'] # body=requested_codec.encode(result) ) except Exception as e: log.error('Failed to publish message') traceback.print_exc() # Node.channel_lock.release() except Exception as e: log.error('Unhandled exception in _validate_message.') traceback.print_exc()
def _on_broadcast(self, message): """ Called when a notification is received. Does not acknowledge notifications because they're delivered via `fanout` exchange. """ try: try: requested_codec, (event, data) = self._decode_message(message) except Exception as e: log.error(str(e)) else: log.debug('Got notification ..{}'.format( str(message.properties['correlation_id'])[-4:])) listeners = self.listeners.get(event, []) for fn in listeners: self.pool.apply_async(fn, args=(data, )) except Exception as e: log.error('Unhandled exception in _on_broadcast') traceback.print_exc()
def _on_message(self, message): """ Called when a message is received on one of the queues. """ log.debug('Received message %s', message) self.pool.apply(self._validate_message, args=(message, ))