我想实现一个调度程序,用于在Python中对硬件资源执行的操作。硬件资源封装在自己的类中,该对象包含状态信息,因此应该只有该对象的一个实例。我试图用Celery做到这一点。我的任务基类是:
from celery import Task
class ObClTask(Task):
def __init__(self):
self.val = 0
def add(self, add_val):
self.val += add_val
return self.val
def mult(self, mult_val):
self.val *= mult_val
return self.val
Celery任务定义如下:
from celery import Celery
from obcl import ObClTask
@app.task(base=ObClTask)
def add(x):
return add.add(x)
@app.task(base=ObClTask)
def mult(x):
return mult.mult(x)
我使用celery
启动--concurrency=1
以确保只生成一个工作人员。
然后,运行像
这样的序列add.delay(5)
add.delay(5)
mult.delay(2)
mult.delay(2)
当我感兴趣的是5
,10
,{时,会返回0
,0
,5
,10
{1}},20
- 即40
和add
在同一个实例上运行。我了解mult
和add
会生成mult
的不同子类。但有没有办法通过Celery实现我的需求?
编辑:这可能适用于类属性。但这是反模式吗?
答案 0 :(得分:0)
@app.task(base=whatever)
会创建一个新任务类,因此最终会有两个不同的任务实例。
我强烈建议您不要使用任务实例来保持状态 - 这对于只读值或本地缓存(cf the db connection cache in the fine manual)都是可以的,但不适用于此用例。如果要在任务执行之间维护共享状态,请将状态保存在某个数据库中,在任务函数开始时读取它并在完成后更新它(并且不要忘记设置一些锁定,因此只有一个任务可以一次访问此资源。)
答案 1 :(得分:0)
我在寻找类似问题的解决方案时发现了您的问题。这就是我最终要做的。我将RabbitMQ用作经纪人,将Redis用作结果后端,但请根据您的情况进行调整:
文件 obcl / obcl.py :
class ObCl(object):
def __init__(self):
self.val = 0
def add(self, add_val):
self.val += add_val
return self.val
def mult(self, mult_val):
self.val *= mult_val
return self.val
文件 obcl / obcl_tasks.py :
from obcl.obcl import ObCl
from celery import Celery
from celery.signals import worker_shutting_down
app = Celery('obcl_tasks', broker='pyamqp://guest@localhost//', backend='redis://localhost/')
my_obcl = ObCl()
@app.task
def add(x):
return my_obcl.add(x)
@app.task
def mult(x):
return my_obcl.mult(x)
@worker_shutting_down.connect
def task_sent_handler(sig=None, how=None, exitcode=None, **kwargs):
# Maybe close hardware resources when shutting down the worker?
# I had to in my case, you might too.
pass
从obcl模块的父文件夹运行celery。确保使用--concurrency=1
,以便您只有一个实例工作进程来控制您的硬件。 :
celery -A obcl.obcl_tasks worker --pool=solo --concurrency=1 --loglevel=info
-------------- celery@localhost v4.2.2 (windowlicker)
---- **** -----
--- * *** * -- Linux-4.18.0-16-generic-x86_64-with-Ubuntu-18.04-bionic 2019-03-24 17:10:03
-- * - **** ---
- ** ---------- [config]
- ** ---------- .> app: obcl_tasks:0x7f443c0bc5c0
- ** ---------- .> transport: amqp://guest:**@localhost:5672//
- ** ---------- .> results: redis://localhost/
- *** --- * --- .> concurrency: 1 (solo)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. obcl.obcl_tasks.add
. obcl.obcl_tasks.mult
[2019-03-24 17:10:03,041: INFO/MainProcess] Connected to amqp://guest:**@127.0.0.1:5672//
[2019-03-24 17:10:03,049: INFO/MainProcess] mingle: searching for neighbors
[2019-03-24 17:10:04,070: INFO/MainProcess] mingle: all alone
[2019-03-24 17:10:04,086: INFO/MainProcess] celery@localhost ready.
现在您可以调用您的任务并且应该获得预期的结果。
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from obcl.obcl_tasks import add, mult
>>> test = add.delay(5)
>>> test.result
5
>>> test = add.delay(5)
>>> test.result
10
>>> test = mult.delay(2)
>>> test.result
20
>>> test = mult.delay(2)
>>> test.result
40