【Python】Flask报错:TimeoutError: QueuePool limit of size 10...以及日常bug处理

1.问题背景

最近在使用Flask框架的flask_sqlalchemy时,突然遇到这个错误:

QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00 (Background on this error at: https://sqlalche.me/e/20/3o7r)

 问题出来,刚开始无从下手,以为框架出问题了,经过对官方的研究和网友们的提示,最终整理一下,为以后遇到此类问题做个积累。

2.问题分析:

产生该问题的原因是因为 SQLAlchemy 连接池的配置问题、并发太高等都可能引起该错误,

还有一个典型的错误原因是,在客户端发送一个请求后, flask后台执行一个数据库操作,随后进行某种逻辑操作,如果该逻辑出现错误,会为客户端返回500错误码,此时该连接未释放,因此会导致数据库连接池继续占用。

2.1 方式一:增加数据库连接池

可以增加数据库连接池的数量进行更新,更改的方式如下:

from flask import Flask
app = Flask(__name__)

# 解决跨域
CORS(app)

app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://root:{encoded_password}@127.0.0.1:3306/test"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_POOL_SIZE'] = 100
# 自动提交映射数据
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

其中这个是核心,这个就是解决这个问题的方式之一:(本例中将数据库连接池改为100,大家根据自己的需求进行相应的更新)

app.config['SQLALCHEMY_POOL_SIZE'] = 100

提供一下官方的地址,可以进行查看 

Configuration — Flask-SQLAlchemy Documentation (3.1.x)

Flask的SQLAlchemy — Flask Documentation (3.0.x)

SQLAlchemy - The Database Toolkit for Python

如果采用 db.create_engine 这种方式,没有设置pool_size的大小,默认为5。加上pool_size=100后,此问题不再出现。可以这么设置:

engin = db.create_engine("mysql://root:password@localhost:3306/dbname?charset=utf8", pool_size=100, max_overflow=20)

2.2 方案二:增加try_except

    try:
        r = requests.get('http://github.com', timeout=0.001)
    except:
        r = None

https://blog.youkuaiyun.com/weixin_42474540/article/details/105087429

3.问题解决后,再提几个细节上的内容

import urllib.parse

password = "abc@123"
encoded_password = urllib.parse.quote(password)

然后这么去使用:

app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://root:{encoded_password}@127.0.0.1:3306/test"

这样就能避免当你密码中带特殊符号的特殊情况了

https://blog.youkuaiyun.com/qq_41020281/article/details/82433096

https://blog.youkuaiyun.com/byna11sina11/article/details/120343803

4.线程池样例编写

# 创建线程池,设置最大线程数(根据机器性能、IO密集型还是CPU密集型进行合理设置线程数的大小)
vector_executor = ThreadPoolExecutor(max_workers=5)
# 线程池方法调用
futures = [vector_executor.submit(vector_task, [id]) for id in ids]


# 异步执行向量化,定义线程池和任务函数
def vector_task(id_tuple):
        pass
2025-03-17 10:13:54,82 ERROR [app.py:875] 2dc6edd3fa Exception on /console/api/login [POST] Traceback (most recent call last): File "e:\Dify\dify-main\.venv\Lib\site-packages\flask\app.py", line 917, in full_dispatch_request rv = self.dispatch_request() File "e:\Dify\dify-main\.venv\Lib\site-packages\flask\app.py", line 902, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ File "e:\Dify\dify-main\.venv\Lib\site-packages\flask_restful\__init__.py", line 489, in wrapper resp = resource(*args, **kwargs) File "e:\Dify\dify-main\.venv\Lib\site-packages\flask\views.py", line 110, in view return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^ resp = meth(*args, **kwargs) File "E:\Dify\dify-main\api\controllers\console\wraps.py", line 187, in decorated return view(*args, **kwargs) File "E:\Dify\dify-main\api\controllers\console\auth\login.py", line 54, in post is_login_error_rate_limit = AccountService.is_login_error_rate_limit(args["email"]) File "E:\Dify\dify-main\api\services\account_service.py", line 493, in is_login_error_rate_limit count = redis_client.get(key) File "e:\Dify\dify-main\.venv\Lib\site-packages\redis\commands\core.py", line 1822, in get return self.execute_command("GET", name, keys=[name]) ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^ File "e:\Dify\dify-main\.venv\Lib\site-packages\redis\client.py", line 559, in execute_command return self._execute_command(*args, **options) ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^ File "e:\Dify\dify-main\.venv\Lib\site-packages\redis\client.py", line 565, in _execute_command conn = self.connection or pool.get_connection(command_name, **options) ~~~~~~~
最新发布
03-18
<think>好的,我现在需要解决用户在Flask应用的登录接口中遇到的Redis连接异常问题。首先,我需要理清楚用户的问题描述。用户提到在登录接口抛出了Redis连接异常,并且需要解决方案。此外,用户还提供了两个引用,一个是关于Flask的应用上下文和请求上下文,另一个是关于系统架构的,可能对问题帮助不大,但需要留意。 首先,我应该考虑可能导致Redis连接异常的常见原因。通常,这类问题可能涉及配置错误、Redis服务未运行、网络问题、连接池设置不当,或者上下文问题,比如在错误的上下文中访问Redis连接。 根据用户提供的引用[^1],提到了应用上下文和请求上下文,以及手动创建上下文的两种方法。这提示我可能需要检查Redis连接是否在正确的上下文中被初始化或访问。例如,如果Redis的连接对象是在应用上下文中创建的,但在请求上下文中被错误地访问,或者没有正确初始化,可能导致异常。 接下来,我需要分步骤分析可能的原因和解决方案: 1. **检查Redis服务状态**:确认Redis服务器是否正在运行,并且可以从Flask应用所在的机器访问。可以通过命令行工具如`redis-cli ping`来测试连接,如果返回PONG,说明服务正常。 2. **检查Flask配置**:查看Flask应用的配置是否正确设置了Redis的连接参数,比如主机名、端口、密码、数据库编号等。常见的配置项包括`REDIS_URL`、`REDIS_HOST`、`REDIS_PORT`等。例如,`app.config['REDIS_HOST'] = 'localhost'`,默认端口是6379,如果没有密码可以留空,但如果有的话,需要配置`REDIS_PASSWORD`。 3. **验证连接代码**:检查在Flask应用中初始化Redis连接的代码是否正确。通常使用`redis.Redis`或`redis.StrictRedis`来创建连接实例。例如: ```python import redis redis_conn = redis.Redis(host=app.config['REDIS_HOST'], port=app.config['REDIS_PORT'], db=0) ``` 或者使用连接池: ```python pool = redis.ConnectionPool(host='localhost', port=6379, db=0) redis_conn = redis.Redis(connection_pool=pool) ``` 需要确保这些参数与配置一致。 4. **上下文问题**:Flask的应用上下文和请求上下文可能会影响某些扩展。例如,如果Redis连接是在应用上下文中创建的,但在请求处理时没有正确激活上下文,可能导致连接不可用。用户引用中提到可以使用`with app.app_context()`来手动创建上下文,或者在需要时获取当前应用对象。如果在视图函数外部访问Redis,可能需要确保在正确的上下文中执行。例如,在后台任务或脚本中,可能需要显式推送应用上下文。 5. **异常处理和重试机制**:在代码中添加适当的异常处理,比如捕获`redis.exceptions.ConnectionError`,并实现重试逻辑,或者记录详细的错误信息,帮助诊断问题。例如: ```python try: redis_conn.ping() except redis.exceptions.ConnectionError as e: current_app.logger.error(f"Redis连接失败: {str(e)}") raise ``` 6. **检查网络问题**:确保Flask应用所在的服务器能够访问Redis服务器。检查防火墙设置,端口是否开放(默认6379),以及是否有安全组规则阻止了连接。 7. **依赖版本兼容性**:确认使用的Redis客户端库(如`redis-py`)版本与Flask应用兼容,没有已知的bug导致连接问题。可以尝试升级或降级版本。 8. **查看Traceback详细信息**:用户的异常跟踪信息(traceback)可能提供了具体的错误位置和原因。例如,是否在尝试执行Redis命令时抛出了连接超时,或者认证失败。根据具体的错误信息进一步排查。 9. **测试连接代码**:编写一个简单的测试脚本,独立于Flask应用,尝试连接Redis,看是否成功。这有助于确定问题是出在Redis配置还是Flask应用本身。 10. **使用Flask扩展**:如果使用Flask-Redis等扩展,检查是否正确初始化和配置。例如,Flask-Redis可能需要通过`init_app`方法初始化。 结合用户提供的引用,应用上下文可能在Redis连接初始化时起到关键作用。例如,如果在应用工厂模式中创建Redis实例,可能需要使用`init_app`方法,并在请求处理时确保应用上下文已激活。 可能的解决方案步骤总结: - 检查Redis服务状态和网络连接。 - 核对Flask配置中的Redis连接参数。 - 确保Redis连接在正确的上下文中初始化和访问。 - 添加详细的日志记录和异常处理,以捕获连接问题。 - 测试独立于Flask的Redis连接,验证基本功能。 - 更新或确保Redis客户端库的版本正确。 如果用户提供了具体的Traceback信息,可以更精准地定位问题,比如连接超时、认证错误,或是资源不足等。</think>针对Flask登录接口的Redis连接异常问题,以下是分步解决方案: ### 1. 确认Redis服务状态 - **检查Redis是否运行**:在终端执行 `redis-cli ping`,若返回 `PONG` 表示服务正常。 - **验证网络可达性**:在Flask应用服务器执行 `telnet <Redis_IP> 6379`,确认端口可访问。 ### 2. 核对Flask配置 在 `config.py` 或应用初始化代码中,需包含正确的Redis参数: ```python app.config["REDIS_URL"] = "redis://:密码@主机:端口/数据库编号" # 示例:redis://:mypass@127.0.0.1:6379/0 ``` 若使用密码,需确保配置文件与实际Redis服务一致。 ### 3. 检查连接初始化代码 **推荐使用连接池管理**: ```python from redis import Redis redis_conn = None def init_redis(app): global redis_conn redis_conn = Redis.from_url(app.config["REDIS_URL"], socket_timeout=5) ``` 在工厂函数中调用: ```python def create_app(): app = Flask(__name__) init_redis(app) return app ``` ### 4. 处理上下文问题 若在非请求上下文(如后台线程)访问Redis,需手动推送应用上下文: ```python with app.app_context(): # 确保在应用上下文中操作 redis_conn.set("key", "value") ``` ### 5. 添加异常捕获与重试 在登录接口代码中包裹Redis操作: ```python @app.route("/login", methods=["POST"]) def login(): try: redis_conn.ping() # 测试连接 # 执行登录逻辑(如存储session) except redis.exceptions.ConnectionError as e: current_app.logger.error(f"Redis连接异常: {str(e)}") return {"code": 500, "msg": "缓存服务不可用"}, 500 except redis.exceptions.TimeoutError: current_app.logger.warning("Redis响应超时,尝试重连...") redis_conn = Redis.from_url(app.config["REDIS_URL"]) # 重新初始化 ``` ### 6. 验证依赖版本 检查 `requirements.txt` 中Redis客户端版本: ```text redis>=4.3.4 # 推荐使用4.x+版本 ``` 执行 `pip install --upgrade redis` 更新库。 ### 7. 连接参数优化 若频繁出现超时,调整连接参数: ```python Redis.from_url( app.config["REDIS_URL"], socket_connect_timeout=3, # 连接超时3秒 socket_keepalive=True, # 启用TCP保活 max_connections=100 # 连接池大小按需调整 ) ``` ### 典型错误排查表 | 错误现象 | 可能原因 | 解决方案 | |---------|---------|---------| | `Connection refused` | Redis未启动/端口错误 | 检查服务状态及防火墙规则 | | `NOAUTH Authentication required` | 密码错误/未配置 | 核对配置文件密码 | | `Timeout` | 网络延迟/连接池耗尽 | 调整超时参数,增加连接池大小 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex_81D

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值