def on_message(self, channel, method, props, body): """ 消息处理函数, 每当接收到一个完整消息后会调用该函数 """ message = json.loads(body) msg_type = message['header']['type'] # 获取消息头部 msg_body = message['body'] # 获取消息体 # 获取消息处理函数 if msg_type in CALLBACK_MANAGER: _do_task = CALLBACK_MANAGER[msg_type] else: self.acknowledge_message(delivery_tag=method.delivery_tag, channel=channel) LOG.error('message handler %s is not implemented' % msg_type) return # 执行消息处理函数 result = _do_task(self, msg_body) # 根据结果判断是否要给发送者响应 if result is None or not props.reply_to: self.acknowledge_message(delivery_tag=method.delivery_tag, channel=channel) else: return_msg = dict() return_msg['header'] = message['header'] return_msg['body'] = result self.acknowledge_message(delivery_tag=method.delivery_tag, channel=channel) self.send_result(channel, props, return_msg)
def create_host_connection(self, host_index, for_listening=False): """Create new connection to host #host_index :param host_index: Integer, number of host for connection establishing :param for_listening: Boolean, creates connection for listening if True :return: New connection """ with self._connection_lock: host = self._host_list[host_index] connection_params = pika.ConnectionParameters( host=host, port=self.conf.mq_port, credentials=pika.credentials.PlainCredentials(self.conf.mq_username, self.conf.mq_password), heartbeat_interval=self._heartbeat_interval if for_listening else None, **self._common_pika_params ) try: if for_listening: connection = None else: connection = pika.BlockingConnection(parameters=connection_params) connection.params = connection_params LOG.info('connected rabbitmq-server %s:%s' % (host, self.conf.mq_port)) return connection except: LOG.error(traceback.format_exc())
def create_host_connection(self, host_index, for_listening=False): """Create new connection to host #host_index :param host_index: Integer, number of host for connection establishing :param for_listening: Boolean, creates connection for listening if True :return: New connection """ with self._connection_lock: host = self._host_list[host_index] connection_params = pika.ConnectionParameters( host=host, port=self.conf.mq_port, credentials=pika.credentials.PlainCredentials( self.conf.mq_username, self.conf.mq_password), heartbeat_interval=self._heartbeat_interval if for_listening else None, **self._common_pika_params) try: if for_listening: connection = None else: connection = pika.BlockingConnection( parameters=connection_params) connection.params = connection_params LOG.info('connected rabbitmq-server %s:%s' % (host, self.conf.mq_port)) return connection except: LOG.error(traceback.format_exc())
def on_channel_closed(self, channel, reply_code, reply_text): """ 连接通道关闭响应函数, 在这里我们仅仅做了关闭连接 """ LOG.warning('Channel was closed: (%s) %s' % (reply_code, reply_text)) if not self._closing: self._connection.close()
def close_connection(self): """ 主动关闭连接 """ LOG.info('Closing connection') self._closing = True self._connection.close()
def close_channel(self): """ 主动关闭连接通道,发送Channel.Close命令给RabbitMQ """ LOG.info('Closing the channel') if self._channel: self._channel.close()
def on_rpc_consumer_cancelled(self, method_frame): """ 注销消费者响应函数 """ LOG.info('Consumer was cancelled remotely, shutting down: %r' % method_frame) if self._channel: # self._channel.close() pass
def start_rpc_consuming(self): """ 准备消费消息 """ LOG.info('Starting to receive response message...') self.add_on_rpc_cancel_callback() # 注册回调队列消息响应函数 self._consumer_tag = self._channel.basic_consume(self.on_message, self.reply_queue)
def on_connection_closed(self, connection, reply_code, reply_text): """ 连接关闭响应函数 """ self._channel = None if self._closing: self._connection.ioloop.stop() else: LOG.warning('Connection closed, reopening in 5 seconds: (%s) %s' % (reply_code, reply_text)) self._connection.add_timeout(5, self.reconnect)
def on_delivery_confirmation(self, method_frame): """ 当收到Basic.ACK命令时, 该函数被调用; RabbitMQ响应Basic.Publish命令 """ confirmation_type = method_frame.method.NAME.split('.')[1].lower() LOG.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)
def on_queue_declared(self, method_frame): """ 队列创建完成响应函数,接收RabbitMQ发送过来的Queue.DeclareOk命令 """ binding_key = None if self.reply_queue is not None: binding_key = '#.%s.#' % self.reply_queue LOG.info('Binding %s to %s with %s' % (self.exchange, self.reply_queue, binding_key)) # 绑定队列和交换机 self._channel.queue_bind(self.on_rpc_bindok, self.reply_queue, self.exchange, binding_key)
def stop(self): """ 主动注销消费者并关闭连接,当RabbitMQ确认注销消费者后,on_cancelok函数 将会被调用,在这个函数里关闭连接通道和连接 """ LOG.info('Stopping') self._stopping = True self.close_channel() self.close_connection() self._connection.ioloop.start() LOG.info('Stopped')
def send_message(self, message, queue=None, reply_queue=None): """ 阻塞发送消息,等待返回结果 :param message: messageclient.Message, 消息对象 :param queue: str, 发送队列名称 :param reply_queue: str, 响应结果队列名称 :return: dict, 消息响应结果 """ self.lock.acquire() # 获取锁访问self.reply_queue if queue is None: return routing_key = queue # 将消息发送给指定的队列 if reply_queue is None: reply_queue = 'reply-%s' % str(uuid.uuid4()) if self._stopping: return # 循环等待通道处于打开状态 while not (self._channel and self._channel.is_open): time.sleep(0.02) # 创建回调队列,注册回调队列响应函数以及消费队列 self._consumer_tag = self.declare_queue(reply_queue, exclusive=False, auto_delete=True, durable=False) self.reply_response = None self.correlation_id = str(uuid.uuid4()) properties = pika.BasicProperties(app_id=None, reply_to=reply_queue, correlation_id=self.correlation_id, content_type='application/json', headers=None) # 发送消息 self._channel.basic_publish(self.exchange, routing_key, json.dumps(message.data, ensure_ascii=False), properties) self._message_number += 1 self._deliveries.append(self._message_number) LOG.info('publiser send message # %i' % self._message_number) # 等待消息响应结果 while self.reply_response is None: time.sleep(0.02) result = self.reply_response # self._channel.basic_cancel(consumer_tag=self._consumer_tag, nowait=True) self.lock.release() # 释放锁唤醒其他等待线程访问 return result
def _decorator(fn): if not util.is_callable(fn): LOG.error('function %s is not callable' % fn) sys.exit(-1) def __decorator(self, message): result = fn(self, message) return result # 将被装饰的用户定义的函数注册到 if type is not None and not type in CALLBACK_MANAGER: CALLBACK_MANAGER[type] = __decorator return __decorator
def connect(self): """ 连接RabbitMQ, 返回连接句柄. 当连接建立后,on_connection_open方法将会被调用 """ LOG.info('Connecting to RabbitMQ %s' % self.conf.mq_hosts) connection_params = pika.ConnectionParameters( host=self.conf.mq_hosts, port=self.conf.mq_port, credentials=pika.credentials.PlainCredentials(self.conf.mq_username, self.conf.mq_password) ) return pika.SelectConnection(parameters=connection_params, on_open_callback=self.on_connection_open, on_open_error_callback=None, on_close_callback=None, stop_ioloop_on_close=False)
def declare_queue(self, queue_name, exclusive=False, auto_delete=False, durable=True): """ 创建队列,向RabbitMQ发送Queue.Declare命令 """ LOG.info('Declaring queue %s' % queue_name) self.reply_queue = queue_name if durable: self.reply_queues.add(queue_name) while not self._channel.is_open: time.sleep(0.02) # 创建回调队列 consumer_tag = self._channel.queue_declare(self.on_queue_declared, queue_name, durable=durable, exclusive=exclusive, auto_delete=auto_delete) return consumer_tag
def send_request(self, message, queue=None, reply_queue=None): """ 异步发送消息 :param queue: str, 队列名称 :param reply_queue: str, 响应队列名称 :return: None """ if queue is None: return routing_key = queue # 将消息发送给指定的队列 if self._stopping: return # 循环等待通道处于打开状态 while not (self._channel and self._channel.is_open): time.sleep(0.02) if reply_queue is None: reply_queue = '' else: # self.declare_queue(reply_queue) # 创建回调队列 # self._channel.queue_declare(None, reply_queue, nowait=True, durable=True) # binding_key = '#.%s.#' % reply_queue # self._channel.queue_bind(None, reply_queue, self.exchange, binding_key, nowait=True) pass self.response = None self.correlation_id = str(uuid.uuid4()) properties = pika.BasicProperties(app_id=None, reply_to=reply_queue, correlation_id=self.correlation_id, content_type='application/json', headers=None) # 发送消息 self._channel.basic_publish(self.exchange, routing_key, json.dumps(message.data, ensure_ascii=False), properties) self._message_number += 1 self._deliveries.append(self._message_number) LOG.info('publiser send request # %i' % self._message_number)
def broadcast_message(self, message, queues=[]): """ 广播消息 """ routing_key = None if queues: routing_key = '.'.join(queues) if self._stopping: return while not (self._channel and self._channel.is_open): time.sleep(0.02) properties = pika.BasicProperties(app_id=None, content_type='application/json', headers=None) self._channel.basic_publish(self.exchange, routing_key, json.dumps(message.data, ensure_ascii=False), properties) self._message_number += 1 self._deliveries.append(self._message_number) LOG.info('broadcast message # %i' % self._message_number)
def publish_message(self, message, queue=None): """ 发送消息到queue指定队列 :param message: messageclient.Message, 消息对象 :param queue: str, 消息的路由关键字,匹配binding_key :return: """ routing_key = queue if self._stopping: return while not (self._channel and self._channel.is_open): time.sleep(0.02) properties = pika.BasicProperties(app_id=None, content_type='application/json', headers=None) self._channel.basic_publish(self.exchange, routing_key, json.dumps(message.data, ensure_ascii=False), properties) self._message_number += 1 self._deliveries.append(self._message_number) LOG.info('Published message # %i' % self._message_number)
def open_channel(self): """ 建立连接通道,给RabbitMQ发送Channel.Open命令,当接收到Channel.Open.OK时表示通道已建立 """ LOG.info('Creating a new channel to RabbitMQ.') self._connection.channel(on_open_callback=self.on_channel_open)
def setup_exchange(self, exchange_name): """ 创建交换机,向RabbitMQ发送Exchange.Declare命令 """ LOG.info('Declaring exchange %s' % exchange_name) self._channel.exchange_declare(self.on_exchange_declareok, exchange_name, self.exchange_type, durable=True)