python celery多worker、多队列、定时任务

多worker、多队列
celery是一个分布式的任务调度模块,那么怎么实现它的分布式功能呢,celery可以支持多台不同的计算机执行不同的任务或者相同的任务。

如果要说celery的分布式应用的话,就要提到celery的消息路由机制,提到AMQP协议。

简单理解:

可以有多个”消息队列”(message Queue),不同的消息可以指定发送给不同的Message Queue,

而这是通过Exchange来实现的,发送消息到”消息队列”中时,可以指定routiing_key,Exchange通过routing_key来吧消息路由(routes)到不同的”消息队列”中去。

celery多worker、多队列、定时任务 - 图1

exchange 对应 一个消息队列(queue),即:通过”消息路由”的机制使exchange对应queue,每个queue对应每个worker。

下面我们来看一个列子:

  1. '''
  2. '''
  3. vi tasks.py
  4. #!/usr/bin/env python
  5. #-*- coding:utf-8 -*-
  6. from celery import Celery
  7. app = Celery()
  8. app.config_from_object("celeryconfig") # 指定配置文件
  9. @app.task
  10. def taskA(x,y):
  11. return x + y
  12. @app.task
  13. def taskB(x,y,z):
  14. return x + y + z
  15. @app.task
  16. def add(x,y):
  17. return x + y

编写配置文件,配置文件一般单独写在一个文件中。

  1. vi celeryconfig.py
  2. #!/usr/bin/env python
  3. #-*- coding:utf-8 -*-
  4. from kombu import Exchange,Queue
  5. BROKER_URL = "redis://47.106.106.220:5000/1"
  6. CELERY_RESULT_BACKEND = "redis://47.106.106.220:5000/2"
  7. CELERY_QUEUES = (
  8. Queue("default",Exchange("default"),routing_key="default"),
  9. Queue("for_task_A",Exchange("for_task_A"),routing_key="for_task_A"),
  10. Queue("for_task_B",Exchange("for_task_B"),routing_key="for_task_B")
  11. )
  12. # 路由
  13. CELERY_ROUTES = {
  14. 'tasks.taskA':{"queue":"for_task_A","routing_key":"for_task_A"},
  15. 'tasks.taskB':{"queue":"for_task_B","routing_key":"for_task_B"}
  16. }

远程客户端上编写测试脚本

  1. vi test.py
  2. from tasks import *
  3. re1 = taskA.delay(100, 200)
  4. print(re1.result)
  5. re2 = taskB.delay(1, 2, 3)
  6. print(re2.result)
  7. re3 = add.delay(1, 2)
  8. print(re3.status)

启动两个worker来分别指定taskA、taskB,开两个窗口分别执行下面语句。

  1. celery -A tasks worker -l info -n workerA.%h -Q for_task_A
  2. celery -A tasks worker -l info -n workerB.%h -Q for_task_B

远程客户端上执行脚本可以看到如下输出:

  1. python test.py
  2. 300
  3. 6
  4. PENDING

在taskA所在窗口可以看到如下输出:

  1. .......
  2. .......
  3. .......
  4. task_A
  5. [tasks]
  6. . tasks.add
  7. . tasks.taskA
  8. . tasks.taskB
  9. [2018-05-27 19:23:49,235: INFO/MainProcess] Connected to redis://47.106.106.220:5000/1
  10. [2018-05-27 19:23:49,253: INFO/MainProcess] mingle: searching for neighbors
  11. [2018-05-27 19:23:50,293: INFO/MainProcess] mingle: all alone
  12. [2018-05-27 19:23:50,339: INFO/MainProcess] celery@workerA.izwz920j4zsv1q15yhii1qz ready.
  13. [2018-05-27 19:23:56,051: INFO/MainProcess] sync with celery@workerB.izwz920j4zsv1q15yhii1qz
  14. [2018-05-27 19:24:28,855: INFO/MainProcess] Received task: tasks.taskA[8860e78a-b82b-4715-980c-ae125dcab2f9]
  15. [2018-05-27 19:24:28,872: INFO/ForkPoolWorker-1] Task tasks.taskA[8860e78a-b82b-4715-980c-ae125dcab2f9] succeeded in 0.0162177120219s: 300

在taskB所在窗口可以看到如下输出:

  1. .......
  2. .......
  3. .......
  4. task_B
  5. [tasks]
  6. . tasks.add
  7. . tasks.taskA
  8. . tasks.taskB
  9. [2018-05-27 19:23:56,012: INFO/MainProcess] Connected to redis://47.106.106.220:5000/1
  10. [2018-05-27 19:23:56,022: INFO/MainProcess] mingle: searching for neighbors
  11. [2018-05-27 19:23:57,064: INFO/MainProcess] mingle: sync with 1 nodes
  12. [2018-05-27 19:23:57,064: INFO/MainProcess] mingle: sync complete
  13. [2018-05-27 19:23:57,112: INFO/MainProcess] celery@workerB.izwz920j4zsv1q15yhii1qz ready.
  14. [2018-05-27 19:24:33,885: INFO/MainProcess] Received task: tasks.taskB[5646d0b7-3dd5-4b7f-8994-252c5ef03973]
  15. [2018-05-27 19:24:33,910: INFO/ForkPoolWorker-1] Task tasks.taskB[5646d0b7-3dd5-4b7f-8994-252c5ef03973] succeeded in 0.0235358460341s: 6

我们看到状态是PENDING,表示没有执行,这个是因为没有celeryconfig.py文件中指定改route到哪一个Queue中,所以会被发动到默认的名字celery的Queue中,但是我们还没有启动worker执行celery中的任务。下面,我们来启动一个worker来执行celery队列中的任务。

