diff --git a/.idea/misc.xml b/.idea/misc.xml index d56657a..845faad 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/.idea/python-learn.iml b/.idea/python-learn.iml index 5ed0139..7bcaaaa 100644 --- a/.idea/python-learn.iml +++ b/.idea/python-learn.iml @@ -1,8 +1,10 @@ - - + + + + diff --git a/rabbitmq/AutoRecoveryConsumer.py b/rabbitmq/AutoRecoveryConsumer.py new file mode 100644 index 0000000..3815665 --- /dev/null +++ b/rabbitmq/AutoRecoveryConsumer.py @@ -0,0 +1,42 @@ +import time + +from rabbitmq.consumer import Consumer + + +class AutoRecoveryConsumer(object): + + def __init__(self, amqp_url, queue): + self._amqp_url = amqp_url + self._queue = queue + self._consumer = Consumer(self._amqp_url, queue) + + def run(self): + while True: + try: + self._consumer.run() + except KeyboardInterrupt: + self._consumer.stop() + break + self.maybe_reconnect() + + def maybe_reconnect(self): + if self._consumer.should_reconnect: + self._consumer.stop() + time.sleep(1) + self._consumer = Consumer(self._amqp_url, self._queue) + + +def main(): + username = 'guest' + password = 'guest' + host = 'localhost' + port = 5672 + vhost = '' + queue = 'hello' + amqp_url = 'amqp://{}:{}@{}:{}/{}'.format(username, password, host, port, vhost) + consumer = AutoRecoveryConsumer(amqp_url, queue) + consumer.run() + + +if __name__ == '__main__': + main() diff --git a/rabbitmq/consumer.py b/rabbitmq/consumer.py new file mode 100644 index 0000000..3705365 --- /dev/null +++ b/rabbitmq/consumer.py @@ -0,0 +1,111 @@ + +# https://www.alibabacloud.com/help/zh/apsaramq-for-rabbitmq/use-cases/automatic-recovery-from-network-failures#0a88a08a3eubs +import logging + +import pika +from pika import BaseConnection +from pika.channel import Channel + +LOG_FORMAT = ('%(levelname) -10s %(asctime)s %(name) -30s %(funcName) ' + '-35s %(lineno) -5d: %(message)s') +LOGGER = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format=LOG_FORMAT) + + +class Consumer(object): + + def __init__(self, amp_url, queue): + self.should_reconnect = False + + self._connection: BaseConnection | None = None + self._channel: Channel | None = None + self._closing = False + self.url = amp_url + + self._queue = queue + + def connect(self): + """ + 创建 connection,并设置回调 + """ + return pika.SelectConnection( + parameters=pika.URLParameters(self.url), + on_open_callback=self.on_connection_open, + on_open_error_callback=self.on_connection_open_failed, + on_close_callback=self.on_channel_closed + ) + + def on_connection_open(self, _unused_connection): + """ + 连接成功的回调 + """ + self._connection.channel(on_open_callback=self.on_channel_open) + + def on_connection_open_failed(self, _unused_connection: BaseConnection, err): + """ + 创建连接错误的回调 + """ + LOGGER.error("Connection failed: %s", err) + self.reconnect() + + def reconnect(self): + """ + 重连,修改 should_reconnect为 True,并停止 io_loop + """ + self.should_reconnect = True, + self.stop() + + def on_channel_open(self, channel: Channel): + """ + 创建 channel 之后的回调 + """ + self._channel = channel + self._channel.add_on_close_callback(self.on_channel_closed) + self.start_consuming() + + def on_channel_closed(self, channel, reason): + """ + channel 关闭的回调 + """ + LOGGER.warning('Channel %i was closed: %s', channel, reason) + self.close_connection() + + def start_consuming(self): + """ + 开始消费 + """ + LOGGER.info('start consuming...') + self._channel.basic_consume(queue=self._queue, on_message_callback=self.on_message, auto_ack=False) + + def close_connection(self): + """ + 关闭连接 + """ + if self._connection.is_closing or self._connection.is_closed: + LOGGER.info('Connection is closing or already closed') + else: + LOGGER.info('Closing connection') + self._connection.close() + + def on_message(self, _unused_channel, basic_deliver, properties, body): + """ + 消费消息并上传 ack + """ + LOGGER.info('Received message %s', body.decode()) + self._channel.basic_ack(delivery_tag=basic_deliver.delivery_tag) + + def run(self): + """ + 创建 connection,并启动 io_loop + """ + self._connection = self.connect() + self._connection.ioloop.start() + + def stop(self): + """ + 停止 io_loop + """ + if not self._closing: + self._closing = True + self._connection.ioloop.stop() + LOGGER.info('Stopping ioloop') diff --git a/rabbitmq/receive.py b/rabbitmq/receive.py new file mode 100644 index 0000000..cbd2dff --- /dev/null +++ b/rabbitmq/receive.py @@ -0,0 +1,37 @@ +import functools +import time + +import pika +from pika import spec +from pika.adapters.blocking_connection import BlockingChannel + +connection = pika.BlockingConnection(pika.connection.ConnectionParameters(host='1781833300178911.mq-amqp.cn-hangzhou-a.aliyuncs.com',port=5672, virtual_host='yibms', credentials=pika.credentials.PlainCredentials( + 'MjoxNzgxODMzMzAwMTc4OTExOkxUQUk1dEI5ZFptV1pQd2dwYzFMcXcxZA==', + 'NzBGQjgzMjgxQzQyMEQ3OThDNDE4QjgzRUY2RjlEODBGOTgwNDAyOToxNzEzOTI1ODg2MTc5' + ))) +channel = connection.channel() + +# channel.queue_declare(queue='dev_task') + + +def callback(ch: BlockingChannel, method: spec.Basic.Deliver, properties: spec.BasicProperties, body: bytes): + connection.add_callback_threadsafe(functools.partial(do_work, (ch, method, properties, body))) + + +def do_work(ch: BlockingChannel, method: spec.Basic.Deliver, properties: spec.BasicProperties, body: bytes): + try: + print(f'[x] received {body.decode()}') + time.sleep(20) + ch.basic_ack(delivery_tag=method.delivery_tag) + print(f'[ack] received {body.decode()}') + except Exception as e: + print(e) + finally: + print(ch.connection.is_open) + # ch.basic_ack(delivery_tag=method.delivery_tag) + + +# auto_ack=False 代表消息不自动确认 +channel.basic_consume(on_message_callback=do_work, queue='dev_task', auto_ack=False) + +channel.start_consuming() diff --git a/rabbitmq/send.py b/rabbitmq/send.py new file mode 100644 index 0000000..aadfa2e --- /dev/null +++ b/rabbitmq/send.py @@ -0,0 +1,21 @@ +import pika + +connection = pika.BlockingConnection(pika.connection.ConnectionParameters(host='localhost', + port=5672, + virtual_host='/', + credentials=pika.credentials.PlainCredentials( + 'guest', + 'guest' + ))) +properties = pika.BasicProperties(message_id='hellow') + +channel = connection.channel() + +channel.queue_declare(queue='hello') + +msg = '你好,我是来自hello队列的消息' + +channel.basic_publish(exchange='', routing_key='hello', body=msg.encode('utf-8'), properties=properties) + +print(f"[x] send '{msg}'") +connection.close()