常规版本的Python的generator是不可以被持久化保存的。但是stackless和pypy这两个修改版本的Python解释器可以。下面这段代码演示了如何把一个执行中的函数持久化保存,然后过段时间再把函数从上次执行到的地方原样拉起。从效果上来说,有点类似于Vmware虚拟机的snapshot的功能:
import cPickle as pickle
def generator_can_be_used_as_workflow():
yield 'do something'
is_approved = yield 'ask boss for permission'
if is_approved:
yield 'do another after approved'
else:
yield 'do another after rejected'
workflow = generator_as_workflow_engine()
print(workflow.next())
print(workflow.next())
# boss is not available now
persisted_workflow = pickle.dumps(workflow)
print('persisted workflow is like this: [%s ...]' % persisted_workflow.replace('\n', ' ')[:150])
# several hours later, boss come back
workflow = pickle.loads(persisted_workflow)
print(workflow.send(True)) # I approve
这段代码的输出是
do something
ask boss for permission
persisted workflow is like this: [cstackless._wrap generator p1 (tRp2 (cstackless._wrap frame p3 (cstackless._wrap code p4 (I0 I1 I1 I99 S'd\x01\x00V\x01d\x02\x00V}\x00\x00|\x00\x00r\x ...]
do another after approved
利用这个原理,我们可以把一个需要运行很长时间的流程用协程的方式来实现。在流程被阻塞的时候(比如需要审批老板不在)把协程持久化成string入库,等流程不再阻塞的时候把协程重新从数据库里拉起来继续执行。优点自然是轻量简单随意强大,缺点也是随意强大导致流程状态不可被外部直接解读和操作,也无法实现运行中的流程实例的代码升级。所以,这种工作流用在电子政务,或者办公自动化等强人机交互的领域(需要极高的灵活性)是不合适的。在运维的发布变更这样的场合下,主要是调度系统与系统,机器与机器这样比较固定且相对短暂的流程还是比较方便的。
ps:webwork使用的RIFE,是我所知最早的使用协程的流程引擎
下面是一段演示用pypy的greenlet,控制流程前进和回退的例子:
import greenlet
if '__main__' == __name__:
root = greenlet.getcurrent()
def g():
print('enter g')
mystack = 'special-g-stack'
greenlet.getcurrent().parent.switch()
print('leave g')
def f():
greenlet_g = greenlet.greenlet(g)
greenlet_g.switch()
root.switch()
print('a')
greenlet_g.switch()
root.switch()
print('b')
root.switch()
print('c')
f_greenlet = greenlet.greenlet(f)
f_greenlet.switch()
f_greenlet_v1 = pickle.dumps(f_greenlet)
print('is greenlet g also pickled? %s' % ('special-g-stack' in f_greenlet_v1))
f_greenlet = pickle.loads(f_greenlet_v1)
f_greenlet.switch()
f_greenlet_v2 = pickle.dumps(f_greenlet)
f_greenlet.switch()
f_greenlet_v3 = pickle.dumps(f_greenlet)
pickle.loads(f_greenlet_v1).switch()
pickle.loads(f_greenlet_v3).switch()
输出是:
enter g
is greenlet g also pickled? True
a
leave g
b
a
leave g
c
通过loads不同版本的流程状态,可以在各个阶段里来回切换。