Python pika消费Rabbit MQ数据,慢消费引起的connection reset问题
问题描述
使用python pika框架,从Rabbit MQ消费数据时,遇到了connection reset的错误,错误内容如下:
Traceback (most recent call last):
File "/app/utils/rabbit.py", line 27, in message_callback
channel.basic_ack(delivery_tag=method.delivery_tag)
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 2130, in basic_ack
self._flush_output()
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 1353, in _flush_output
self._connection._flush_output(lambda: self.is_closed, *waiters)
File "/usr/local/lib/python3.11/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')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/app/app.py", line 14, in main
rabbit.start_listen()
File "/app/utils/rabbit.py", line 52, in start_listen
channel.start_consuming()
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 1883, in start_consuming
self._process_data_events(time_limit=None)
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 2044, in _process_data_events
self.connection.process_data_events(time_limit=time_limit)
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 851, in process_data_events
self._dispatch_channel_events()
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 567, in _dispatch_channel_events
impl_channel._get_cookie()._dispatch_events()
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 1510, in _dispatch_events
consumer_info.on_message_callback(self, evt.method,
File "/app/utils/rabbit.py", line 34, in message_callback
channel.basic_nack(delivery_tag=method.delivery_tag)
File "/usr/local/lib/python3.11/site-packages/pika/adapters/blocking_connection.py", line 2151, in basic_nack
self._impl.basic_nack(
File "/usr/local/lib/python3.11/site-packages/pika/channel.py", line 401, in basic_nack
self._raise_if_not_open()
File "/usr/local/lib/python3.11/site-packages/pika/channel.py", line 1403, in _raise_if_not_open
raise exceptions.ChannelWrongStateError('Channel is closed.')
pika.exceptions.ChannelWrongStateError: Channel is closed.
我的代码如下:
import pika
from utils.logger import logger
import traceback
import json
from utils.config import get_rabbit_config
from utils.mail import send_mail
import time
EXCHANGE = 'DEFAULT_EXCHANGE'
ROUTING_KEY = 'purchasing.system.contract'
QUEUE_NAME = 'PURCHASING_CONTRACT_CONSUMER'
class Rabbit():
def __init__(self, callback=None) -> None:
self.callback = callback
def message_callback(self, channel, method, properties, body):
try:
logger.info(f'receive message: {body}')
message = json.loads(body)
if self.callback:
result = self.callback(message)
# time.sleep(5)
# result = False
else:
logger.warn('self.callback is None')
if result:
channel.basic_ack(delivery_tag=method.delivery_tag)
else:
channel.basic_nack(delivery_tag=method.delivery_tag)
except Exception as e:
logger.error(traceback.format_exc())
send_mail('purchasing-contract-consumer error',
traceback.format_exc())
channel.basic_nack(delivery_tag=method.delivery_tag)
time.sleep(3)
def start_listen(self):
config = get_rabbit_config()
credentials = pika.PlainCredentials(
username=config['username'], password=config['password'])
parameters = pika.ConnectionParameters(
# host=config['host'], port=config['port'], credentials=credentials, heartbeat=300, stack_timeout=300, socket_timeout=300, blocked_connection_timeout=300)
host=config['host'], port=config['port'], credentials=credentials, heartbeat=120, socket_timeout=60)
connection = pika.BlockingConnection(parameters=parameters)
channel = connection.channel()
# channel.exchange_declare(exchange=EXCHANGE,exchange_type='topic',durable=True)
channel.queue_declare(queue=QUEUE_NAME, durable=True)
channel.queue_bind(queue=QUEUE_NAME, exchange=EXCHANGE,
routing_key=ROUTING_KEY)
channel.basic_consume(
queue=QUEUE_NAME, on_message_callback=self.message_callback, auto_ack=False)
logger.info('start listening...')
channel.start_consuming()
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
我的应用场景是:从rabbit 消费数据,并通过restful API调用web服务(self.callback是一个web API调用),上报消费的数据。但是web服务响应非常慢,每次调用花费1min多钟,并且rabbit MQ中积累了大量消息。
原因分析
刚开始以为是rabbit MQ connection的超时问题引起的,于是设置连接超时时间为300s,代码如下:
host=config['host'], port=config['port'], credentials=credentials, heartbeat=300, stack_timeout=300, socket_timeout=300, blocked_connection_timeout=300)
connection reset问题依然存在,于是又怀疑是result = self.callback(message)调用时间过长,超过了1min,于是将其注释掉,改成
result = self.callback(message)
time.sleep(5)
result = False
错误依然存在,也排除了不是self.callback(message)长时间阻塞引起的问题。
查阅相关资料,发现是因为未设置prefetch_count,如果不设置这个参数,框架默认设置为0,意味着无限消费数据,此时,如果消费端处理消息的速度非常慢,并且Rabbit MQ中有大量消息堆积,那么socket的缓存区就会塞满,此时客户端socket就会告诉服务端socket,将滑动窗口设置为0。由于客户端socket的缓存区一直被沾满,服务端长时间无法发送数据,甚至连socket的心跳消息也无法发出,就会导致connection reset异常;之后如果客户端调用restful API完成数据上传,尝试调用channel.basic_ack(delivery_tag=method.delivery_tag),此时,连接已经被重置,那么就抛出connection reset异常。
解决方案
在代码中增加prefetch_count,代码如下:
import pika
from utils.logger import logger
import traceback
import json
from utils.config import get_rabbit_config
from utils.mail import send_mail
import time
EXCHANGE = 'DEFAULT_EXCHANGE'
ROUTING_KEY = 'purchasing.system.contract'
QUEUE_NAME = 'PURCHASING_CONTRACT_CONSUMER'
class Rabbit():
def __init__(self, callback=None) -> None:
self.callback = callback
def message_callback(self, channel, method, properties, body):
try:
logger.info(f'receive message: {body}')
message = json.loads(body)
if self.callback:
result = self.callback(message)
# time.sleep(5)
# result = False
else:
logger.warn('self.callback is None')
if result:
channel.basic_ack(delivery_tag=method.delivery_tag)
else:
channel.basic_nack(delivery_tag=method.delivery_tag)
except Exception as e:
logger.error(traceback.format_exc())
send_mail('purchasing-contract-consumer error',
traceback.format_exc())
channel.basic_nack(delivery_tag=method.delivery_tag)
time.sleep(3)
def start_listen(self):
config = get_rabbit_config()
credentials = pika.PlainCredentials(
username=config['username'], password=config['password'])
parameters = pika.ConnectionParameters(
# host=config['host'], port=config['port'], credentials=credentials, heartbeat=300, stack_timeout=300, socket_timeout=300, blocked_connection_timeout=300)
host=config['host'], port=config['port'], credentials=credentials, heartbeat=120, socket_timeout=60)
connection = pika.BlockingConnection(parameters=parameters)
channel = connection.channel()
channel.basic_qos(prefetch_count=1) # 限制消费端消费的数据,防止缓存区被占满。
# channel.exchange_declare(exchange=EXCHANGE,exchange_type='topic',durable=True)
channel.queue_declare(queue=QUEUE_NAME, durable=True)
channel.queue_bind(queue=QUEUE_NAME, exchange=EXCHANGE,
routing_key=ROUTING_KEY)
channel.basic_consume(
queue=QUEUE_NAME, on_message_callback=self.message_callback, auto_ack=False)
logger.info('start listening...')
channel.start_consuming()
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
logger.warning('warning...')
设置完成后,可以正常消费数据,connection reset的异常消息。
Python pika消费Rabbit MQ数据,慢消费引起的connection reset问题的更多相关文章
- Rabbit MQ 怎么保证可靠性、幂等性、消费顺序?
RabbitMQ如何保证消息的可靠性 RabbitMQ消息丢失的三种情况 生产者弄丢消息时的解决方法 方法一:生产者在发送数据之前开启RabbitMQ的事务(采用该种方法由于事务机制,会导致吞吐量下降 ...
- Python 基于Python结合pykafka实现kafka生产及消费速率&主题分区偏移实时监控
基于Python结合pykafka实现kafka生产及消费速率&主题分区偏移实时监控 By: 授客 QQ:1033553122 1.测试环境 python 3.4 zookeeper- ...
- 消费端如何保证消息队列MQ的有序消费
消息无序产生的原因 消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(Producer),但是往往在生产环境中有多个消息的消费端(Consumer) ...
- Kafka重复消费和丢失数据研究
Kafka重复消费原因 底层根本原因:已经消费了数据,但是offset没提交. 原因1:强行kill线程,导致消费后的数据,offset没有提交. 原因2:设置offset为自动提交,关闭kafka时 ...
- Kafka在高并发的情况下,如何避免消息丢失和消息重复?kafka消费怎么保证数据消费一次?数据的一致性和统一性?数据的完整性?
1.kafka在高并发的情况下,如何避免消息丢失和消息重复? 消息丢失解决方案: 首先对kafka进行限速, 其次启用重试机制,重试间隔时间设置长一些,最后Kafka设置acks=all,即需要相应的 ...
- Kafka消费不到数据的特殊情况
我大约是把kafka消费不到数据的特殊情况都经历了一遍了吧= =. kafka消费不到数据的原因,首先检查配置之类的,如是否设置了group.id,对应的topic是否正确等等,这些不多说. 下面是我 ...
- 聊聊mq中消息消费的几种方式
mq系列文章 对mq了解不是很多的,可以看一下下面两篇文章: 聊聊mq的使用场景 聊聊业务系统中投递消息到mq的几种方式 聊聊消息消费的几种方式 如何确保消息至少消费一次 如何保证消息消费的幂等性 本 ...
- 开源基于Canal的开源增量数据订阅&消费中间件
CanalSync canal 是阿里巴巴开源的一款基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB). 我开发的这个CanalSync项目 ht ...
- SparkStreaming消费kafka中数据的方式
有两种:Direct直连方式.Receiver方式 1.Receiver方式: 使用kafka高层次的consumer API来实现,receiver从kafka中获取的数据都保存在spark exc ...
- 包银消费CTO汤向军:消费金融大数据风控架构与实践
1 业务架构 风控平台是相对独立的系统,信审的案件可以从借款端平台推过来,也可以从第三方平台推过来.信审案件到达风控平台后,自动创建工作流,根据风控流程处理各流程环节任务. •自动决策 风控流程自动处 ...
随机推荐
- 开源IDS/IPS Suricata的部署与使用
目录 前言 在Linux上部署Suricata Suricata的基本配置 配置文件 Suricata的规则 Suricata的使用 Suricata检测SQL注入 前言 Suricata 是一个高性 ...
- mvn eclipse:eclipse -Dwtpversion=2.0 -DdownloadSources=true -DdownloadJavadocs=true -DjdkLevel=1.6
mvn eclipse:eclipse -Dwtpversion=2.0 -DdownloadSources=true -DdownloadJavadocs=true -DjdkLevel=1.6
- python之常用方法(精)
查找列表中出现最频繁的元素 使用 max() 函数可以快速查找出一个列表中出现频率最高的某个元素. >>> a = [1, 2, 3, 4, 3, 4, 5, 4, 4, 2] &g ...
- 关于IMultiValueConverter的使用
在前端向后端传递数据的过程中,因为涉及多个属性的调用,将数据绑定到CommandParameter,采用了多值转换器进行数据传递. class MultiBindingConverter : IMul ...
- VLC web(http)控制 (2) 状态获取
VLC 状态通过http://127.0.0.1:8080/requests/status.xml获取.(IP地址自行更换) 内容如下: <root> <fullscreen> ...
- 【Amadeus原创】本地安装gitlab,初始化管理员密码
注册还是无法登录,最后发现,需要初始化root密码. docker exec进去,然后执行gitLab-rails,修改密码, 然后登录即可. [root@ecs-9684 ~]# docker ex ...
- 鸿蒙应用开发从入门到入行 - 篇7:http网络请求
鸿蒙应用开发从入门到入行 第七篇 - http网络请求 导读:在本篇文章里,您将掌握鸿蒙开发工具DevEco的基本使用.ArkUI里的基础组件,并通过制作一个简单界面掌握使用 HarmonyOS - ...
- 各版本jdk百度云下载,包括linux版和windows版
并不是越新的版本就一定越好,请先考虑jdk的版本是否跟你的开发环境有版本冲突问题. 2021-11-4更新 ps:从官网下载实在是太慢了!! 官网链接:https://www.oracle.com/j ...
- 2024年1月Java项目开发指南11:axios请求与接口统一管理
axios中文网:https://www.axios-http.cn/ 安装 npm install axios 配置 在src下创建apis文件夹 创建axios.js文件 配置如下: // src ...
- 【WEB前端】【JQuery】搜索li标签的内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...