FastAPI+Redis+asyncio.gather重启Redis后出现内部服务器错误
重启Redis后redis-py异步接口持续返回500错误的原因与解决方法
问题描述
重启Redis服务后,尽管Redis已恢复运行,但项目中的部分接口仍连续返回500错误,具体复现情况如下:
环境信息
- Python 3.10.10
- Redis 7.0.5
- 依赖包:
redis==4.5.4、aioredis==2.0.1
复现场景
- 测试脚本:启动Redis后运行脚本,每3秒输出正常结果;停止Redis后能捕获连接异常;重启Redis后,首次调用
asyncio.gather执行批量Redis操作时,结果中仍会出现遗留的连接错误,直到下一次循环才恢复正常。但替换为aioredis客户端则无此问题。 - FastAPI接口:正常请求返回200;停止Redis后请求返回500;重启Redis后,前两次请求仍返回500,第三次请求才恢复正常。如果移除代码中的
asyncio.gather或首个await操作,恢复正常的速度会变快。
疑问:这是redis库的Bug,还是我的使用方式有误?
原因分析
这是redis-py 4.x版本在异步连接池处理上的已知行为,并非你的使用方式错误:
redis-py的异步连接池不会主动清理已断开的失效连接,Redis重启后,连接池仍会尝试复用这些旧连接,导致请求报错,直到连接池将这些失效连接标记并移除。asyncio.gather会批量执行多个异步任务,一旦其中某个任务持有失效连接,就会导致整批任务出现错误;而aioredis的连接池实现了更主动的失效连接检测与清理逻辑,因此不会出现该问题。- FastAPI场景中,前两次请求仍在复用池中的失效连接,直到连接池完成失效连接的淘汰流程,第三次请求才能获取到新的有效连接。
解决方案
针对这个问题,可通过以下几种方式解决:
- 切换到aioredis:既然
aioredis不存在此问题,且它是Redis官方推荐的异步客户端,直接迁移使用aioredis是最省心的方案。 - 开启连接池健康检查:初始化
redis-py的异步连接池时,设置health_check_interval参数,让连接池定期检测连接有效性,自动剔除失效连接:
from redis.asyncio import ConnectionPool, Redis # 初始化连接池,每5秒检查一次连接健康 pool = ConnectionPool.from_url( "redis://localhost:6379", health_check_interval=5 ) redis_client = Redis(connection_pool=pool)
- 捕获错误后强制清理连接池:在捕获到连接异常时,主动调用连接池的
disconnect方法,强制清理所有失效连接,之后重新执行任务:
import asyncio from redis.asyncio import Redis redis_client = Redis.from_url("redis://localhost:6379") async def batch_redis_ops(): try: return await asyncio.gather( redis_client.get("key1"), redis_client.get("key2") ) except ConnectionError: # 清理失效连接 await redis_client.connection_pool.disconnect() # 重新执行任务 return await asyncio.gather( redis_client.get("key1"), redis_client.get("key2") )
- 调整批量操作逻辑:如果无法修改连接池配置,可以暂时避免在
asyncio.gather中批量执行Redis操作,或者确保每个操作使用独立连接(但会降低性能,仅作为临时方案)。
内容的提问来源于stack exchange,提问作者Vadim
相关产品推荐
相关产品推荐

