事件处理组件编排的YAML配置解析

引子

“事件处理业务的简易组件编排框架” 中,讲到了事件处理流程的组件编排,是通过枚举来实现事件处理流程的配置的。实际应用中,可以采用 YAML 文件来配置。

YAML 是一种主流的配置文件语法。相比 XML,形式上更为简洁;相比 JSON, 去掉了引号之繁琐;相比扁平的 properties,能够表达更复杂的结构。本文来学习 YAML 语法和解析。


基础知识

  • 大小写敏感
  • YAML 通过缩进来标识数据或属性之间的层级;
  • 缩进时不允许使用Tab键,只允许使用空格;
  • 同一缩进的属性,是上一层的同一对象的平级属性;
  • 数组元素,以“缩进 - ” 表示;
  • # 表示注释,从这个字符一直到行尾,都会被解析器忽略。

YAML 支持的数据结构有三种:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
  • 纯量(scalars):单个的、不可再分的值

可参考: “YAML语法介绍”


基本结构

如下所示,是一个典型的 YML 配置(eventflow_finished.yml):


bizTypes:
  - 'docker_bounce_shell'
eventType: 'create'
eventSourceType: 'bizEvent'
eventflowClassName: 'cc.lovesq.eventflow.demo.DefaultEventFlow'
model: 'eventflow'
way: 'serial'
originParamType: 'cc.lovesq.eventflow.demo.DefaultEventData'
componentParamType: 'cc.lovesq.eventflow.demo.DefaultFlowContext'
components:
  - 'notificationSender_common'
  - 'defaultBigDataSender'
  - 'cc.lovesq.eventflow.demo.components.DefaultBigDataSender'

要解析这个配置,需要建立相应的对象模型,就像将 JSON 字符串转换成 Java 对象一样。这个对象 EventFlowExecutionModel 也不难建立:


public class EventFlowExecutionModel extends ComponentsExecutionModel {

    /**
     * 事件流程处理的业务类型,比如反弹shell, 本地提权等
     * 由于一个通用事件处理流程可以处理多个业务类型,因此这里可以是列表
     */
    private List<String> bizTypes;

    /** 事件类型,比如创建,更新等 */
    private String eventType;

    /** 事件流程处理的事件源类型,比如 agent上报事件, bizEvent事件 */
    private String eventSourceType;

    /** 根据类名来选择事件处理流程实例 */
    private String eventflowClassName;

    /** eventflow 事件流程处理(不指定默认)  components 有序组件集执行 */
    private ExecutionModel model = ExecutionModel.eventflow;

    /** 原始传入事件处理流程的入口参数类型 */
    private String originParamType;

    /** serial 串行(不指定默认)  parallel 并发 */
    protected ExecWay way = ExecWay.serial;

    /** 组件参数类型 */
    protected String componentParamType;

    protected List<String> components;

    // getter/ setter
}


使用 snakeyaml 解析:


public class ConfigUtil {

    /**
     * 通过 yml 文件加载组件执行模型对象
     */
    public static EventFlowExecutionModel load(String ymlFilePath) {
        return load(ymlFilePath, EventFlowExecutionModel.class);
    }

    /**
     * 通过 yml 文件加载对象模型
     */
    public static <T> T load(String ymlFilePath, Class<T> cls) {
        Yaml yaml = new Yaml();
        T model = yaml.loadAs(cls.getClassLoader().getResourceAsStream(ymlFilePath), cls);
        return model;
    }

}

需要引入依赖:


<dependency>
     <groupId>org.yaml</groupId>
     <artifactId>snakeyaml</artifactId>
     <version>1.27</version>
</dependency>


编写单测:


public class ConfigUtilTest extends CommonForAssert {

    @Test
    public void testLoad() {
        EventFlowExecutionModel eventFlowExecModel = ConfigUtil.load("eventflow_finished.yml");
        eq(Arrays.asList("docker_bounce_shell"), eventFlowExecModel.getBizTypes());
        eq("cc.lovesq.eventflow.demo.DefaultEventFlow", eventFlowExecModel.getEventflowClassName());
        eq(ExecutionModel.eventflow, eventFlowExecModel.getModel());
        eq(ExecWay.serial, eventFlowExecModel.getWay());
        eq("cc.lovesq.eventflow.demo.DefaultEventData", eventFlowExecModel.getOriginParamType());
        eq("cc.lovesq.eventflow.demo.DefaultFlowContext", eventFlowExecModel.getComponentParamType());
        eq(Arrays.asList("notificationSender_common", "defaultBigDataSender", "cc.lovesq.eventflow.demo.components.DefaultBigDataSender"), eventFlowExecModel.getComponents());
    }
}

