Celery从Redis队列读取结果时出现Unicode解码错误
你这情况我之前也碰到过——本地环境跑起来完全正常,到staging服务器就卡在结果获取环节抛解码错误,明明任务都已经执行成功了。结合你的环境差异(Python3.5 vs 3.6)和报错信息,大概率是序列化或者依赖版本兼容性的问题,给你几个具体的排查和解决步骤:
1. 对齐Celery相关依赖版本
Python3.5和3.6对依赖库的支持有差异,先检查本地和staging的Celery、Kombu、redis-py版本是不是一致:
在两边分别执行:
pip freeze | grep -E "celery|kombu|redis"
如果版本不一样,把staging的版本同步到本地的稳定版本。比如我之前用的兼容组合是Celery4.3.0 + Kombu4.6.7 + redis3.3.11(Python3.5和3.6都能稳定运行),你可以根据本地实际版本调整:
pip install celery==4.3.0 kombu==4.6.7 redis==3.3.11
2. 显式指定序列化方式(核心解决方法)
默认情况下Celery用pickle序列化,但不同Python版本的pickle协议不兼容——Python3.6默认用协议4,而Python3.5最高只支持协议3,这就会导致反序列化时出现你看到的UTF-8解码错误。
有两种解决思路:
方案一:改用JSON序列化(推荐,兼容性最好)
修改你的worker初始化代码,强制用JSON来序列化任务和结果:
celery = Celery('workerprocess', broker='redis://localhost:6379/0', backend='redis://localhost:6379/0', task_serializer='json', result_serializer='json', accept_content=['json'])
方案二:指定pickle协议版本为3
如果你必须用pickle(比如任务参数有JSON无法序列化的对象),可以指定协议版本为3:
celery = Celery('workerprocess', broker='redis://localhost:6379/0', backend='redis://localhost:6379/0', task_serializer='pickle', result_serializer='pickle', accept_content=['pickle'], pickle_protocol=3)
注意:修改后一定要重启Celery worker,并且清理Redis里的旧数据避免干扰:
redis-cli FLUSHDB
3. 检查Redis里的结果存储
你用lrange celery 0 0查的是任务队列,结果其实存在celery-task-meta-<task_id>这类键里。可以用以下命令查看所有结果键:
redis-cli KEYS "celery-task-meta-*"
随便挑一个键看看内容:
redis-cli GET "celery-task-meta-xxx"
如果返回的内容是乱码或者无法用UTF-8解码,就坐实了是序列化的问题,按上面第二步改就行。
4. 确认Worker的运行环境
确保staging的Celery worker是用Python3.5启动的,别不小心用了系统自带的Python2.7。启动时可以指定解释器:
python3.5 -m celery -A workerprocess worker --loglevel=info
另外,仔细看worker的日志,确认任务执行时没有隐性错误——比如任务里的代码在Python3.5下有语法或兼容性问题,导致结果没正常序列化就存到Redis里了。
内容的提问来源于stack exchange,提问作者Della

