RabbitMQ Python——Asynchronous publisher example

来源:互联网 发布:python java go技术栈 编辑:程序博客网 时间:2024/05/21 08:50
# -*- coding: utf-8 -*-import loggingimport pikaimport jsonLOG_FORMAT = ('%(levelname) -10s %(asctime)s %(name) -30s %(funcName) '              '-35s %(lineno) -5d: %(message)s')LOGGER = logging.getLogger(__name__)class ExamplePublisher(object):    EXCHANGE = 'message'    EXCHANGE_TYPE = 'topic'    PUBLISH_INTERVAL = 1    QUEUE = 'text'    ROUTING_KEY = 'example.text'        def __init__(self,ampq_url):        '''        Connect to RabbitMQ        :param str amqp_url: The URL for connecting to RabbitMQ        '''        self._connection = None        self._channel = None        self._deliveries = []        self._acked = 0        self._nacked = 0        self._message_number = 0        self._stopping = False        self._url = ampq_url        self._closing = False        def connect(self):        LOGGER.info("Connecting to %s",self._url)        '''        class pika.adapters.select_connection.SelectConnection(parameters=None,                             on_open_callback=None, stop_ioloop_on_close=True)        '''        return pika.SelectConnection(pika.URLParameters(self._url),self.on_connection_open)    def on_connection_open(self,unused_connection):        LOGGER.info("Connection opened")        self.add_on_connection_close_callback()        self.open_channel()    def open_channel(self):        LOGGER.info('Creating a new channel')        """This method will open a new channel with RabbitMQ by issuing the        Channel.Open RPC command. When RabbitMQ confirms the channel is open        by sending the Channel.OpenOK RPC reply, the on_channel_open method        will be invoked.            The channel method in `connection`, then it will call `channel.Channel`        see the original code        """        self._connection.channel(on_open_callback = self.on_channel_open)            def on_channel_open(self,channel):            LOGGER.info('Channel opened')        self._channel = channel        self.add_on_channel_close_callback()        self.setup_exchange(self.EXCHANGE)                    def setup_exchange(self,exchange_name):        '''         Setup the exchange on RabbitMQ        '''        LOGGER.info('Declaring exchange %s', exchange_name)        self._channel.exchange_declare(self.on_exchange_declareok,                                       exchange=exchange_name,                                       type=self.EXCHANGE_TYPE,durable=True)            def on_exchange_declareok(self,unused_frame):        '''        Setup the queue on RabbitMQ        '''        LOGGER.info("Exchange declared")        self.setup_queue(self.QUEUE)            def setup_queue(self,queue_name):        '''        Setup the queue on RabbitMQ        '''        LOGGER.info("Declaring queue %s",queue_name)        self._channel.queue_declare(self.on_queue_declareok,queue_name,durable=True)            def on_queue_declareok(self,method_frame):        LOGGER.info('Binding %s to %s with %s',                    self.EXCHANGE, self.QUEUE, self.ROUTING_KEY)        self._channel.queue_bind(self.on_bindok,self.QUEUE,self.EXCHANGE,self.ROUTING_KEY)            def on_bindok(self,unused_frame):        LOGGER.info("Queue bound")        self.start_publishing()            def add_on_connection_close_callback(self):        LOGGER.info("Adding connection close callback")        self._connection.add_on_close_callback(self.on_connection_closed)    '''    Add a callback notification when the connection has closed.    The callback will be passed the connection, the reply_code (int) and the reply_text (str), if sent by the remote server.    '''        def on_connection_closed(self,connection,reply_code,reply_text):        self._channel = None        if self._closing:            self._connection.ioloop.stop()        else:            LOGGER.warning('Connection closed, reopening in 5 seconds: (%s) %s',                           reply_code, reply_text)            self._connection.add_timeout(5, self.reconnect)    def reconnect(self):        self._connection.ioloop.stop()        self._connection = self.connect()        self._connection.ioloop.start()            def add_on_channel_close_callback(self):        LOGGER.info('Adding channel close callback')        self._channel.add_on_close_callback(self.on_channel_closed)    def on_channel_closed(self, channel, reply_code, reply_text):        LOGGER.warning('Channel was closed: (%s) %s', reply_code, reply_text)        if not self._closing:            self._connection.close()        def start_publishing(self):        LOGGER.info('Issuing consumer related RPC commands')        self.enable_delivery_confirmations()        self.schedule_next_message()            def enable_delivery_confirmations(self):        '''         Send the Confirm.Select RPC method to RabbitMQ to enable delivery        confirmations on the channel. The only way to turn this off is to close        the channel and create a new one.        When the message is confirmed from RabbitMQ, the        on_delivery_confirmation method will be invoked passing in a Basic.Ack        or Basic.Nack method from RabbitMQ that will indicate which messages it        is confirming or rejecting.        '''        LOGGER.info('Issuing Confirm.Select RPC command')        self._channel.confirm_delivery(self.on_delivery_confirmation)            def on_delivery_confirmation(self,method_frame):        confirmation_type = method_frame.method.NAME.split('.')[1].lower()        LOGGER.info('Received %s for delivery tag: %i',                    confirmation_type,                    method_frame.method.delivery_tag)        if confirmation_type == 'ack':            self._acked += 1        elif confirmation_type == 'nack':            self._nacked += 1        self._deliveries.remove(method_frame.method.delivery_tag)        LOGGER.info('Published %i messages, %i have yet to be confirmed, '                    '%i were acked and %i were nacked',                    self._message_number, len(self._deliveries),                    self._acked, self._nacked)    def schedule_next_message(self):        """If we are not closing our connection to RabbitMQ, schedule another        message to be delivered in PUBLISH_INTERVAL seconds.        """        if self._stopping:            return        LOGGER.info('Scheduling next message for %0.1f seconds',                    self.PUBLISH_INTERVAL)        self._connection.add_timeout(self.PUBLISH_INTERVAL,                                     self.publish_message)    def publish_message(self):        if self._stopping:            return        message = {u'مفتاح': u' قيمة',                   u'键': u'值',                   u'キー': u'値'}        properties = pika.BasicProperties(app_id='example-publisher',                                          content_type='text/plain',                                          headers=message)        self._channel.basic_publish(self.EXCHANGE, self.ROUTING_KEY,                                    json.dumps(message, ensure_ascii=False),                                    properties)        self._message_number += 1        self._deliveries.append(self._message_number)        LOGGER.info('Published message # %i', self._message_number)        self.schedule_next_message()        def run(self):        self._connection = self.connect()        self._connection.ioloop.start()        def close_channel(self):        LOGGER.info('Closing the channel')        if self._channel:            self._channel.close()    def close_connection(self):        LOGGER.info('Closing connection')        self._closing = True        self._connection.close()        def stop(self):        LOGGER.info('Stopping')        self._stopping = True        self.close_channel()        self.close_connection()        self._connection.ioloop.start()        LOGGER.info('Stopped')        def main():    logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)    # Connect to localhost:5672 as guest with the password guest and virtual host "/" (%2F)    example = ExamplePublisher('amqp://guest:guest@localhost:5672/%2F?connection_attempts=3&heartbeat_interval=3600')    try:        example.run()    except KeyboardInterrupt:        example.stop()if __name__ == '__main__':    main()                

本文参考:https://pika.readthedocs.org/en/latest/examples/asynchronous_publisher_example.html

在Channel queue_declare API中有这么一句话Declare queue, create if needed. This method creates or checks a queue. When creating a new queue the client can specify various properties that control the durability of the queue and its contents, and the level of sharing for the queue.

这句话的意思就是只有当消息队列是新建的时候才能使用durable=True参数,否则会出现莫名其妙的错误。

BlockingConnection和SelectConnection所使用的API是不同的,前者使用BlockingChannel,后者使用Channel。

Pika的API文档:https://pika.readthedocs.org/en/0.9.12/