【妙用协程】 - 可持久化的协程能被用来实现工作流

常规版本的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不同版本的流程状态,可以在各个阶段里来回切换。

    原文作者:taowen
    原文地址: https://segmentfault.com/a/1190000000661437
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