celery -A tasks worker -l info -n worker.%h -Q celery
再次在远程客户端执行test.py,可以看到结果执行成功,并且刚新启动的worker窗口有如下输出:

  1. .......
  2. .......
  3. .......
  4. [tasks]
  5. . tasks.add
  6. . tasks.taskA
  7. . tasks.taskB
  8. [2018-05-27 19:25:44,596: INFO/MainProcess] Connected to redis://47.106.106.220:5000/1
  9. [2018-05-27 19:25:44,611: INFO/MainProcess] mingle: searching for neighbors
  10. [2018-05-27 19:25:45,660: INFO/MainProcess] mingle: sync with 2 nodes
  11. [2018-05-27 19:25:45,660: INFO/MainProcess] mingle: sync complete
  12. [2018-05-27 19:25:45,711: INFO/MainProcess] celery@worker.izwz920j4zsv1q15yhii1qz ready.
  13. [2018-05-27 19:25:45,868: INFO/MainProcess] Received task: tasks.add[f9c5ca2b-623e-4c0a-9c45-a99fb0b79ed5]
  14. [2018-05-27 19:25:45,880: INFO/ForkPoolWorker-1] Task tasks.add[f9c5ca2b-623e-4c0a-9c45-a99fb0b79ed5] succeeded in 0.0107084610499s: 3

Celery与定时任务

在celery中执行定时任务非常简单,只需要设置celery对象中的CELERYBEAT_SCHEDULE属性即可。
下面我们接着在celeryconfig.py中添加CELERYBEAT_SCHEDULE变量:

  1. '''
  2. 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006
  3. 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
  4. '''
  5. cat celeryconfig.py
  6. #!/usr/bin/env python
  7. #-*- coding:utf-8 -*-
  8. from kombu import Exchange,Queue
  9. BROKER_URL = "redis://47.106.106.220:5000/1"
  10. CELERY_RESULT_BACKEND = "redis://47.106.106.220:5000/2"
  11. CELERY_QUEUES = (
  12. Queue("default",Exchange("default"),routing_key="default"),
  13. Queue("for_task_A",Exchange("for_task_A"),routing_key="for_task_A"),
  14. Queue("for_task_B",Exchange("for_task_B"),routing_key="for_task_B")
  15. )
  16. CELERY_ROUTES = {
  17. 'tasks.taskA':{"queue":"for_task_A","routing_key":"for_task_A"},
  18. 'tasks.taskB':{"queue":"for_task_B","routing_key":"for_task_B"}
  19. }
  20. # 新增加的定时任务部分
  21. CELERY_TIMEZONE = 'UTC'
  22. CELERYBEAT_SCHEDULE = {
  23. 'taskA_schedule' : {
  24. 'task':'tasks.taskA',
  25. 'schedule':2,
  26. 'args':(5,6)
  27. },
  28. 'taskB_scheduler' : {
  29. 'task':"tasks.taskB",
  30. "schedule":10,
  31. "args":(10,20,30)
  32. },
  33. 'add_schedule': {
  34. "task":"tasks.add",
  35. "schedule":5,
  36. "args":(1,2)
  37. }
  38. }

还是按之前启动三个worker

  1. celery -A tasks worker -l info -n workerA.%h -Q for_task_A
  2. celery -A tasks worker -l info -n workerB.%h -Q for_task_B
  3. celery -A tasks worker -l info -n worker.%h -Q celery

启动定时任务

  1. [root@izwz920j4zsv1q15yhii1qz scripts]# celery -A tasks beat
  2. celery beat v4.1.1 (latentcall) is starting.
  3. __ - ... __ - _
  4. LocalTime -> 2018-05-27 19:39:29
  5. Configuration ->
  6. . broker -> redis://47.106.106.220:5000/1
  7. . loader -> celery.loaders.app.AppLoader
  8. . scheduler -> celery.beat.PersistentScheduler
  9. . db -> celerybeat-schedule
  10. . logfile -> [stderr]@%WARNING
  11. . maxinterval -> 5.00 minutes (300s)

在之前启动worker的三个窗口分别可以看到定时任务正在运行:

  1. celery -A tasks worker -l info -n workerA.%h -Q for_task_A
  2. [2018-05-27 19:41:27,432: INFO/ForkPoolWorker-1] Task tasks.taskA[60f41780-c9a2-477b-be46-6620ef07631f] succeeded in 0.00289130600868s: 11
  3. [2018-05-27 19:41:29,428: INFO/MainProcess] Received task: tasks.taskA[27220f52-dde2-471a-a87c-3f533d67217c]
  4. ......
  5. ......
  6. celery -A tasks worker -l info -n workerB.%h -Q for_task_B
  7. [2018-05-27 19:41:18,420: INFO/ForkPoolWorker-1] Task tasks.taskB[b6f9aee3-e6b4-4f10-9428-457d9bb844cf] succeeded in 0.00282042898471s: 60
  8. [2018-05-27 19:41:28,416: INFO/MainProcess] Received task: tasks.taskB[44dfea0b-b725-4874-bea2-9b66e8da573b]
  9. ......
  10. ......
  11. celery -A tasks worker -l info -n worker.%h -Q celery
  12. [2018-05-27 19:41:23,428: INFO/ForkPoolWorker-1] Task tasks.add[315a9cca-3c95-4517-9289-2ece15cd46a4] succeeded in 0.00355823297286s: 3
  13. [2018-05-27 19:41:28,423: INFO/MainProcess] Received task: tasks.add[c4a1b2c7-ecb7-4af4-85c1-a341b3ec6726]
  14. ......
  15. ......