Use python's pika package to connect to rabbitmq, the code is as follows:
import pika
import threading
import time
def on_message(channel, method_frame, header_frame, body):
print(f'on_message thread id: {threading.get_ident()}')
delivery_tag = method_frame.delivery_tag
print(body, "start")
for i in range(10):
print(i)
time.sleep(20)
print(body, "end")
channel.basic_ack(delivery_tag)
credentials = pika.PlainCredentials('username', 'password')
parameters = pika.ConnectionParameters('test.webapi.username.com', credentials=credentials, heartbeat=5)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue="standard", durable=True)
channel.basic_qos(prefetch_count=1)
channel.basic_consume('standard', on_message)
print(f'main thread id: {threading.get_ident()}')
try:
channel.start_consuming()
except KeyboardInterrupt:
channel.stop_consuming()
connection.close()
Results of the:
(Test) [user@user test]$ python mq1.py
main thread id: 140682766104384
on_message thread id: 140682766104384
b'1' start
0
1
2
3
4
5
6
7
8
9
b'1' end
Traceback (most recent call last):
File "mq1.py", line 38, in <module>
channel.start_consuming()
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 1865, in start_consuming
self._process_data_events(time_limit=None)
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 2026, in _process_data_events
self.connection.process_data_events(time_limit=time_limit)
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 833, in process_data_events
self._dispatch_channel_events()
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 567, in _dispatch_channel_events
impl_channel._get_cookie()._dispatch_events()
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 1492, in _dispatch_events
consumer_info.on_message_callback(self, evt.method,
File "mq1.py", line 24, in on_message
channel.basic_ack(delivery_tag)
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 2112, in basic_ack
self._flush_output()
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 1335, in _flush_output
self._connection._flush_output(lambda: self.is_closed, *waiters)
File "/home/user/conda/envs/Test/lib/python3.8/site-packages/pika/adapters/blocking_connection.py", line 523, in _flush_output
raise self._closed_result.value.error
pika.exceptions.StreamLostError: Stream connection lost: ConnectionResetError(104, 'Connection reset by peer')
Judging from the results, the exception occurred after a long consumption process (200s) was completed and an error was reported. Specifically, an error was reported when calling channel.basic_ack(delivery_tag); it is speculated that the connection with the MQ Server has been reset at this time. ConnectionResetError(104, 'Connection reset by peer'), at this time, if you actively confirm, an error will occur.
The correct solution is as follows:
"""
@author: Zhigang Jiang
@date: 2022/1/16
@description:
"""
import functools
import pika
import threading
import time
def ack_message(channel, delivery_tag):
print(f'ack_message thread id: {threading.get_ident()}')
if channel.is_open:
channel.basic_ack(delivery_tag)
else:
# Channel is already closed, so we can't ACK this message;
# log and/or do something that makes sense for your app in this case.
pass
def do_work(channel, delivery_tag, body):
print(f'do_work thread id: {threading.get_ident()}')
print(body, "start")
for i in range(10):
print(i)
time.sleep(20)
print(body, "end")
cb = functools.partial(ack_message, channel, delivery_tag)
channel.connection.add_callback_threadsafe(cb)
def on_message(channel, method_frame, header_frame, body):
print(f'on_message thread id: {threading.get_ident()}')
delivery_tag = method_frame.delivery_tag
t = threading.Thread(target=do_work, args=(channel, delivery_tag, body))
t.start()
credentials = pika.PlainCredentials('username', 'password')
parameters = pika.ConnectionParameters('test.webapi.username.com', credentials=credentials, heartbeat=5)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue="standard", durable=True)
channel.basic_qos(prefetch_count=1)
channel.basic_consume('standard', on_message)
print(f'main thread id: {threading.get_ident()}')
try:
channel.start_consuming()
except KeyboardInterrupt:
channel.stop_consuming()
connection.close()
The idea is pika
that threads are not safe, so another thread is needed when receiving messages and ACK response messages.