1.基本概况
近期项目进行横向拓展会用到celery,于是开始研究celery的手册。在看手册的过程中发现,基本所有的官方例子都是运行在一台设备上,按照例子很容易就调通了。但实际的运行环境中肯定是部署在不同的设备上。那么如何部署在两个设备上呢?
2. 开始研究
- 运行环境:
2台ubuntu虚拟机,网段分别为(192.168.140.100和192.168.140.101)
celery+rabbitMQ - 代码环境:
(1)celery_master.py
#! /usr/bin/env python
# -*-coding:utf-8 -*-
import time
from celery import Celery
app = Celery('celery_worker', broker='amqp://guest@localhost//', backend='amqp://guest@localhost//')
这里构建了一个celery中的application,其中broker采用本地rabbitMQ,账户为guest账户。这里的第一个参数要指定woker的名称。
PS: 这里特别需要注意第一个参数,这个参数为__main__
。
main
原手册地址
Name of the __main__
module. Required for standalone scripts.
If set this will be used instead of __main__
when automatically generating task names.
根据这段话,其实说明白点就是要执行具体任务的入口函数的地址,这里相当于执行celery_worker.__main__
。 这里有个问题,如果你使用的是task.delay
,这种方式,则必须指定这个celery为‘celery_worker’,告诉系统执行的是‘celery_worker’中的add。这块其实感觉比较混乱,总之,就是你要让任务找到它的执行空间就行。如果你采用send_task
去完成这个任务,这块你的两个app对象的第一个参数就不需要如此考究了,因为你可以在send_task指定执行的是哪个函数。
例如:
result = app.send_task('celery_worker.add', [1, 2])
对端的工程目录层级中包含一个‘celery_worker.py’的文件,里面有个add的函数。
这里推荐使用‘send_task’这种做法,这里为了展示Celery类实例化时候的第一个参数__main__
的作用,完成了这种案例(不推荐这种写法,只是说明可行)。
@app.task
def add(a, b):
pass
这个地方定义了一个函数,名叫add,接受两个参数,但函数体里面为pass,其实这个很重要。很多人在写celery_master和celery_work的时候把add原封不动的重新copy了一次,两个文件中都实现了add函数。但其实,将任务分发过后,具体执行的add为celery_work中的add。这里我之所以写一个pass的函数,其实相当于C语言中的函数声明,因为发送任务的时候调用了add。
while (flag):
print app.control.ping(timeout=0.5) # 发送ping包,看是否能访问目标地址
result = add.delay(1, 2) # 发送具体的任务和值
print "ID: %s" % result.id
if result.failed():
flag = False
print "Flag: %s" % flag
time.sleep(1)
这段代码很简单,实现的功能就是每隔1分钟发送给目标worker一个任务,任务为add,参数为1、2。
(2)celery_worker.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from celery import Celery
app = Celery('who care', broker='amqp://guest@localhost//', backend='amqp://')
这里同样构建了一个celery中的application,可以看出这里的第一个参数可以随便设置。
@app.task
def add(a, b):
print 'Start task'
time.sleep(3)
print 'Finish task'
return a + b
具体的add逻辑,这里不用多解释了吧。
以上就是整个代码的部分。
为了验证代码能正常运行,将两个代码都放到192.168.140.100设备上。然后两个终端,分别执行
celery -A celery_worker worker --loglevel=info
和python celery_master.py
就能看到它们在本地工作了。
3.问题来了
上面这个例子很明显和官方文档差不了多少,其实这里就是想告诉大家如何委婉的解决master和worker中关于具体任务函数该如何写,当然还有其他方法,自行研究。
现在,问题的关键在于如何在不同的设备上分开部署celery_master和celery_worker。
先说一个问题,既然要部署在不同设备上,rabbitMQ是存在账号这个说法的,之前用的guest账号只能运行在localhost下。官方有明确说明("guest" user can only connect via localhost):http://www.rabbitmq.com/access-control.html
(1)登陆192.168.140.100机器,将celery_master.py拷贝到该机器,然后执行下面操作(http://stackoverflow.com/questions/25869858/celery-error-in-connecting-to-rabbitmq-server):
现在生成一个账号密码为test、test的 rabbitMQ账号,以及一个test-vhost的virtual host。命令如下:
sudo rabbitmqctl add_user test test
sudo rabbitmqctl set_permissions -p test-vhost test ".*" ".*" ".*"
完成上诉两个步骤候开始修改代码:
app = Celery('celery_worker', broker='amqp://test:test@localhost/test-vhost', backend='amqp://')
(2)登陆192.168.140.101机器,将celery_worker.py拷贝到该机器,然后执行下面操作:
修改代码
app = Celery('who care', broker='amqp://test:test@192.168.140.100//', backend='amqp://')
完成上述步骤后,分别在两个机器上启动对应的指令:
192.168.140.100:python celery_master.py
192.168.140.101:celery -A celery_worker worker --loglevel=info
程序就会正常运作了。