def test_get_producer(self, rabbit_config, confirms): amqp_uri = rabbit_config['AMQP_URI'] producer_ids = [] with get_producer(amqp_uri, confirms) as producer: producer_ids.append(id(producer)) transport_options = producer.connection.transport_options assert isinstance(producer, Producer) assert transport_options['confirm_publish'] is confirms with get_producer(amqp_uri, confirms) as producer: producer_ids.append(id(producer)) assert len(set(producer_ids)) == 1
def publish(msg, **kwargs): exchange = self.exchange serializer = self.serializer if exchange is None and self.queue is not None: exchange = self.queue.exchange retry = kwargs.pop('retry', self.retry) retry_policy = kwargs.pop('retry_policy', self.retry_policy) mandatory = kwargs.pop('mandatory', False) with get_producer(self.amqp_uri, self.use_confirms) as producer: headers = self.get_message_headers(worker_ctx) producer.publish(msg, exchange=exchange, headers=headers, serializer=serializer, retry=retry, retry_policy=retry_policy, mandatory=mandatory, **kwargs) if mandatory: if not self.use_confirms: warnings.warn( "Mandatory delivery was requested, but " "unroutable messages cannot be detected without " "publish confirms enabled.") try: returned_messages = producer.channel.returned_messages returned = returned_messages.get_nowait() except queue.Empty: pass else: raise UndeliverableMessage(returned)
def publish(msg): with get_producer(amqp_uri) as producer: producer.publish( msg, serializer="json", routing_key=queue.name )
def test_confirms_disabled(self, rabbit_config): amqp_uri = rabbit_config['AMQP_URI'] with get_producer(amqp_uri, False) as producer: producer.publish( "msg", exchange="missing", routing_key="key" )
def _call(self, *args, **kwargs): _log.debug('invoking %s', self) worker_ctx = self.worker_ctx container = worker_ctx.container msg = {'args': args, 'kwargs': kwargs} # We use the `mandatory` flag in `producer.publish` below to catch rpc # calls to non-existent services, which would otherwise wait forever # for a reply that will never arrive. # # However, the basic.return ("no one is listening for topic") is sent # asynchronously and conditionally, so we can't wait() on the channel # for it (will wait forever on successful delivery). # # Instead, we make use of (the rabbitmq extension) confirm_publish # (https://www.rabbitmq.com/confirms.html), which _always_ sends a # reply down the channel. Moreover, in the case where no queues are # bound to the exchange (service unknown), the basic.return is sent # first, so by the time kombu returns (after waiting for the confim) # we can reliably check for returned messages. # Note that overriding :attr:`self.use_confirms` may disable this # functionality and therefore :class:`UnknownService` will never # be raised (and the caller will hang). routing_key = '{}.{}'.format(self.service_name, self.method_name) exchange = get_rpc_exchange(container.config) with get_producer(self.amqp_uri, self.use_confirms) as producer: headers = self.get_message_headers(worker_ctx) correlation_id = str(uuid.uuid4()) reply_listener = self.reply_listener reply_to_routing_key = reply_listener.routing_key reply_event = reply_listener.get_reply_event(correlation_id) producer.publish(msg, exchange=exchange, routing_key=routing_key, mandatory=True, serializer=self.serializer, reply_to=reply_to_routing_key, headers=headers, correlation_id=correlation_id, retry=self.retry, retry_policy=self.retry_policy) try: producer.channel.returned_messages.get_nowait() except queue.Empty: pass else: raise UnknownService(self.service_name) return RpcReply(reply_event)
def test_confirms_enabled(self, rabbit_config): amqp_uri = rabbit_config['AMQP_URI'] with pytest.raises(NotFound): with get_producer(amqp_uri) as producer: producer.publish( "msg", exchange="missing", routing_key="key" )
def test_pool_gives_different_producers(self, rabbit_config): amqp_uri = rabbit_config['AMQP_URI'] producer_ids = [] # get a producer with get_producer(amqp_uri, True) as confirmed_producer: producer_ids.append(id(confirmed_producer)) assert len(set(producer_ids)) == 1 # get a producer with the same parameters with get_producer(amqp_uri, True) as confirmed_producer: producer_ids.append(id(confirmed_producer)) assert len(set(producer_ids)) == 1 # same producer returned # get a producer with different parameters with get_producer(amqp_uri, False) as unconfirmed_producer: producer_ids.append(id(unconfirmed_producer)) assert len(set(producer_ids)) == 2 # different producer returned
def send_response(self, result, exc_info, **kwargs): error = None if exc_info is not None: error = serialize(exc_info[1]) # disaster avoidance serialization check: `result` must be # serializable, otherwise the container will commit suicide assuming # unrecoverable errors (and the message will be requeued for another # victim) try: kombu.serialization.dumps(result, self.serializer) except Exception: exc_info = sys.exc_info() # `error` below is guaranteed to serialize to json error = serialize(UnserializableValueError(result)) result = None exchange = get_rpc_exchange(self.config) retry = kwargs.pop('retry', self.retry) retry_policy = kwargs.pop('retry_policy', self.retry_policy) with get_producer(self.amqp_uri, self.use_confirms) as producer: routing_key = self.message.properties['reply_to'] correlation_id = self.message.properties.get('correlation_id') msg = {'result': result, 'error': error} _log.debug('publish response %s:%s', routing_key, correlation_id) producer.publish(msg, retry=retry, retry_policy=retry_policy, exchange=exchange, routing_key=routing_key, serializer=self.serializer, correlation_id=correlation_id, **kwargs) return result, exc_info
def send_response(self, message, result, exc_info): config = self.container.config error = None if exc_info is not None: error = serialize(exc_info[1]) with get_producer(config[AMQP_URI_CONFIG_KEY]) as producer: routing_key = message.properties.get('reply_to') msg = {'result': result, 'error': error} producer.publish(msg, exchange=orders_exchange, serializer=DEFAULT_SERIALIZER, routing_key=routing_key) return result, error
def dispatch(service_name, event_type, event_data): """ Dispatch an event claiming to originate from `service_name` with the given `event_type` and `event_data`. """ serializer = nameko_config.get(SERIALIZER_CONFIG_KEY, DEFAULT_SERIALIZER) exchange = get_event_exchange(service_name) with get_connection(amqp_uri) as connection: exchange.maybe_bind(connection) # TODO: reqd? maybe_declare? with get_producer(amqp_uri, use_confirms) as producer: msg = event_data routing_key = event_type producer.publish(msg, exchange=exchange, serializer=serializer, routing_key=routing_key, retry=retry, retry_policy=retry_policy, mandatory=mandatory, **kwargs) if mandatory: if not use_confirms: warnings.warn( "Mandatory delivery was requested, but " "unroutable messages cannot be detected without " "publish confirms enabled.") try: returned_messages = producer.channel.returned_messages returned = returned_messages.get_nowait() except queue.Empty: pass else: raise UndeliverableMessage(returned)
def producer(self, get_producer): producer = get_producer().__enter__.return_value # make sure we don't raise UndeliverableMessage if mandatory is True producer.channel.returned_messages.get_nowait.side_effect = queue.Empty return producer