近期笔者在投入变更风险防控开放平台的额外功能开发,目的是希望设计一套更加灵活的变更风险观测策略匹配机制,能够在满足面向任意变更场景应用观测策略的同时,尽可能保证产品体验,让用户清晰地了解到自己配置的什么策略能够在什么情况的变更过程中生效。在先前的文章当中,有粗浅提到变更观测策略匹配的事情,但没有深入探究,并且笔者以前接手的项目,很多底层设计的技术债也难以偿还,要在以前实现的基础上再做重构也不是很现实了。
所以,趁着扩展新系统的机会,笔者重新思考了下变更风险观测策略匹配机制的事情,跟随这篇文章来抛砖引玉下自己的想法。
首先,还是一个根本问题,一条变更风险观测策略的粒度是怎样的?如果粒度太粗,那么从产品视角,很容易导致「所见非所得」的问题;如果粒度太细,那么用户配置起来也会相当麻烦。经评估,由单个检测能力执行的粒度代表一条策略,这样的形式相对合理,不仅方便产品层面做「所见即所得」的设计,而且实际执行过程中,也可以以原子化的形式做编排,从调度角度来讲也很容易扩展。
详细来讲,一条策略在设计上,需要关联以下几类概念:
- 检测能力:1个变更风险观测能力,和1套能力执行/调度参数
- 变更场景:以变更渠道/类型/阶段3个维度描述变更场景,1条策略匹配1个变更渠道,N个变更类型,N个变更阶段
- 变更对象:1个范围的变更对象,同时也要支持细粒度的变更对象KV属性
检测能力的执行调度这部分不用太细讲,变更场景的话,从经验角度,每个变更工单有多个变更阶段,所以变更观测需要观测的维度也是阶段维度,这样的话按照变更渠道/类型/阶段去拆解变更场景也是比较合理的。设计上的纠结点主要在于变更对象匹配,在实际场景中,如果变更对象是以树的形式管理的,那么匹配变更对象可能得匹配某个非叶子节点;如果变更对象可以用通用的string表达,那么用户也可能希望一条策略匹配多个string或者正则。在这个基础上,有时用户也希望某条策略去匹配某个顶层的树节点,但又豁免掉下级的某些子节点。所以,由这些需求反推,我们可以简单得到这样的变更对象Matcher设计:
1 | type ChangeObjectMatcher struct { |
在这个基础上,我们能够很方便表达出「匹配一个范围的变更对象,豁免多个子范围的变更对象」这个事情。当然,实际ExcludeMatcher应该往外面提一个字段,做成「在多个子变更场景下,豁免多个子范围的变更对象」会更加合适。至于前端分页,需要从DB筛选一波之后,内存再筛选一波,然后再做分页,这个取决于策略的量级,至少在笔者的实战场景下,DB查询已经可以变更场景+树节点,第一波出来的策略数量也不会过百,那么在内存里做分页,对内存的开销也是可以接受的。
即便如此,要兼顾产品设计和观测任务运行时的「所见即所得」的话,还有两件事情要做。
第一件事情是,需要从变更对象的角度,另外增加一套ExcludeMatcher。这样做是因为,用户操作层面,可能在前端可以看到某些策略是否匹配上,但也希望能够看到这些策略的启禁用情况。这个启禁用是变更对象视角下的,所以需要有另一套ExcludeMatcher来表达。
第二件事情是,需要把前端视角的匹配逻辑和检测任务视角的匹配逻辑区分两个Policy。这是因为,前端视角下筛选条件有限,理论上肯定无法「所见即所得」,比如我们需要匹配细粒度的ObjectAttributes,那只有在检测任务运行时才会知道。所以除了产品设计上需要有额外的说明之外,检索流程设计上用一个单独的MatchPolicy字段去表达,比如MatchPolicy为Runtime就匹配细粒度的变更属性,那这样写策略匹配逻辑就比较方便了。