蓝鲸bk-sops源码学习二:流程组件注册实现原理「建议收藏」

蓝鲸bk-sops源码学习二:流程组件注册实现原理「建议收藏」研究背景自己的项目都是python3.6开发。想使用蓝鲸的流程系统,真是千难万难。魔改路上真是一路坎坷。由于BK-SOPS需要结合蓝鲸的一整套服务才能够运行,所以单独把标准运维的流程系统抽出来然后融合进自己的系统。看看蓝鲸标准运维的功能多元接入支持:标准运维对接了蓝鲸通知、作业平台、配置平台等服务,作为官方标准插件库提供服务,还支持用户自定义接入企业内部系统,定制开发标准插件。可视化流…

大家好,又见面了,我是你们的朋友全栈君。

研究背景

自己的项目都是python3.6开发。想使用蓝鲸的流程系统,真是千难万难。魔改路上真是一路坎坷。由于BK-SOPS需要结合蓝鲸的一整套服务才能够运行,所以单独把标准运维的流程系统抽出来然后融合进自己的系统。

看看蓝鲸标准运维的功能

  • 多元接入支持:标准运维对接了蓝鲸通知、作业平台、配置平台等服务,作为官方标准插件库提供服务,还支持用户自定义接入企业内部系统,定制 开发标准插件。
  • 可视化流程编排:通过拖拽方式组合标准插件节点到一个流程模板。
  • 多种流程模式:支持标准插件节点的串行、并行,支持子流程,可以根据全局参数自动选择 分支执行,节点失败处理机制可配置。
  • 参数引擎:支持参数共享,支持参数替换。
    可交互的任务执行:任务执行中可以随时暂停、继续、撤销,节点失败后可以重试、跳过。
  • 通用权限管理:通过配置平台同步业务角色,支持流程模板的使用权限控制。

改造日志

2020年10月20日

有博友留言问能不能独立部署,是可以的。下面是我之前研究的代码。能运行并且自定义节点什么的,我没有改完。欢迎大家一起学习,分享出来我之前研究的源码

https://github.com/CyberWong/django_pipeline

2019年08月23日

这里用到比较多的就是元类。
元类就是在构建类时候运行的,蓝鲸的组件就是在构建类时候把组件写到一个公共类,然后构造流程时候通过get方法,返回组件类。

代码一

from pipeline.component_framework.component import Component
class TestComponent(Component):
    name = 'test'
    code = 'test'
    bound_service = TestService
    form = 'test.js'

这就是个标准的test组件。继承Component,看看Component的结构

from pipeline.component_framework.base import ComponentMeta
class Component(object,metaclass=ComponentMeta):

    def __init__(self, data_dict):
        self.data_dict = data_dict

    @classmethod
    def outputs_format(cls):
        outputs = cls.bound_service().outputs()
        outputs = map(lambda oi: oi._asdict(), outputs)
        return outputs

    def clean_execute_data(self, context):
        return self.data_dict

    def data_for_execution(self, context, pipeline_data):
        data_dict = self.clean_execute_data(context)
        inputs = { 
   }

        for key, tag_info in data_dict.items():
            if tag_info is None:
                raise ComponentDataLackException('Lack of inputs: %s' % key)

            inputs[key] = get_variable(key, tag_info, context, pipeline_data)

        return DataObject(inputs)

    def service(self):
        return self.bound_service()

Python2和Python3使用元类的语法不一样,sops用的是Python2,这里需要修改一下

# python2
class Component(object):
    __metaclass__ = ComponentMeta

在python3里面,Python3默认就是新式类,声明的语法如下:

# python3
class Component(object,metaclass=ComponentMeta):
# or
class Component(metaclass=ComponentMeta):

研究了好久为什么通过ComponentLibrary获取不到自己定义的组件。ComponentLibrary就是一个组件库,Component类初始化时候会到ComponentLibrary组件库类中,注册的方法就是通过元类,也就是上面的ComponentMeta

class ComponentMeta(type):
    def __new__(cls, name, bases, attrs):
        super_new = super(ComponentMeta, cls).__new__
	
        # Also ensure initialization is only performed for subclasses of Model
        # (excluding Model class itself).
    
        parents = [b for b in bases if isinstance(b, ComponentMeta)]
        # 为了确保只对子类初始化
        if not parents:
            return super_new(cls, name, bases, attrs)

        # Create the class
       	# 创建一个类,并动态引入
        module_name = attrs.pop('__module__')
        new_class = super_new(cls, name, bases, { 
   '__module__': module_name})
        module = importlib.import_module(new_class.__module__)

        # Add all attributes to the class
        attrs.setdefault('desc', '')
        for obj_name, obj in attrs.items():
            setattr(new_class, obj_name, obj)

        # check
        if not getattr(new_class, 'name', None):
            raise ValueError("component %s name can't be empty" %
                             new_class.__name__)

        if not getattr(new_class, 'code', None):
            raise ValueError("component %s code can't be empty" %
                             new_class.__name__)

        if not getattr(new_class, 'bound_service', None) or not issubclass(new_class.bound_service, Service):
            raise ValueError("component %s service can't be empty and must be subclass of Service" %
                             new_class.__name__)

        if not getattr(new_class, 'form', None):
            setattr(new_class, 'form', None)

        # category/group name
        # 在这里我们可以发现,可以自己自定义组名。使用__group_name__
        group_name = getattr(
            module, "__group_name__",
            new_class.__module__.split(".")[-1].title()
        )
        setattr(new_class, 'group_name', group_name)
        new_name = u"%s-%s" % (group_name, new_class.name)
		# 自定义icon
        # category/group name
        group_icon = getattr(
            module, "__group_icon__",
            ''
        )
        setattr(new_class, 'group_icon', group_icon)
		
        if not getattr(module, '__register_ignore__', False):
        	# 就是在这里
            ComponentLibrary.components[new_class.code] = new_class
            # try:
            # ComponentModel.objects.update_or_create(
            # code=new_class.code,
            # defaults={ 
   
            # 'name': new_name,
            # 'status': __debug__,
            # }
            # )
            # except Exception as e:
            # if not isinstance(e, ProgrammingError):
            # logging.exception(e)

        return new_class

