目前我们最新上线的 mobileapi(用于给手机客户端提供接口服务) 项目已经引入了 JBoss 的 Drools 规则引擎
在介绍项目的上下文之前 我们先看看我们之前代码中存在的各种膈应人的逻辑:
- 亲子购物和亲子游乐的 7.2.0 版本, 产品页显示团购推荐模块, 同时隐藏热卖产品模块…
- 产品详情页的预约按钮文案, 旅游婚纱显示”咨询有礼”; 幼儿教育和早教中心如果存在”试听”的tag, 则显示”预约试听”; 女士婚纱/旗袍/晚礼服/龙凤褂如滚存在”0元试听”标签则显示”预约试纱”, 默认显示”预约看店”…
- 商户页亲子购物/亲子游乐分类在7.2.0版本, 产品推荐模块显示2个产品, 否则显示4个产品…
- 旅游婚纱的判断渗透到各个接口中…
于是, 代码中会出现各种各样的 fuck
字眼, 当然大部分都是我写的, 原谅我是个脾气暴躁的死程序员…
1 | // 20141222 对于旅游婚纱产品, 显示为咨询有礼 |
1 | // 20141222 过滤旅游婚纱的分类 |
1 | // 收集所有推荐的产品id, 这个productIds 也就是输出的排序 |
1 | boolean isHomeDecorate = HomeUtil.isHomeDecorate(shop.getMainCategoryId()) && VersionUtil.compare(context.getVersion(), "6.9.5") >= 0; |
规则引擎的出现非常好的解决了这样将一些复杂的条件判断耦合在业务代码中的难以维护的问题. 通过一组规则(版本/分类/标签等), 为接口的返回提供了一套预判的配置, 比如”最多输出几个? 最少输出几个? 忽略哪几个? 默认是啥?”, 那么接口在处理业务逻辑的时候, 完全可以只通过这个配置处理, 将复杂的判断抽离统一的业务逻辑.
如何使用
- 使用eclipse或者idea作为IDE的同学可以去直接下载drools的插件. 官方的库里就有
- 对于本地要启动mobileapi项目的, 需要在本地的
%TOMCAT_HOME%/bin/catalian.sh
中加入这么一行代码, 放心加, 这个是生产环境上也有的.
1 | CATALINA_OPTS="$CATALINA_OPTS -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.region=CN" |
参考 之前写的规则, 添加自己新的规则.
通过 单元测试, 来验证自己的规则已经生效, 并且符合预期.
举个例子
这个需求是: 针对7.2.0版本的客户端及以上版本, 如果商户分类是亲子购物或者亲子游乐, 则显示4个团购推荐, 这4个推荐必须是亲子游乐/幼儿教育/亲子摄影分类的. 如果搜索到的团购不足2个, 则隐藏该模块, 是不是很绕?
于是我们创建一个用于配置的Fact对象:
1 | public class GrouponRecommendConfigFT { |
并正对这个业务去定制规则:
1 | declare BabyFunAndShopping |
可以看到, 我们只是针对业务, 去生成一个最终的配置, 最终的接口逻辑就是根据这份配置去做最终的输出.
下面针对这个业务, 定制我们的单元测试来验证规则:
1 |
|
下一步的设想
可以通过一个web去动态的修改规则实现规则的动态调整, 而不用停机发布, 可以将规则保存在zookeeper, 或者通过swallow的方式发送给所有的web server. 这一点drools框架是支持的.
在一些适合的场景也引入规则引擎, 如发红包活动, 防作弊点赞等.
常见问题
- 既然drools可以动态加载, 和groovy有点类似, 为啥不直接用groovy?
groovy虽然可以动态加载, 但实际上还是要在里面写一大堆if…else逻辑, 等于是把恶心的逻辑放到了另一个文件, 并没有实现解耦. 而规则引擎更像是邮件的fillter, 配置更灵活. 另一个很重要的原因是, drools 在compile rules的时候, 会通过 RETE 算法进行优化, 效率更高.
- 为啥不直接用lion?
还嫌lion不够乱的? 每个api加2个配置, 维护都是一场灾难.
- 性能怎样?
前面说到了drools在 RETE 的算法的基础上还做了一写自己的优化, 性能绝对不是问题. 在之前的公司, 使用 drools 做风控判断. 针对3到5各rule, 2000+的qps轻轻松松.