关于Flask使用Celery的实践经验分享
最近大Boss反馈Celery经常出现问题,几经实践终于把问题解决了!于是乎有了这篇博客的诞生,算是一个实践经验的分享吧!
软件版本如下:
Celery (4.1.)
Flask (0.12.)
RabbitMQ(3.6.)
librabbitmq (1.6.)
介绍
简单来说Celery是一个异步的任务队列,当我们需要将一些任务(比如一些需要长时间操作的任务)异步操作的时候,这时候Celery就可以帮到我们,另外Celery还支持定时任务(类似Crontab)。详细的介绍可以参考官网
使用RabbitMQ作为Broker
RabbitMQ是官方推荐使用的Broker,它实际是一个消息中间件,负责消息的路由分发,安装RabbitMQ如下:
# install on Ubuntu
apt-get update
apt-get install rabbitmq-server -yq
需要注意的是,线上环境我们需要创建新的账号,并将guest账号删除,操作如下:
rabbitmqctl add_user myuser mypassword # 新增用户
rabbitmqctl add_vhost myvhost # 新增vhost,以使用不同的命名空间
rabbitmqctl set_permissions -p myvhost myuser ".*" ".*" ".*" # 设置权限
rabbitmqctl delete_user guest # 安全原因,删除guest
注意:vhost是一个虚拟空间,用于区分不同类型的消息
然后,在Celery的配置中配置broker URL:
CELERY_BROKER_URL = 'amqp://myuser:mypassword@localhost:5672/myvhost'
注意:当使用amqp协议头时,如果安装有librabbitmq
则使用librabbitmq
,否则使用pyamqp
Celery的日志输出
在task中想要输出日志,最好的方法是通过如下方式:
from celery.utils.log import get_task_logger lg = get_task_logger(__name__) @celery.task
def log_test():
lg.debug("in log_test()")
但是仅如此会发现所有的日志最后都跑到shell窗口的stdout当中,原来必须得在启动celery的时候使用-f option来指定输出文件,如下:
celery -A main.celery worker -l debug -f log/celery/celery_task.log &
-A:指定celery实例
worker: 启动worker进程
-l:指定log level,这里指定log level为debug level
-f:指定输出的日志文件
使用Redis作为backend
当使用Redis作为存储后端的时候,我们可以通过设置DB number来使得Celery的结果存储与其它数据存储隔离开来,比如在笔者的项目中,redis还用作缓存的存储后端,因此为了区分,Celery在使用Redis的时候使用的DB number是1(默认是0),关于Redis DB number可以参考这里.
因此我们的backend设置如下:
CELERY_RESULT_BACKEND = 'redis://localhost:6379/1' # 最后的数字1代表DB number
查看Celery任务的结果可以通过Redis-cli连接Redis数据库进行查看:
> redis-cli
> select # 这里选择DB , 也可以在使用redis-cli -n 1来进入指定的DB
> get key # 获取指定key对应的结果
Celery可能会遇到的坑
Celery4.x版本使用librabbitmq的问题
Celery 4.x版本在使用librabbitmq时,会出现类似这样的错误
Received and deleted unknown message. Wrong destination?!?
解决这个问题有两个方式:
- 推荐方式,更改配置项task_protocol为1。
Github上Robert Kopaczewski详细解释了这个问题,原文如下:
Apparently librabbitmq issue is related to new default protocol in celery .x. You can switch to previous protocol version by either
putting CELERY_TASK_PROTOCOL = in your settings if you're using Django or settings app.conf.task_protocol = 1 in celeryconf.py.
- 另一种方式是不使用librabbitmq, 通过pip uninstall librabbitmq, 并且更改broker配置的协议头为'pyamqp',如下,也可以解决这个问题。
BROKER_URL = 'pyamqp://guest:guest@localhost:5672/%2F'
由于librabbitmq的性能优势,我们还是推荐方式1来解决该问题。
RabbitMQ远程连接问题
如果RabbitMQ与Celery不在同一台机器上,除在Celery配置的时候要将BROKER_URL
设置为正确的IP地址外,还需要将Rabbitmq的配置文件/usr/local/etc/rabbitmq/rabbitmq-env.conf
中的NODE_IP_ADDRESS
更改为0.0.0.0
NODE_IP_ADDRESS=0.0.0.0
Celery import问题
The message has been ignored and discarded. Did you remember to import the module containing this task?
Or maybe you're using relative imports? Please see
http://docs.celeryq.org/en/latest/internals/protocol.html
for more information. The full contents of the message body was:
'\x8e\xa7expires\xc0\xa3utc\xc3\xa4args\x91\x85\xa3tid\xb85971a43d47f84bb278f77fc2\xa3sen\xa2A1\xa2tt\xa2ar\xa2co\xc4\x00\xa1t\xa4like\xa5chord\xc0\xa9callbacks\xc0\xa8errbacks\xc0\xa7taskset\xc0\xa2id\xc4$c133dbf8-2c89-4311-b7cf-c377041058ec\xa7retries\x00\xa4task\xd9$tasks.messageTasks.send_like_message\xa5group\xc0\xa9timelimit\x92\xc0\xc0\xa3eta\xc0\xa6kwargs\x80' (239b)
Traceback (most recent call last):
File "/Users/liufeng/.pyenv/versions/2.7.13/envs/kaopu_backend/lib/python2.7/site-packages/celery/worker/consumer/consumer.py", line , in on_task_received
strategy = strategies[type_]
KeyError: u'tasks.messageTasks.send_like_message'
出现这条错误是由于我们的tasks跟celery并不是在同一个文件中,即不是同一个module,当我们通过如下命令启动task worker时,实际只加载了app module,而没有加载tasks相关的module
celery -A app.celery worker -l info
要解决这个问题,必须为celery配置文件添加import参数,如下
app.config['imports'] = ['tasks.messageTasks']
Celery unregistered task问题
在开发过程中遇到了这样一个问题:
[-- ::,: ERROR/MainProcess] Received unregistered task of type u'app.tasks.messageTasks.send_follow_message'.
The message has been ignored and discarded. Did you remember to import the module containing this task?
Or maybe you're using relative imports? Please see
http://docs.celeryq.org/en/latest/internals/protocol.html
for more information. The full contents of the message body was:
'\x8e\xa7expires\xc0\xa3utc\xc3\xa4args\x91\x86\xa6sender\xa5Jenny\xa9target_id\xb859a5313847f84be534ad7d46\xabtarget_type\xa4user\xa7content\xc4\x00\xa8receiver\xb859a5313847f84be534ad7d46\xa4type\xa6follow\xa5chord\xc0\xa9callbacks\xc0\xa8errbacks\xc0\xa7taskset\xc0\xa2id\xc4$a4d40c14-1976-41a6-a753-d2a495929920\xa7retries\x00\xa4task\xd9*app.tasks.messageTasks.send_follow_message\xa5group\xc0\xa9timelimit\x92\xc0\xc0\xa3eta\xc0\xa6kwargs\x80' (312b)
Traceback (most recent call last):
File "/Users/liufeng/.pyenv/versions/2.7.13/envs/kaopu_backend/lib/python2.7/site-packages/celery/worker/consumer/consumer.py", line , in on_task_received
strategy = strategies[type_]
KeyError: u'app.tasks.messageTasks.send_follow_message'
解决这个问题,最开始是根据提示,将所有涉及到task的module全部加上from __future__ import absolute_import
之后运行之后还是不行,后来发现是由于之前启动时使用的是app module, 但是我的代码已经改成了main.py,所以重新启动了celery,最后问题解决
使用镜像迁移系统也依然需要重新添加rabbitmq的用户
问题最开始是发现无法点赞,也无法Follow用户,通过http消息发现出现502错误,于是登录到服务器检查,发现应用服务本身没有任何报错,于是又去查看Celery的日志,结果发现出现如下错误:
[-- ::,: ERROR/MainProcess] consumer: Cannot connect to amqp://celeryuser:**@loc alhost:5672/celeryvhost: Couldn't log in: a socket error occurred.
经过一番搜索发现网上的评论主要是说URL不对的情况下会出现这种情况,但是我的URL没有改过啊,那又会是什么问题呢?继续看,发现有人提到了权限问题,于是又是一番检查,发现RabbitMQ中并没有原先设置的用户(我使用的是原系统的镜像,原以为用户也是已经设置好的)
# 查看有哪些用户
rabbitmqctl list_users
然后就简单了,按照步骤创建用户,vhost,再赋予权限,删除guest,然后就终于都连好了
另外,发现从镜像复制系统后,RabbitMQ并不能正常工作,必须杀掉原先的进程,重新启动
更改task的代码后,重启Celery
需要注意的是,在更改task的代码后,必须重新启动Celery,否则代码改动无法生效,可能导致一些意外的问题
以上就是笔者使用过程中的一些坑,经验有限,如果有错漏还请指正!不要看格式啦,因为之间都是习惯写印象笔记,所以你们就将就着看吧!
关于Flask使用Celery的实践经验分享的更多相关文章
- 领域驱动设计(DDD)的实践经验分享之ORM的思考
原文:领域驱动设计(DDD)的实践经验分享之ORM的思考 最近一直对DDD(Domain Driven Design)很感兴趣,于是去网上找了一些文章来看看,发现它确实是个好东西.于是我去买了两本关于 ...
- 领域驱动设计(DDD)的实践经验分享之持久化透明
原文:领域驱动设计(DDD)的实践经验分享之持久化透明 前一篇文章中,我谈到了领域驱动设计中,关于ORM工具该如何使用的问题.谈了很多我心里的想法,大家也对我的观点做了一些回复,或多或少让我深深感觉到 ...
- 我的devops实践经验分享一二
前言 随着系统越来越大,开发人员.站点.服务器越来越多,微服务化推进,......等等原因,实现自动化的devops越来越有必要. 当然,真实的原因是,在团队组建之初就预见到了这些问题,所以从一开始就 ...
- CI Weekly #6 | 再谈 Docker / CI / CD 实践经验
CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...
- Celery的实践指南
http://www.cnblogs.com/ToDoToTry/p/5453149.html Celery的实践指南 Celery的实践指南 celery原理: celery实际上是实现了一个典 ...
- Expression Blend4经验分享:自适应布局浅析
今天分享一下Blend制作自适应分辨率布局的经验,大家先看下效果图: 这是一个标准的三分天下的布局,两侧的红色区域是背景区域,是用来干吗的呢,下面简单的分析一下,大家就明白了. 1.拿到一个项目,进行 ...
- celery最佳实践
作为一个Celery使用重度用户.看到Celery Best Practices这篇文章.不由得菊花一紧. 干脆翻译出来,同一时候也会添加我们项目中celery的实战经验. 至于Celery为何物,看 ...
- 安装程序添加iis的方法经验分享
原文:安装程序添加iis的方法经验分享 网上有一些这样的方法,但我这里主要做一些对比和扩充 网上这方面的文章的岁数比较大,server 08R2和win7出来后,整理这方面的资料的文章没找到,所以这里 ...
- 以技术面试官的经验分享毕业生和初级程序员通过面试的技巧(Java后端方向)
本来想分享毕业生和初级程序员如何进大公司的经验,但后来一想,人各有志,有程序员或许想进成长型或创业型公司或其它类型的公司,所以就干脆来分享些提升技能和通过面试的技巧,技巧我讲,公司你选,两厢便利. 毕 ...
随机推荐
- python 基础 4.2 高阶函数上
一.高阶函数 把函数当做参数传递的一种函数 1>map()函数 map函数是python内置的一个高阶函数,它接受一个函数f和一个list,并把list元素以此传递给函数f,然后返回一个函数 ...
- ElasticSearch架构思考(转)
一个ElasticSearch集群需要多少个节点很难用一种明确的方式回答,但是,我们可以将问题细化成一下几个,以便帮助我们更好的了解,如何去设计ElasticSearch节点的数目: 打算处理多少数据 ...
- Asynchronous_method_invocation 异步方法调用 让步 yielding
zh.wikipedia.org/wiki/同步 [同步不同事件发生 时间一致] 同步(英语:Synchronization),指在一个系统中所发生的事件(event),之间进行协调,在时间上出现一致 ...
- 【题解】P3162CQOI2012组装
[题解][CQOI2012]组装 考虑化为代数的形式,序列\(\left[a_i \right]\)表示选取的\(i\)种类仓库的坐标. \(ans=\Sigma(a_i-x)^2,(*)\),展开: ...
- 关于Python有用的snippets
1.将字典的key,value反转换位置 值value可以取任何数据类型,但键key必须是不可变的,如字符串,数字或元组. dict1={'Lisa':1,'Bob':2,'Mick':3} dict ...
- Vue:实践学习笔记(5)——Vue-Cli脚手架的使用
Vue:实践学习笔记(5)——Vue-Cli脚手架的使用 快速开始 项目配置 可视化配置 vue ui 命令配置 vue init webpack vue-demo(项目名) 运行测试 进入vue-d ...
- ubuntu 搭建 tomcat
一.下载tomcat 先下载到本地,然后ftp上传到服务器 官方 Apache Tomcat 的下载 2 二.解压安装 先解压 tar zxvf apache-tomcat-7.0.64.tar.gz ...
- Android 普通okhttp、okhttp utils执行 post get请求,文件上传下载、请求图片
public class OKHttpActivity extends Activity implements View.OnClickListener { public static final M ...
- leetcode 859. Buddy Strings
Given two strings A and B of lowercase letters, return true if and only if we can swap two letters i ...
- UVA - 11475 Extend to Palindrome —— 字符串哈希 or KMP or 后缀数组
题目链接:https://vjudge.net/problem/UVA-11475 题意: 给出一个字符串,问在该字符串后面至少添加几个字符,使得其成为回文串,并输出该回文串. 题解: 实际上是求该字 ...