awakeBird Back-end Dev Engineer

项目中引入celery

2017-01-11

捣鼓了两天自己的直播通项目,终于大概有了雏形,在主页的视图函数中集成了爬虫,爬取用户关注主播的在线hangtag。但在运行时发现打开主页时延迟明显,有时候竟然长达数秒,突然想起了之前了解到的celery,试试能不能解决这一问题。

celery是什么

关于celery的知识全部在网上了解,星星点点,但还是有所收获。celery是一个典型的异步任务系统,在应用上下文之外执行任务,将消耗资源的东西通通交给celery来做,可以让主机迅速向应客户端的请求。

celery有三个核心组件:

  • 客户端:在flask系统中和flask一起运行
  • workers:就是传说中的second terminal,用来执行异步任务,可以有多个
  • 消息代理:用来进行celery的通信,一般用redis。

为什么用redis?因为其实时性强,一般用作数据频繁插入,更新或者删除的任务中,以减少对数据库的操作

让celery跑起来

这部分还是比较简单的,有几个主要步骤。

  • 安装之后要在配置文件中写入用作消息代理的redis的服务器,两行搞定。
    CELERY_BROKER_URL = 'redis://localhost:6379/0'
    CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
    
  • 之后创建celery实例,这里是直接抄来的,至于原因还没搞懂。
    celery = Celery(__name__, broker=Config.CELERY_BROKER_URL)
    celery.conf.update(app.config)
    
  • 创建celery任务,使用装饰器,先跑起来再说。
    @celery.task(name='circle_task')
    def circletask():
      logging.info('lalala')
    

之后满怀信心,直接打开三个终端开始有样学样,第一个打开redis-server,第二个运行app,第三个打开celery,抄了个命令:

$ celery -A manage.celery worker --loglevel=info

然而打开后发现没有动静,才想起并没有提供任务执行的delay。随即又想到我的目的是进行计划任务,让任务可以固定时间重复进行,直接开始搜celery进行实现的方法,还好google够强大,又找到了下面的配置方法:

CELERYBEAT_SCHEDULE = {
        'every-minute': {
            'task': 'circle_task',
            #'schedule': crontab(minute='*/1'),
            # 'args': (1,2),
            'schedule': timedelta(seconds=60)
        	},
    	}

运行命令也变成了:

$ celery -A manage.celery worker --loglevel=info --beat
$ celery -A proj worker -B -l info

至于里面的参数,只知道timedelta和crontab都可以设置间隔时间,其他的没有深入了解。将代码改好后运行celery,问题来了。

  • 首先是celery说任务方法没有注册:
    Received unregistered task of type 'circle_task'.
    The message has been ignored and discarded.
    

在stack overflow溜达了一圈,终于找到解决方法,在配置文件中声明这个方法即可,于是:

CELERY_IMPORTS = ['app.task']

解决成功。这里千万注意加app,天知道我经历了什么。

  • 之后运行celery,终于不报错了天呐,屏幕上也出现了lalala的logging,这还说什么,改代码!在task中添加了之前在index视图函数中的爬虫部分,运行celery,好,可以运行。等等,这一大片红字是什么!
    'RuntimeError: application not registered on db 
             instance and no application bound to current context
    

    原来是没有程序上下文不能进行sqlalchemy的操作,于是,在程序段前添加

    with app.app_context():
    

    成功!

此处输入图片的描述

最后终于成功看到了模拟上线提醒的logging字样,激动万分,编程的大起大落真是…哈!


Comments

Content