pipeline
pipeline 是标准运维 v3 内部使用的任务调度引擎,其主要职责是解析,执行,管理由用户创建的流程任务,并提供了如暂停,撤销,跳过和重试等灵活的控制能力和并行、子流程等进阶特性,并可通过水平扩展来进一步提升任务的并发处理能力。
插件注册
一切都需要从git上源码文档讲起.
下面是官方的例子
1 | # coding=utf-8 |
结果执行以后,报错.
1 | Traceback (most recent call last): |
好吧,出师未捷身先死,正好看看为什么会出现这样的错误。
定位报错
我们可以看到,报错是这一行出现的
1 | File "/data/bkops/pipeline/component_framework/library.py", line 39, in get_component |
ComponentLibrary这个类
1 | class ComponentLibrary(object): |
通过类方法,来调用类变量,如果没有找到,就返回错误.
那么也就是说,我们写的插件,并没有注册到这个变量里面,那么他是如何注册的呢?
我尝试直接导入这个类,发现导入以后变量已经被注册了
1 | from pipeline.component_framework.library import ComponentLibrary |
而Django会在setup()的时候去做一些注册初始化的工作.通过重写AppConfig的ready方法可以实现Django加载后的一些操作
pipeline/component_framework/apps.py
1 | # -*- coding: utf-8 -*- |
其中,COMPONENT_AUTO_DISCOVER_PATH的类型为列表,默认配置的值为:
1 | COMPONENT_AUTO_DISCOVER_PATH = [ |
那么这个路径就是模块自动发现的默认路径了,后面交付给了autodiscover_collections(path) 让我们看一下这个函数
1 | import pkgutil |
autodiscover_collections函数,通过django.apps,apps.get_app_configs()方法,返回了一个列表,列表中是每个APP的配置对象.通过
app_config.name拿到APP路径,并且拼接上默认查找的目录如:components.collections最终变成如:import_module('django.contrib.auth.components.collections')如果导入报错,则忽略异常
如果导入包成功,则调用autodiscover_items函数
1 | import pkgutil |
此时,django在初始化APP过程后,就走了一遍上述的流程. ComponentLibrary类中components的属性就已经被注册了.
那么问题又来了,他是怎么做到在导入包的时候就实例化对象并且赋值给这个字典呢?
我们随便看一个已经写好的组件
1 | # -*- coding: utf-8 -*- |
其中, 所有的Component都会继承Component这个父类,那我们看一下这个父类的写法
1 | # -*- coding: utf-8 -*- |
实现方法
上面的类,可以看到定义了一个metaclass
1 | # -*- coding: utf-8 -*- |
我们知道,object是所有类的父类,而所有的类都是type的实例
因此,定义metaclass,重写type的new方法,可以完成在类创建的过程中一些操作
- new方法需要三个参数
- 类的名称, 继承关系, (类变量,方法)
1 | def __new__(cls, name, bases, attrs): |
因为考虑到多继承的关系, type会被 __new__ 多次,所以需要区分不会去初始化父类,保证仅初始化子类。
bases 是一个元组, 包含继承关系
1 | # 如果bases为空数组,或者bases继承的所有类都不是ComponentMeta的实例,则直接返回 |
下面开始创建这个类
1 |
|
说实话,我不是很明白为什么手动创建类,又把原来的值重新赋值给它.
直接这样不好吗~
1 | new_class = super_new(cls, name, bases, attrs) |
下面就是一些检查属性的操作,目前还不清楚用途,所以先不研究.
赋值的代码在这一段
1 | if not getattr(module, '__register_ignore__', False): |
这里可以明显的看到赋值了,那我们回到之前的报错,为什么会没有赋值?
要想赋值,则必须模块中 __register_ignore__ 属性等于 False
那我们看看源码中的属性值是什么?
1 | __register_ignore__ = not settings.ENABLE_EXAMPLE_COMPONENTS |
在看一下config的配置
1 | ENABLE_EXAMPLE_COMPONENTS = False |
默认配置是False
我们改回来!!
到此,component自动发现的过程就讲完了.
简单的流程创建
Element基类
标准运维中的流程事件,都会继承这个基类
1 | # -*- coding: utf-8 -*- |
返回唯一UUID的函数
1 | import uuid |
PE对象
这个对象设置了一些静态数据
1 | class PipelineElement(object): |
grow 函数
1 | def __grow(tree, elem): |