背景
考拉安全部技术这块目前主要负责两块业务:一个是内审,主要是通过敏感日志管理平台搜集考拉所有后台系统的操作日志,数据导入到es后,结合storm进行实时计算,主要有行为查询、数据监控、事件追溯、风险大盘等功能;一个是业务风控,主要是下单、支付、优惠券、红包、签到等行为的风险控制,对抗的风险行为包括黄牛刷单、恶意占用库存、机器领券、撸羊毛等。这两块业务其实有一个共通点,就是有大量需要进行规则决策的场景,比如内审中需要进行实时监控,当同一个人在一天时间内的导出操作超过多少次后进行告警,当登录时不是常用地登录并且设备指纹不是该账号使用过的设备指纹时告警。而在业务风控中需要使用到规则决策的场景更多,由于涉及规则的保密性,这里就不展开了。总之,基于这个出发点,安全部决定开发出一个通用的规则引擎平台,来满足以上场景。
写在前面
在给出整体架构前,想跟大家聊聊关于架构的一些想法。目前架构上的分层设计思想已经深入人心,大家都知道要分成controller,server,dao等,是因为我们刚接触到编码的时候,mvc的模型已经大行其道,早期的jsp里面包含大量业务代码逻辑的方式已经基本绝迹。但是这并不是一种面向对象的思考方式,而往往我们是以一种面向过程的思维去编程。举个简单例子,我们要实现一个网银账户之间转账的需求,往往会是下面这种实现方式:
这种设计在需求简单的情况下看上去没啥问题,但是当需求变得复杂后,会导致代码变得越来越难以维护,整个架构也会变的腐烂。比如现在需要增加账户的信用等级,不同等级的账户每笔转账的最大金额不同,那么我们就需要在service里面加上这个逻辑。后来又需要记录转账明细,我们又需要在service里面增加相应的代码逻辑。最后service代码会由于需求的不断变化变得越来越长,最终变成别人眼中的“祖传代码”。导致这个问题的根源,我认为就是我们使用的是一种面向过程的编程思想。那么如何去解决这种问题呢?主要还是思维方式上需要改变,我们需要一种真正的面向对象的思维方式。比如一个“人”,除了有id、姓名、性别这些属性外,还应该有“走路”、“吃饭”等这些行为,这些行为是天然属于“人”这个实体的,而我们定义的bean都是一种“失血模型”,只有get/set等简单方法,所有的行为逻辑全部上升到了service层,这就导致了service层过于臃肿,并且很难复用已有的逻辑,最后形成了各个service之间错综复杂的关联关系,在做服务拆分的时候,很难划清业务边界,导致服务化进程陷入泥潭。
对应上面的问题,我们可以在Account这个实体中加入本应该就属于这个实体的行为,比如借记、贷记、转账等。每一笔转账都对应着一笔交易明细,我们根据交易明细可以计算出账户的余额,这个是一个潜在的业务规则,这种业务规则都需要交由实体本身来维护。另外新增账户信用实体,提供账户单笔转账的最大金额计算逻辑。这样我们就把原本全部在service里面的逻辑划入到不同的负责相关职责的“领域对象”当中了,service的逻辑变得非常清楚明了,想实现A给B转账,直接获取A实体,然后调用A实体中的转账方法即可。service将不再关注转账的细节,只负责将相关的实体组织起来,完成复杂的业务逻辑处理。
上面的这种架构设计方式,其实就是一种典型的“领域驱动设计(DDD)”思想,在这里就不展开说明了(主要是自己理解的还不够深入,怕误导大家了)。DDD也是目前非常热门的一种架构设计思想了,它不能减少你的代码量,但是能使你的代码具有很高的内聚性,当你的项目变得越来越复杂时,能保持架构的稳定而不至于过快的腐烂掉,不了解的同学可以查看相关资料。要说明的是,没有一种架构设计是万能的、是能解决所有问题的,我们需要做的是吸收好的架构设计思维方式,真正架构落地时还是需要根据实际情况选择合适的架构。
整体架构设计
上面说了些架构设计方面的想法,现在我们回到规则引擎平台本身。我们抽象出了四个分层,从上到下分别为:服务层、引擎层、计算层和存储层,整个逻辑层架构见下图:
在各个分层的逻辑架构划定后,我们就可以开始分析整个平台的业务功能模块。主要包括了事件接入模块、指标计算模块、规则引擎模块、运营中心模块,整个业务架构如下图:
1.事件接入中心
事件接入中心主要包括事件接入模块和数据管理模块。数据接入模块是整个规则引擎的数据流入口,所有的业务方都是通过这个模块接入到平台中来。提供了实时(dubbo)、准实时(kafka)和离线(hive)三种数据接入方式。数据管理模块主要是进行事件的元数据管理、标准化接入数据、补全必要的字段,如下图:
2.指标计算模块
指标计算模块主要是进行指标计算。一个指标由主维度、从维度、时间窗口等组成,其中主维度至少有一个,从维度最多有一个。如下图:
举个例子,若有这样一个指标:“最近10分钟,同一个账号在同一个商家的下单金额”,那么主维度就是下单账号+商家id,从维度就是订单金额。可以看到,这里的主维度相当于sql里面的group by,从维度相当于count,数值累加相当于sum。从关于指标计算,有几点说明下:
3.规则引擎模块
计划开始做规则引擎时进行过调研,发现很多类似的平台都会使用drools。而我们从一开始就放弃了drools而全部使用groovy脚本实现,主要是有以下几点考虑:
当然还有另外一种方式是将drools和groovy结合起来,综合双方的优点,也是一种不错的选择,大家可以尝试一下。
规则引擎模块是整个平台的核心,我们将整个模块分成了以下几个部分:
规则引擎在设计中也碰到了一些问题,这里给大家分享下一些心得:
规则配置如下图所示:
未来规划
后面规则引擎平台主要会围绕下面几点来做: