事件处理组件编排的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 对象解析类似。