支持多个事件处理流程

实际应用中,显然需要在一个文件里支持多个事件处理流程。 如下所示(eventflow.yml):


version: 1.0
flows:

  - bizTypes:
    - 'docker_bounce_shell'
    - 'abnormal_login'
    eventType: 'create'
    eventSourceType: 'bizEvent'
    eventflowClassName: 'cc.lovesq.eventflow.demo.DefaultEventFlow'
    model: 'eventflow'
    way: 'serial'
    originParamType: 'cc.lovesq.eventflow.demo.DefaultEventData'
    componentParamType: 'cc.lovesq.eventflow.demo.DefaultFlowContext'
    components:
    - 'notificationSender_common'
    - 'defaultBigDataSender'
    - 'cc.lovesq.eventflow.demo.components.DefaultBigDataSender'

  - bizTypes:
    - 'local_rights'
    - 'docker_local_rights'
    eventType: 'create'
    eventSourceType: 'bizEvent'
    eventflowClassName: 'cc.lovesq.eventflow.demo.DefaultEventFlow'
    model: 'eventflow'
    way: 'serial'
    originParamType: 'cc.lovesq.eventflow.demo.DefaultEventData'
    componentParamType: 'cc.lovesq.eventflow.demo.DefaultFlowContext'
    components:
    - 'notificationSender_common'
    - 'defaultBigDataSender'

相应的,也要建立对应的 Java 对象模型:


public class EventFlowsModel {

    private String version;

    private List<EventFlowExecutionModel> flows;

    // getter / setter
}


锚点与引用

在配置文件 eventflow.yml 中,除了 bizTypes 和 components ,中间的基本都是重复配置。yaml 支持通过锚点 &config 和引用 <<: *config 来引用重复配置。如下所示(eventflow_brief.yml):


version: 1.0

flows:

  - bizTypes:
    - 'docker_bounce_shell'
    - 'abnormal_login'
    commonConfig: &commonConfig
      eventType: 'create'
      eventSourceType: 'bizEvent'
      eventflowClassName: 'cc.lovesq.eventflow.demo.DefaultEventFlow'
      model: 'eventflow'
      way: 'serial'
      originParamType: 'cc.lovesq.eventflow.demo.DefaultEventData'
      componentParamType: 'cc.lovesq.eventflow.demo.DefaultFlowContext'
    components:
    - 'notificationSender_common'
    - 'defaultBigDataSender'
    - 'cc.lovesq.eventflow.demo.components.DefaultBigDataSender'

  - bizTypes:
    - 'local_rights'
    - 'docker_local_rights'
    commonConfig:
      <<: *commonConfig
    components:
    - 'notificationSender_common'
    - 'defaultBigDataSender'

对应的 Java 对象为:


public class CommonConfig {

    /** 事件类型,比如创建,更新等 */
    private String eventType;

    /** 事件流程处理的事件源类型,比如 agent上报事件, bizEvent事件 */
    private String eventSourceType;

    /** 根据类名来选择事件处理流程实例 */
    private String eventflowClassName;

    /** eventflow 事件流程处理(不指定默认)  components 有序组件集执行 */
    private ExecutionModel model = ExecutionModel.eventflow;

    /** 原始传入事件处理流程的入口参数类型 */
    private String originParamType;

    /** serial 串行(不指定默认)  parallel 并发 */
    protected ExecWay way = ExecWay.serial;

    /** 组件参数类型 */
    protected String componentParamType;

    // getter / setter
}

public class EventFlowExecutionModelRefCommon {

    private List<String> bizTypes;

    private CommonConfig commonConfig;

    private List<String> components;

    // getter / setter
}

public class EventFlowsModelRefCommon {

    private String version;

    private List<EventFlowExecutionModelRefCommon> flows;

    // getter / setter
}

这里的诀窍就是:建立与 yml 文件里对应的模型,就像 JSON 对象解析类似。


posted @ 2021-04-20 21:52  琴水玉  阅读(726)  评论(0编辑  收藏  举报