简易版celery的实现

本文档介绍了作者实现的一个简易Celery版本,基于Redis作为broker。内容包括项目目录结构、Celery类、任务类的详细说明,以及如何运行该项目。重点讲解了Celery类中的task装饰器和启动方法,任务类的任务发布函数,以及如何在客户端和服务端运行项目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简易版celery的实现

最近学习了下,celery源码,看了一点点皮毛后,自己动手写了个简易的celery,通过redis作为broker,没有复杂的路由匹配规则,队列和任务之间一个直接匹配的简易规则。这里对项目简单的记录下。

项目目录结构

在这里插入图片描述
接下来挨个介绍下这几个文件:

  1. simple 是celery类所在位置,具体实现了celery的启动,加载配置文件,任务装饰器;
  2. utils 下base是任务类的实现,任务的发布方法,任务绑定celery实例方法;
  3. redisbase 是连接redis数据的类,主要是往队列里插入数据;
  4. example 是我自己写的用例

Celery类

# -*- coding: utf-8 -*-

'''
simplecelery @2019 03 26
author xuxiaolong

redis 作为broker
redis 作为结果返回result_backend
'''
from time import sleep
from utils.base import BaseTask
import json
import multiprocessing

from importlib import import_module
from utils.redisbase import RedisHelper
class Celery(object):
    def __init__(self, name):
        self.name = name
        self.queuedic = dict()
        self._task = dict()

    def start(self):
        '''
        启动方法
        从redis list 中获取message ,并找到对应的任务实例去执行,通过调用task.runtask()方法执行
        '''
        #_redis = RedisHelper(self.host,self.port,self.db,self.password)
        #为每一个队列开启一个进程,执行对应的任务
        queue = set([v["queue"] for v in self.taskdic.values()])
       # pool = multiprocessing.Pool(processes=len(queue))
        def runloop(queue):
            _redis = RedisHelper(host=self.host, port=self.port, db=self.db, password=self.password)
            while True:
                retjson = _redis.Lpop(queue)
                print retjson
                if retjson is None:
                    sleep(5) #加了个休眠,避免terminal 刷的太快
                    continue
                message = json.loads(retjson)
                #print message
                #print self.taskdic
                for fun in self.queuedic[queue]:
                    #print message["name"] == fun.split(".")[-1:][0]
                    if message["name"] == fun.split(".")[-1:][0]:
                        print self._task[message["name"].encode('utf-8')](*message["args"],**message["kwargs"])
        for q in queue:
            runloop(q)
            #pool.apply_async(runloop(q))
        #pool.close()
        #pool.join()
        
    def config_from_object(self, include=None):
        '''获取基本的配置信息'''
        self.config = import_module(include)
        # redis_url 配置redis地址信息
        redis,hostpass, portdb = self.config.redis_url.split(':')
        self.password, self.host = hostpass.split('@')
        self.port, self.db = portdb.split('/')
        # celery_route配置路由信息
        self.taskdic = self.config.celery_route
        for k,v in self.taskdic.items():
            if v["queue"] not in self.queuedic.keys():
                self.queuedic[v["queue"]] = [k]
            else:
                self.queuedic[v["queue"]].append(k)


    #任务装饰器
    def task(self,*args,**kwargs):
        #print args,kwargs
        def create_inner_task():
            def create_tasks(func):
                ret = self.create_task_fromfun(func)
                return ret
            return create_tasks
        return create_inner_task()

    #通过函数创建任务对象
    def create_task_fromfun(self,func):
        tasks = type(func.__name__, (BaseTask,), dict({'name':func.__name__,'run': staticmethod(func)}))()
        #print isinstance(tasks,BaseTask)
        if tasks.name not in self._task:
            self._task[tasks.name] = tasks.run
        tasks.bind(self)
        return tasks

这里task装饰器的,装饰的是任务函数,原理是,将任务函数,转换成一个任务对象的实例,也就是BaseTask,并把方法体,赋给BaseTask的run方法,利用的是type元类的来实现的,
tasks = type(func.name, (BaseTask,), dict({‘name’:func.name,‘run’: staticmethod(func)}))()
这里有个小地方需要注意下,任务函数需要转成非绑定的方法,也就是通过staticmethod方法,装成静态方法;
start 方法,可以理解成监听函数,是一个无限循环,不断的从配置的队列中读取数据,然后,交给tasks去执行,也就是调用我们上面说的,run方法

任务类

# -*- coding: utf-8 -*-
import json
from redisbase import RedisHelper
class BaseTask(object):
    '''
    任务类的基类,所有任务的拓展都继承此类
    '''


    def runtask(self,*args,**kwargs):
        '''
        任务执行的方法
        '''
        _redis = RedisHelper(host=self._app.host, port=self._app.port, db=self._app.db, password=self._app.password)

        messagedic = dict()
        messagedic["name"] = self.name
        messagedic["args"] = args
        messagedic["kwargs"] = kwargs
        print self.run(*args,**kwargs)

        for k,v in self._app.taskdic.items():
            if k.split(".")[-1:][0] == self.name:
               print _redis.Lpush(v["queue"],json.dumps(messagedic))

    def bind(self,app):
        '''
        绑定app实例到task
        '''
        self._app = app

runtask 是我们的任务的发布函数,通过在客户端调用,往指定队列插入数据,服务端读取执行
redisbase 就不介绍了,没啥可说的,就是操作数据库的

具体怎么让项目跑起来

exampe 是示例 :
celery.py文件代码:

from simple import Celery

app = Celery("test")
# 加载配置
app.config_from_object('example.config')

@app.task()
def add(x,y):
    #print(x+y)
    return x+y


if __name__ == '__main__':
    app.start()

这里实例化我们的celery,并且实现具体的任务函数,记得一定要加上task装饰器,和celery一样 的,
celery.py 是在服务端执行的,它会去具体执行任务;
config.py
是配置文件,安装这个规则写就行,示例如下:
redis_url=“redis//?****:8004/0”
celery_route={‘example.celery.add’:{‘queue’:‘addqueue’}}
run.py:

from example.celery import add
if __name__ == '__main__':
    print(add.runtask(1,2))

这是在客户端运行的,具体的任务发布,通过runtask来发布

完整的代码可以去github上下载: https://github.com/shabbyboy/simplecelery.git

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值