问题描述

使用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问题的更多相关文章

  1. Rabbit MQ 怎么保证可靠性、幂等性、消费顺序?

    RabbitMQ如何保证消息的可靠性 RabbitMQ消息丢失的三种情况 生产者弄丢消息时的解决方法 方法一:生产者在发送数据之前开启RabbitMQ的事务(采用该种方法由于事务机制,会导致吞吐量下降 ...

  2. Python 基于Python结合pykafka实现kafka生产及消费速率&主题分区偏移实时监控

    基于Python结合pykafka实现kafka生产及消费速率&主题分区偏移实时监控   By: 授客 QQ:1033553122   1.测试环境 python 3.4 zookeeper- ...

  3. 消费端如何保证消息队列MQ的有序消费

    消息无序产生的原因 消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(Producer),但是往往在生产环境中有多个消息的消费端(Consumer) ...

  4. Kafka重复消费和丢失数据研究

    Kafka重复消费原因 底层根本原因:已经消费了数据,但是offset没提交. 原因1:强行kill线程,导致消费后的数据,offset没有提交. 原因2:设置offset为自动提交,关闭kafka时 ...

  5. Kafka在高并发的情况下,如何避免消息丢失和消息重复?kafka消费怎么保证数据消费一次?数据的一致性和统一性?数据的完整性?

    1.kafka在高并发的情况下,如何避免消息丢失和消息重复? 消息丢失解决方案: 首先对kafka进行限速, 其次启用重试机制,重试间隔时间设置长一些,最后Kafka设置acks=all,即需要相应的 ...

  6. Kafka消费不到数据的特殊情况

    我大约是把kafka消费不到数据的特殊情况都经历了一遍了吧= =. kafka消费不到数据的原因,首先检查配置之类的,如是否设置了group.id,对应的topic是否正确等等,这些不多说. 下面是我 ...

  7. 聊聊mq中消息消费的几种方式

    mq系列文章 对mq了解不是很多的,可以看一下下面两篇文章: 聊聊mq的使用场景 聊聊业务系统中投递消息到mq的几种方式 聊聊消息消费的几种方式 如何确保消息至少消费一次 如何保证消息消费的幂等性 本 ...

  8. 开源基于Canal的开源增量数据订阅&消费中间件

    CanalSync canal 是阿里巴巴开源的一款基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB). 我开发的这个CanalSync项目 ht ...

  9. SparkStreaming消费kafka中数据的方式

    有两种:Direct直连方式.Receiver方式 1.Receiver方式: 使用kafka高层次的consumer API来实现,receiver从kafka中获取的数据都保存在spark exc ...

  10. 包银消费CTO汤向军:消费金融大数据风控架构与实践

    1 业务架构 风控平台是相对独立的系统,信审的案件可以从借款端平台推过来,也可以从第三方平台推过来.信审案件到达风控平台后,自动创建工作流,根据风控流程处理各流程环节任务. •自动决策 风控流程自动处 ...

随机推荐

  1. Transformers包使用记录

    Transformers是著名的深度学习预训练模型集成库,包含NLP模型最多,CV等其他领域也有,支持预训练模型的快速使用和魔改,并且模型可以快速在不同的深度学习框架间(Pytorch/Tensorf ...

  2. golang之json.RawMessage

    RawMessage 具体来讲是 json 库中定义的一个类型.它实现了 Marshaler 接口以及 Unmarshaler 接口,以此来支持序列化的能力.注意上面我们引用 官方 doc 的说明. ...

  3. python系统模块之re

    正则模块re: 元字符: 字符 描述 . 除换行符外的任意字符 \ 转义字符 [...] 字符集合,匹配任务其中一个 \d 数字:[0-9] \D 非数字:[^\d] \w 单词字符[A-Za-z0- ...

  4. golang之常用第三方包汇总

    汇总golang日常开发中常用的库包 [web] gin:  github.com/gin-gonic/gin [MySQL] gorm: [Redis] go-redis:  github.com/ ...

  5. 什么是.NET的强类型字符串(Strongly typed string)?

    在.NET中,强类型字符串(Strongly typed string)并不是一个官方的概念,是指使用特定的结构来表示某种类型字符串数据的编码实践.类似于枚举,可以提供编译时检查类型,减少运行时错误, ...

  6. Ant Design X:卓越的AI界面解决方案

    Ant Design X:卓越的AI界面解决方案 ​​ Ant Design X 是 Ant Design 的全新 AGI 组件库,旨在帮助开发者更轻松地研发 AI 产品用户界面.Ant Design ...

  7. Java并发 —— 线程并发(二)

    Java 锁  Java 中的锁是在多线程环境下,保证共享资源健康,线程安全的一种手段  线程操作某个共享资源之前,先对资源加一层锁,保证操作期间没有其他线程访问资源,操作完成后再释放锁 保持数据一致 ...

  8. acode连接termux

    在acode中安装AcodeX - Terminal插件 在termux中运行 curl -sL https://raw.githubusercontent.com/bajrangCoder/acod ...

  9. 腾讯云对象存储COS获Veritas认证,数据安全能力再升级

    近日获悉,腾讯云对象存储 COS 正式通过 Veritas 备份软件标准化测试,为数据安全再添新助力. Veritas 对 COS 的支持已经从底层打通,目前 Veritas 的 NetBackup ...

  10. 若依多模块版本,Linux下用Tomcat部署

    若依多模块版本 简介 下载地址 https://gitee.com/y_project/RuoYi 下载项目,打war包 下载项目 到 Gitee 下载项目 用 idea 打开,会自动下载 pom 依 ...