下面再看看ComponentLibrary这个东西,很有趣的一种设计方法。

class ComponentLibrary(object):
    components = { 
   }

    def __new__(cls, *args, **kwargs):
    	# 
        component_code = kwargs.get('component_code', None)
        if args:
            component_code = args[0]
        if not component_code:
            raise ValueError('please pass a component_code in args or kwargs: '
                             'ComponentLibrary(\'code\') or ComponentLibrary(component_code=\'code\')')
        if component_code not in cls.components:
            raise ComponentNotExistException('component %s does not exist.' %
                                             component_code)
        return cls.components[component_code]

    @classmethod
    def get_component_class(cls, component_code):
        return cls.components.get(component_code)

    @classmethod
    def get_component(cls, component_code, data_dict):
        return cls.get_component_class(component_code)(data_dict)

受益匪浅。大牛的设计思想果然牛逼

蓝鲸bk-sops源码学习

一:流程图前端一
二:流程图前端二之节点创建
三:流程组件注册实现原理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/141413.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 空间分析 | 莫兰指数的计算

    空间分析 | 莫兰指数的计算什么是莫兰指数?根据百度百科的定义是“空间自相关系数的一种,其值分布在[-1,1],用于判别空间是否存在自相关。”简单的说就是判定一定范围内的空间实体相互之间是否存在相关关系,比如:一座座居民楼它们是聚集在一块还是离散分布在各处。莫兰指数数值分布在[-1,1],[0,1]说明各地理实体之间存在正相关的关系,[-1,0]之间说明存在负相关的关系,而0值则无相关关系。因为位置的确定是相…

    2022年6月25日
  • 开发手机游戏的一点心得(一)

    开发手机游戏的一点心得(一)作者:风过回廊文章来源:http://www.sf.org.cn2003年三月份,我刚开始接触了手机游戏的开发。开发手机上的游戏程序,最初仅仅只是出于兴趣爱好,利用业余时间自己陆陆续续的也写了一些Code,得到了一些经验,本来是想敝帚自珍的,但是朋友的鼓励,使我决定把自己的一点点心得体会写出来,藉以告慰我在学习中所阵亡的千千万万脑细胞,也为和我一样在黑暗的艰难摸索人们中提供一些微不足道的帮助吧

    2022年4月30日
  • HashMap扩容流程[通俗易懂]

    HashMap扩容流程[通俗易懂]文章目录为什么扩容?什么时候扩容?如何扩容?今天在和同时讨论HashMap的时候,提到了扩容和冲哈希的事情,然后我发现大家都是一种半懂不懂的状态。于是回去做了一番功课,写下这篇文章。HashMap的扩容,又被很多人叫rehash、重哈希,我本人是很反对这个叫法的,事实上HashMap扩容的时候,Node中存储的Key的hash值并没有发生变化,只是Node的位置发生了变化。首先说为什么需要扩…

    2022年9月15日
  • 知识图谱—知识推理综述(一)[通俗易懂]

    知识图谱—知识推理综述(一)[通俗易懂]知识图谱—知识推理综述(一)1知识推理的概念以及分类1.1知识推理的基本概念所谓的知识推理,就是在已有知识的基础之上,推断出未知的知识的过程。通过从已知的知识出发,通过已经获取的知识,从中获取到所蕴含的新的事实,或者从大量的已有的知识中进行归纳,从个体知识推广到一般性的知识。根据上面的概念的描述,我们可以知道,对于知识推理而言,其包括的内容可以分为两种,第一种是我们已经知道的,用于进行推理的已有知识,另外一种是我们运用现有的知识推导或者归纳出来的新的知识。对于知识而言,其形式是多种多样的,可以是

    2022年6月10日
  • 网络常用端口号大全图片_windows关闭不必要的端口

    网络常用端口号大全图片_windows关闭不必要的端口端口号—具有网络功能的应用软件的标识号。注意,端口号是不固定的,即可以由用户手工可以分配(当然,一般在软件编写时就已经定义)。当然,有很多应用软件有公认的默认的端口,比如FTP:20和21,HTTP:80,TELNET:23等等,这里就不一一列举了。一个软件可以拥有多个端口号,这证明这个软件拥有不止一个网络功能。0-1023是公认端口号,即已经公认定义或为将要公认定义的软件保留的,而102…

    2022年9月13日
  • python进阶(1)Lambda表达式「建议收藏」

    python进阶(1)Lambda表达式「建议收藏」Lambda表达式lambda表示的是匿名函数,不需要用def来声明,一句话就可以声明出一个函数语法函数名=lambda参数:返回值注意点1.函数的参数可以有多个,多个参数之间用逗号隔

    2022年7月30日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号