1背景介绍1.1什么是DMN?DMN的全称是DecisionModelandNotation(决策模型和符号,decisionmodelandnotation)。规范旨在帮助参与决策的每个人简单快速地理解决策过程。DMN标识DMN是由OMG(ObjectManagementGroup,对象管理组织)管理的规范,而UML在该组织下广为人知。DMN示例如图所示。DMN的形式类似于流程图,可以通过可视化更直观地反映流程和处理过程,每个节点用一个有逻辑的表格来表示节点处理数据的方式。由于DMN是一个规范,在应用方面,主要依赖于各个DMN工具供应商提供具体的实现,类似于SQL语句与MySQL、Oracle的关系。1.2为什么使用DMN应用DMN的主要目的是解决转转图书项目定价逻辑过于复杂的问题。在引入DMN之前,本书项目使用java代码来实现产品人员提供的定价逻辑。但是随着逻辑变得越来越复杂,定价逻辑的代码变得更难阅读和维护。同时,将产品人员的逻辑翻译成代码是一个单向的过程。实现过程只有程序员才能看懂,产品人员只能通过结果来推断是否正确合理。逻辑变得复杂后,很难快速发现问题。一部分复杂的规则、区间、系数可以通过DMN直观的表示逻辑过程,产品人员也可以直观的看到是否符合预期,也可以进行编辑,解决了过于依赖程序员的问题掌握执行也可以减少在表达和交流过程中因理解偏差而导致的错误。1.3.什么是流口水?Drools是一个用java语言编写的开源规则引擎,是DMN规范的一个实现。比如DMN规范就是一个接口,Drools就是其中一个实现接口的工具类。DroolslogoDrools属于Kie开源社区,kie社区由RedHat赞助,社区活跃度比较高。Kie'sproducts1.4为什么选择DroolsDrools引擎基于java实现,无需为Drools单独部署运行环境,运维成本为0,非常友好。其次,DMN规范规定,实现其规范的软件必须满足三级增量合规级别,第三级最高,第一级最低。当第三层满足时,第一层和第二层必须同时满足。Drools引擎对DMN规范的支持属于第三级别,功能完备。Drools引擎为各种版本的DMN提供支持。同时,Drools引擎社区非常活跃,提供完整的高可靠性工作组件。2Drools引擎应用以下内容基于7.61.0版本,区别于8.x及以上版本2.1官方推荐的最直接的应用方法首先需要部署运行一个KieServer来执行DMN规则,以及然后部署Drools官方提供的基于Web的工作站软件,用于编辑、测试和发布DMN规则。部署DMN规则后,Drools提供的工作站软件可以通过KieServerRESTAPI运行规则获取结果,即通过Http请求KieServer提供的接口。2.2转转书籍的限制首先,书籍项目并没有按照官方推荐直接部署应用。有几个原因:首先,转转的内部项目部署有自己的框架体系,而KieServer是基于JBoss运行的服务器软件,在现有系统中部署外部项目需要额外的运维成本。其次,KieServer作为第三方工具,出现问题时很难仅靠图书项目的工作人员解决,对于转转图书这样的在线商业项目来说,等待社区反馈也比较困难。因此,为了尽可能降低出现问题的概率和节省人力,书籍项目往往会引入尽可能少的外部依赖。基于以上原因,本书项目在应用Drools时选择了另一种方式。3没有KieServer的Drools引擎实践没有KieServer,自然也就没有RESTAPI的http接口,也失去了官方工作站的支持,不过相对来说,Drools对这种场景提供了一定的支持。3.1DMN规则的在线编辑Kie旗下还有一个产品叫Kogito,这是一款提供BPMN和DMN在线编辑的服务器软件。同时还有一个一体的js文件,实现了DMN的在线编辑。完整的功能。js提供的界面书工程就是基于这个js文件打包的,增加了易用性,将DMN编辑功能集成到现有的工作后台。包裹页面,下面是js提供的功能区,添加编辑记录列表,方便管理和回滚。编辑列表js工具编辑的DMN规则内容是一个xml格式的字符串,可以使用js提供的getContent()接口导出。3.2使用Drools引擎执行DMN规则没有KieServer的支持,需要通过代码来运行Drools引擎。首先在项目中引入Drools引擎组件,然后在项目中创建对Drools引擎的引用并执行。//初始化KieServicesKieServiceskieServices=KieServices.Factory.get();//通过指定的maven依赖创建Kie容器ReleaseIdreleaseId=kieServices.newReleaseId("com","my-kjar","1.0.0");KieContainerkieContainer=kieServices.newKieContainer(releaseId);//通过容器获取DMN运行时的DMNRuntimedmnRuntime=KieRuntimeFactory.of(kieContainer.getKieBase()).get(DMNRuntime.class);//通过获取需要执行的DMNModelDMN运行时,其中包含DMN规则Stringnamespace="my-namespace";StringmodelName="dmn-model-name";DMNModeldmnModel=dmnRuntime.getModel(namespace,modelName);//获取上下文并传入DMN需要使用的规则DataDMNContextdmnContext=dmnRuntime.newContext();dmnContext.set("输入数据","123");//执行规则获取结果DMNResultdmnResult=dmnRuntime.evaluateAll(dmnModel,dmnContext);for(DMNDecisionResultdr:dmnResult.getDecisionResults()){log.info("Decision:'"+dr.getDecisionName()+"',"+"Result:"+dr.getResult());}自Kie容器使用maven作为读取DMN配置的手段,因此需要将DMN规则的内容打包成Kie容器可以识别的jar包,部署到项目可以访问的maven环境中。3.3改进处理流程按照以上流程,Drools引擎可以在没有Kie服务器的情况下运行。但是距离登陆线上还有一点距离。官方工作站可以提供DMN规则上线前的验证测试功能,而一体机js文件没有,所以为了保证准确性和稳定性,需要额外实现DMN规则的验证动作.这里可以使用KieContainer提供的验证接口来触发Drools引擎的验证动作并获取结果。结果verify=kieContainer.verify();for(Messagemessage:verify.getMessages()){log.info(message.getLevel().name()+":"+message.getText());}将验证flow保存DMN规则时,可以快速发现是否有写错。3.4DLC——在没有maven环境的情况下运行Drools引擎前面提到Drools引擎需要使用maven来获取运行的DMNjar文件,但是在实际应用中,线上环境可能并没有部署maven。比如本书项目需要在公司内部公共spark集群上运行Drools,但是spark集群上并没有部署maven环境,按照上面的流程运行会报错。所以我们需要一种手段让Drools脱离maven环境。通过分析源码可以发现,在创建KieContainer时,会使用KieServices.getRepository()方法获取数据源,通过maven坐标找到其中的KieModule。那么我们要做的就是直接将我们需要的jar写入到KieServices的数据源中://首先通过JarOutputStreamByteArrayOutputStreamoutputStream=newByteArrayOutputStream()生成一个包含DMN规则字符串并且符合Drools规范的jar二进制流;JarOutputStreamjos=newJarOutputStream(outputStream);//kmodule.xmljos.putNextEntry(newJarEntry("/META-INF/kmodule.xml"));jos.write("
