celery是什么
Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。
Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。
消息中间件
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
任务执行单元
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
任务结果存储
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
另外, Celery还支持不同的并发和序列化的手段
并发:Prefork, Eventlet, gevent, threads/single threaded
序列化:pickle, json, yaml, msgpack. zlib, bzip2 compression, Cryptographic message signing 等等
使用场景
celery是一个强大的 分布式任务队列的异步处理框架,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。
异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
定时任务:定时执行某件事情,比如每天数据统计
celery通过消息(任务)进行通信,
celery通常使用一个叫Broker(中间人/消息中间件/消息队列/任务队列)来协助clients(任务的发出者/客户端)和worker(任务的处理者/工作进程)进行通信的.
clients发出消息到任务队列中,broker将任务队列中的信息派发给worker来处理。
client ---> 消息 --> Broker(消息队列) -----> 消息 ---> worker(celery运行起来的工作进程)
消息队列(Message Queue),也叫消息队列中间件,简称消息中间件,它是一个独立运行的程序,表示在消息的传输过程中临时保存消息的容器。
所谓的消息,是指代在两台计算机或2个应用程序之间传送的数据。消息可以非常简单,例如文本字符串或者数字,也可以是更复杂的json数据或hash数据等。
所谓的队列,是一种先进先出、后进呼后出的数据结构,python中的list数据类型就可以很方便地用来实现队列结构。
目前开发中,使用较多的消息队列有RabbitMQ,Kafka,RocketMQ,MetaMQ,ZeroMQ,ActiveMQ等,当然,像redis、mysql、MongoDB,也可以充当消息中间件,但是相对而言,没有上面那么专业和性能稳定。
并发任务10k以下的,直接使用redis
并发任务10k以上,1000k以下的,直接使用RabbitMQ
并发任务1000k以上的,直接使用RocketMQ
安装
pip install -U Celery
或着:
sudo easy_install Celery
Celery不建议在windows系统下使用,Celery在4.0版本以后不再支持windows系统,所以如果要在windows下使用只能安装4.0以前的版本,而且即便是4.0之前的版本,在windows系统下也是不能单独使用的,需要安装gevent、geventlet或eventlet协程模块
基本使用
单文件夹下使用
创建celery_test文件夹,pro写生产端,tasks写消费端,result用来获取请求结果
import celery
import time
backend = "redis://:密码@ip:port/14"
broker = "redis://:密码@ip:port/15"
cel = celery.Celery('test' , backend=backend, broker=broker)
@cel.task()
def send_sms (name ):
print ("开始发送" )
time.sleep(3 )
return "发送成功%s" % name
在tasks.py的当前目录运行celery的worker
celery -A tasks worker -l info
5版本命令和之前略有不同
出现下面信息表示成功运行
创建生产端代码 Pro.py
运行后返回一个id值
from tasks import send_sms
ret = send_sms.delay("hahaha " )
print (ret)
from tasks import cel
from celery.result import AsyncResult
asyncresult = AsyncResult(id ="4b235142-1921-4eff-9e41-27886b273a8e" ,app=cel)
print (asyncresult.status)
ret = asyncresult.get()
asyncresult.forget()
print (ret)
asyncresult.get()
asyncresult.forget()
asyncresult.revoke(terminate=True )
asyncresult.revoke(terminate=False )
asyncresult.failed()
还可以查看任务状态
asyncresult.status会输出任务状态
比如 ok PENDING,RETRY STARTED
多文件夹下使用
文件结构如下, mycelery用来保存celery实例的配置
mycelery.py 注意此时需要用include关键字,指定异步的任务路径
import celery
backend='redis://:password@ip:port/14'
broker='redis://:password@ip:port/15'
cel=celery.Celery('test' ,backend=backend,broker=broker,include=[
'celery_tasks.task01' ,
'celery_tasks.task02'
])
cel.conf.timezone = 'Asia/Shanghai'
cel.conf.enable_utc = False
task01/02.py
写入任务函数,注意导包路径
from celery_tasks.mycelery import cel
import time
@cel.task()
def send_sms (name ):
print ("短信开始发送" )
time.sleep(3 )
return "短信发送成功%s" % name
启动worker,注意启动路径和参数里包含的相对路径celery_tasks.mycelery
建议后续都从根路径启动workder,并用相对路径指定配置文件的位置,
这里是在celery_test2的根路径下启动的worker
young_shi@MacBook -Air-2 celery_test2 % ls
celery_tasks pro.py result.py
young_shi@MacBook -Air-2 celery_test2 % celery -A celery_tasks.mycelery worker -l info
from celery_tasks.task01 import send_sms
result = send_sms.delay("yuan" )
print (result.id )
result2 = send_sms.delay("alex" )
print (result2.id )
celery执行定时任务
celey的delay方法可以异步执行,而定时任务要用到apply_async
方法
异步任务名.apply_async((arg,), {'kwarg': value}, countdown=60, expires=120)
简单结构下的定时任务 在pro文件给apply_async添加延时
from celery_task import send_email
from datetime import datetime
ctime = datetime.now()
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
from datetime import timedelta
time_delay = timedelta(seconds=10 )
task_time = utc_ctime + time_delay
result = send_email.apply_async(args=["egon" ], eta=task_time)
print (result.id )
多任务结构中
代码结构和上面一样.这里展示2中定时任务写法
生产模型延时执行
mycelery的代码不变,pro.py代码如下, delay方法改成apply_async方法,并加上延时的datetime对象
pro.py代码修改如下
from datetime import datetime
from celery_tasks.task01 import send_sms
ctime = datetime.now()
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
from datetime import timedelta
time_delay = timedelta(seconds=10 )
task_time = utc_ctime + time_delay
result = send_sms.apply_async(args=["egon" ], eta=task_time)
print (result.id )
配置文件中添加定时执行
这种模式和生产消费模型关系不大,在mycelery.py添加beat_schedule配置
from datetime import timedelta
import celery
from celery.schedules import crontab
backend='redis://:密码@ip:port/14'
broker='redis://:密码@ip:port/15'
cel=celery.Celery('test' ,backend=backend,broker=broker,include=[
'celery_tasks.task01' ,
'celery_tasks.task02'
])
cel.conf.timezone = 'Asia/Shanghai'
cel.conf.enable_utc = False
cel.conf.beat_schedule = {
'add-every-10-seconds' : {
'task' : 'celery_tasks.task01.send_sms' ,
'schedule' : timedelta(seconds=6 ),
'args' : ('张三' ,),
},
}
用celery -A celery_tasks.mycelery worker -l info
开启worker进行监听
用celery -A celery_tasks.mycelery worker -B -l info
这里-B参数会读取beat_schedule里的配置参数,定时执行.
这里命令用的5.0版本,和4.0版本有点不一样 4.0命令格式 celery beat -A proj
Celery Beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列
celery的常见方法和属性总结
from mycelery.sms.tasks import send_sms1,send_sms2,send_sms3,send_sms4
mobile = "13312345656"
code = "666666"
ret1 = send_sms1.delay()
ret2 = send_sms2.delay(mobile,code)
ret4 = send_sms4.apply_async(kwargs={"x" :10 ,"y" :20 },countdown=30 )
ret4.id
ret4.status
ret4.get()
if ret4.status == "SUCCESS" :
print (ret4.get())
项目中使用celery
celery作为一个单独项目运行
app01
mycelery/ 1. 新建一个package
├── config.py 2. 添加配置文件用`config_from_object` 加载
├── __init__.py
├── main.py 5. 这里生成celery实例对象,加载celery的配置和任务
└── sms/ 3. 对异步功能单独创建一个package , 但是必须要有tasks.py文件,文件名不能取别的
├── __init__.py
├── tasks.py 4. 文件名必须叫tasks,用来编写异步执行的函数
└── email/
├── __init__.py
├── tasks.py
config代码
broker_url = "redis://:密码@ip:port/14"
result_backend = 'redis://:密码@ip:port/15'
sms下的tasks
from mycelery.main import app
import time
@app.task
def send_sms (mobile ):
"""发送短信"""
print ("向手机号%s发送短信成功!" %mobile)
time.sleep(5 )
return "send_sms OK"
main代码
import os
from celery import Celery
app = Celery("sms" )
app.config_from_object("mycelery.config" )
app.autodiscover_tasks(["mycelery.sms" ,])
django视图代码
from mycelery.sms.tasks import send_sms
def celery_test (request ):
send_sms.delay("15867416745" )
return HttpResponse("555" )
Celery不建议在windows系统下使用,Celery在4.0版本以后不再支持windows系统,所以如果要在windows下使用只能安装4.0以前的版本,而且即便是4.0之前的版本,在windows系统下也是不能单独使用的,需要安装gevent、geventlet或eventlet协程模块
并且运行参数要加-P
celery -A celery_task worker -l info -P eventlet
celery调度其他模块
这里以调度django里项目里的应用举例
对django导包引入
在main.py主程序中对django进行导包引入,并设置django的配置文件进行django的初始化。
import os,django
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'luffycityapi.settings.dev' )
django.setup()
app = Celery("luffycity" )
app.config_from_object("mycelery.config" )
app.autodiscover_tasks(["mycelery.sms" ,"mycelery.email" ])
这里注意练习过程中发现的2个低级错误
celery.py最好改个名字,容易和模块celery名字冲突
其他文件导包的模块必须从luffycityapi/ # 服务端项目根目录
开始导入,否则celery作为第三方模块加载时候 无法从根目录找到响应的路径
luffycityapi/
└── luffycityapi/
├── apps/
├ └── Home/
├ └── users/
├ └── tasks.py
├── settings/
├── __init__.py
└── Mycelery.py
luffycityapi/Mycelery.py,主应用目录下创建cerley入口程序,创建celery对象并加载配置和异步任务,代码:
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'luffycityapi.settings.dev' )
app = Celery('luffycityapi' )
app.conf.task_default_queue = 'Celery'
app.config_from_object('django.conf:settings' , namespace='CELERY' )
app.autodiscover_tasks()
settings 这里将celery的配置信息集中写到django的配置文件中了, settings/dev.py,代码:
CELERY_BROKER_URL = 'redis://:123456@127.0.0.1:6379/14'
CELERY_RESULT_BACKEND = 'redis://:123456@127.0.0.1:6379/15'
CELERY_TIMEZONE = TIME_ZONE
CELERY_FORCE_EXECV = True
CELERYD_CONCURRENCY = 200
CELERY_ACKS_LATE = True
CELERYD_MAX_TASKS_PER_CHILD = 500
CELERYD_TIME_LIMIT = 10 * 60
CELERY_DISABLE_RATE_LIMITS = True
CELERY_ACCEPT_CONTENT = ['json' , 'pickle' ]
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
"user-add" : {
"task" : "add" ,
"schedule" : 10 ,
}
}
luffycityapi/__init__.py
,主应用下初始化,代码:
import pymysql
from .Mycelery import app as celery_app
pymysql.install_as_MySQLdb()
__all__ = ['celery_app' ]
users/tasks.py,代码:
from celery import shared_task
from ronglianyunapi import send_sms as sms
import logging
logger = logging.getLogger("django" )
@shared_task(name="send_sms" )
def send_sms (tid, mobile, datas ):
"""异步发送短信"""
try :
return sms(tid, mobile, datas)
except Exception as e:
logger.error(f"手机号:{mobile} ,发送短信失败错误: {e} " )
@shared_task(name="send_sms1" )
def send_sms1 ():
print ("send_sms1执行了!!!" )
django中的用户发送短信,就可以改成异步发送短信了。
users/views,视图中调用异步发送短信的任务,代码:
from .tasks import send_sms
send_sms.delay(settings.RONGLIANYUN.get("reg_tid" ),mobile, datas=(code, time // 60 ))
import random
from django_redis import get_redis_connection
from django.conf import settings
from .tasks import send_sms
"""
/users/sms/(?P<mobile>1[3-9]\d{9})
"""
class SMSAPIView (APIView ):
"""
SMS短信接口视图
"""
def get (self, request, mobile ):
"""发送短信验证码"""
redis = get_redis_connection("sms_code" )
interval = redis.ttl(f"interval_{mobile} " )
if interval != -2 :
return Response({"errmsg" : f"短信发送过于频繁,请{interval} 秒后再次点击获取!" , "interval" : interval},status=status.HTTP_400_BAD_REQUEST)
code = f"{random.randint(0 , 999999 ):06d} "
time = settings.RONGLIANYUN.get("sms_expire" )
sms_interval = settings.RONGLIANYUN["sms_interval" ]
send_sms.delay(settings.RONGLIANYUN.get("reg_tid" ), mobile, datas=(code, time // 60 ))
pipe = redis.pipeline()
pipe.multi()
pipe.setex(f"sms_{mobile} " , time, code)
pipe.setex(f"interval_{mobile} " , sms_interval, "_" )
pipe.execute()
return Response({"errmsg" : "OK" }, status=status.HTTP_200_OK)
终端下先启动celery,在django项目根目录下启动。
cd ~/Desktop/luffycity/luffycityapi
celery -A luffycityapi worker -l INFO
celery multi start worker -A luffycityapi -E --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker1.pid" --logfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/celery.log" -l info -n worker1
celery multi stop worker -A luffycityapi --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker1.pid"
还是可以在django终端下调用celery的
$ python manage.py shell
>>> from users.tasks import send_sms1
>>> res = send_sms1.delay()
>>> res = send_sms1.apply_async(countdown=15 )
>>> res.id
'893c31ab-e32f-44ee-a321-8b07e9483063'
>>> res.state
'SUCCESS'
>>> res.result
celery work的参数
Usage: celery worker [OPTIONS]
Start worker instance.
Examples
--------
$ celery --app=proj worker -l INFO
$ celery -A proj worker -l INFO -Q hipri,lopri
$ celery -A proj worker --concurrency=4
$ celery -A proj worker --concurrency=1000 -P eventlet
$ celery worker --autoscale=10 ,0
Worker Options:
-n, --hostname HOSTNAME 用于在多个 worker 进程中区分。如果不指定该参数,则会自动生成一个唯一的名称。
-D, --detach Start worker as a background process.
-S, --statedb PATH Path to the state database. The extension
'.db' may be appended to the filename.
-l, --loglevel [DEBUG|INFO|WARNING|ERROR|CRITICAL|FATAL] 日志级别
Logging level.
-O, --optimization [default|fair]
Apply optimization profile.
--prefetch-multiplier <prefetch multiplier>
Set custom prefetch multiplier value for
this worker instance.
Pool Options:
-c, --concurrency <concurrency> 并发数,即同时处理的任务数。默认为服务器 CPU 核数的 2 倍。
-P, --pool [prefork|eventlet|gevent|solo|processes|threads|custom]
数用于指定 celery worker 进程池的类型,即使用哪种方式来管理 worker 进程。该参数接受一个字符串值,可选值包括:
prefork:使用 prefork 进程池,即每个 worker 进程在启动时就会预先创建好,并在需要时直接使用。这种方式可以提高启动速度,但会占用较多的内存。
eventlet:使用 eventlet 协程池,即将每个 worker 进程变成一个协程,通过事件循环机制来实现并发处理。这种方式可以大幅降低内存占用,但可能会影响性能。
gevent:使用 gevent 协程池,与 eventlet 类似,但使用 gevent 库来实现协程,性能更高。
solo:不使用进程池,每个任务都在主进程中运行。这种方式适用于只有少量任务的场景,但不适合高并发场景。
-E, --task-events, --events Send task-related events that can be
captured by monitors like celery events,
celerymon, and others.
--time-limit FLOAT 单个任务的最大执行时间,超过该时间则会被 worker 强制终止。
--soft-time-limit FLOAT 单个任务的软时间限制,当任务超过该时间时,worker 会发出警告,但仍会让任务继续执行
--max -tasks-per-child INTEGER 每个 worker 进程最多处理多少个任务后重启,防止因为内存泄漏等问题导致的进程异常。
--max -memory-per-child INTEGER 参数用于限制每个 worker 进程最大占用的内存大小,一旦超过该限制,worker 进程会被强制重启,以避免内存泄漏等问题导致的进程崩溃。
该参数接受一个整数值,表示每个 worker 进程可以占用的最大内存大小,单位为 MB。
Queue Options:
--purge, --discard
-Q, --queues COMMA SEPARATED LIST
-X, --exclude-queues COMMA SEPARATED LIST
-I, --include COMMA SEPARATED LIST
Features:
--without-gossip
--without-mingle
--without-heartbeat
--heartbeat-interval INTEGER
--autoscale <MIN WORKERS>, <MAX WORKERS>
Embedded Beat Options:
-B, --beat
-s, --schedule-filename, --schedule TEXT
--scheduler TEXT
Daemonization Options:
-f, --logfile TEXT Log destination; defaults to stderr
--pidfile TEXT
--uid TEXT
--gid TEXT
--umask TEXT
--executable TEXT
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者新选择:用DeepSeek实现Cursor级智能编程的免费方案
· Tinyfox 发生重大改版
· 独立开发经验谈:如何通过 Docker 让潜在客户快速体验你的系统
· 小米CR6606,CR6608,CR6609 启用SSH和刷入OpenWRT 23.05.5
· 近期最值得关注的AI技术报告与Agent综述!