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
程序就會正常運作了。