1 布景介绍

1.1 DMN是什么

DMN全称Decision Model and Notation(决议计划模型和符号、决议计划模型和表明法),是一种用于表明业务决议计划和规矩的标准,旨在协助参与决议计划的人都能简略快速了处理议计划进程。

转转图书对基于Drools引擎的DMN实践

DMN是由OMG(Object Management Group,对象办理组织)办理的一种标准,该组织下比较知名的还有UML等。

转转图书对基于Drools引擎的DMN实践

如图所示,DMN的体现形式近似于流程图,经过可视化来更直观地体现出流转和处理的进程,而各个节点运用顺便逻辑的表格来体现该节点处理数据的方法。

由于DMN是一个标准,所以在运用上,首要依托各个DMN东西供货商供给具体完结,近似于SQL语句和MySQL、Oracle的关系。

1.2 为什么要用DMN

运用DMN首要意图是为了处理转转图书项目定价逻辑过于凌乱的问题。

在引进DMN之前,图书项目运用java代码完结产品人员供给的定价逻辑。可是随着逻辑越来越凌乱,定价逻辑的代码也越来越难以阅读和保护。一起,将产品人员的逻辑翻译成代码是一个单向进程,只要程序员能了解完结进程,产品人员只能经过成果反推是否正确合理,在逻辑变得凌乱之后,很难快速发现问题。

转转图书对基于Drools引擎的DMN实践

而经过DMN,能够可视化的表明出逻辑进程,产品人员也能够直观的看到是否符合预期,还能够进行修改,从而处理了只要程序员掌握完结而过于依赖程序员的问题,也能削减表述沟通的进程发生了解偏差进而导致的过错。

转转图书对基于Drools引擎的DMN实践

1.3 什么是Drools

Drools是用java言语编写的开源规矩引擎,是DMN标准的一种完结。举例来说DMN标准便是接口,Drools便是完结了接口的其间一个东西类。

转转图书对基于Drools引擎的DMN实践

Drools属于Kie开源社区,而kie社区由Red Hat资助,有比较高的社区活跃度。

转转图书对基于Drools引擎的DMN实践

1.4 为什么挑选Drools

Drools引擎是根据java完结的,这使得不需求为Drools独自布置运转环境,运维本钱是0,非常友爱。

其次,DMN标准规定完结其标准的软件有必要满意三级递增的符合性等级,三级最高,一级最低,满意三级等级时有必要一起满意一级和二级等级。而Drools引擎对DMN标准的支撑属于三级等级,功用完善。

转转图书对基于Drools引擎的DMN实践

一起,Drools引擎社区活跃度高,一起供给了完好的作业组件,可靠性高。

2 Drools引擎运用

以下内容根据7.61.0版别,与8.x及以上的版别存在差异

2.1 官方引荐的最直接的运用方法

首先需求布置运转一个Kie Server用来履行DMN规矩,然后布置Drools官方供给的根据Web的作业站软件用来修改、测验、发布DMN规矩。

转转图书对基于Drools引擎的DMN实践

当布置完结DMN规矩之后,能够经过Kie Server REST API来运转规矩获取成果,即经过Http的方法请求Kie Server供给的接口。

2.2 转转图书的限制

首先说图书项目并没有直接依照官方引荐的做法布置运用,原因有几个方面:

第一点,转转内部项目布置运转有自有的框架系统,而Kie Server是根据JBoss运转的服务器软件,在现有系统中布置一个外部项目需求额定的运维本钱。

其次,Kie Server作为一个第三方东西,当出现问题时仅靠图书项意图人员难以处理,而等候社区反应对于转转图书这种线上的商业项目难以接受。所以为了尽或许的削减出现问题的概率,节约人力,图书项目倾向于尽或许少的引进外部依赖。

根据以上原因,图书项目在运用Drools的时分挑选了另外一种方法。

3 脱离Kie Server的Drools引擎实践

脱离开Kie Server,天然就没有了REST API的http接口,一起也失去了官方作业站的支撑,可是相对的,Drools供给了一些对这种场景下的支撑。

3.1 在线修改DMN规矩

在Kie旗下有另一款名为Kogito的产品,是一个供给在线修改BPMN和DMN的服务器软件,一起其间有一个all-in-one的js文件,完结了在线修改DMN的悉数功用。

转转图书对基于Drools引擎的DMN实践

图书项目根据这个js文件进行了包装,增加易用性,使得DMN修改功用融入到现有的作业后台之中。

转转图书对基于Drools引擎的DMN实践

一起增加修改记载列表用来便于办理和回滚。

转转图书对基于Drools引擎的DMN实践

js东西修改后的DMN规矩内容是xml格局的字符串,能够运用js供给的getContent()接口导出。

3.2 运用Drools引擎履行DMN规矩

没有了Kie Server的支撑之后,需求经过代码的方法运转Drools引擎。

首先在项目中引进Drools引擎的组件

转转图书对基于Drools引擎的DMN实践

然后在项目中创立Drools引擎的引证并履行。

// 初始化KieServices
KieServices kieServices = KieServices.Factory.get();
// 经过指定的maven依赖创立Kie容器
ReleaseId releaseId = kieServices.newReleaseId( "com", "my-kjar", "1.0.0" );
KieContainer kieContainer = kieServices.newKieContainer( releaseId );
// 经过容器获取DMN运转时
DMNRuntime dmnRuntime = KieRuntimeFactory.of(kieContainer.getKieBase()).get(DMNRuntime.class);
// 经过DMN运转时获取需求履行的DMNModel,其间包括了DMN规矩
String namespace = "my-namespace";
String modelName = "dmn-model-name";
DMNModel dmnModel = dmnRuntime.getModel(namespace, modelName);
// 获取上下文,并传入DMN规矩需求用到的数据
DMNContext dmnContext = dmnRuntime.newContext();
dmnContext.set("inputData", "123");  
// 履行规矩获取成果
DMNResult dmnResult = dmnRuntime.evaluateAll(dmnModel, dmnContext);  
for (DMNDecisionResult dr : dmnResult.getDecisionResults()) {  
    log.info("Decision: '" + dr.getDecisionName() + "', " + "Result: " + dr.getResult());
}

由于Kie容器运用maven作为读取DMN装备的手段,所以要求DMN规矩内容需求打包到Kie容器能够识别的jar包里,并且布置到项目可访问的maven环境中。

3.3 完善处理流程

依照上述流程就能够完结脱离Kie server运转Drools引擎。可是间隔落地上线还有一小点间隔。

官方的作业站能够供给DMN规矩上线前的验证和测验功用,可是all-in-one的js文件没有,所以为了保证精确性和稳定性,需求额定完结DMN规矩的验证动作。

这里能够运用KieContainer供给的verify接口来触发Drools引擎的验证动作并获取成果。

Results verify = kieContainer.verify();
for (Message message : verify.getMessages()) {
    log.info(message.getLevel().name() + ": " + message.getText());
}

将验证流程放到保存DMN规矩的时分,就能够快速发现是否存在编写过错。

3.4 DLC – 脱离maven环境运转Drools引擎

前面说了Drools引擎需求运用maven获取运转的DMNjar文件,可是在实践运用中,线上环境纷歧定会布置maven。例如图书项目需求在公司内部共用的spark集群上运转Drools,可是spark集群上没有布置maven环境,依照上述流程运转就会报错。

所以需求一个能够使Drools脱离maven环境的手段。

经过剖析源码能够发现,在创立KieContainer的时分,会运用KieServices.getRepository()方法获取数据源,并经过maven坐标查找其间的KieModule。所以需求做的便是直接咱们需求的jar写入KieServices的数据源中:

// 首先经过JarOutputStream生成包括DMN规矩字符串,且符合Drools标准的jar二进制流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    JarOutputStream jos = new JarOutputStream(outputStream);
    // kmodule.xml
    jos.putNextEntry(new JarEntry("/META-INF/kmodule.xml"));
    jos.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><kmodule xmlns=\"http://www.drools.org/xsd/kmodule\"><kbase name=\"kbase_1\" packages=\"rules\" default=\"true\"><ksession name=\"ksession_1\" default=\"true\"/></kbase></kmodule>".getBytes(StandardCharsets.UTF_8));
    // pom.properties
    jos.putNextEntry(new JarEntry("/META-INF/maven/com.myspace/test/pom.properties"));
    jos.write("groupId=com.myspace\nartifactId=test\nversion=1.0.0".getBytes(StandardCharsets.UTF_8));
    // pom.xml
    jos.putNextEntry(new JarEntry("/META-INF/maven/com.myspace/test/pom.xml"));
    jos.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\" xmlns=\"http://maven.apache.org/POM/4.0.0\"    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">  <modelVersion>4.0.0</modelVersion>  <groupId>com.myspace</groupId>  <artifactId>test</artifactId>  <version>1.0.0</version>  <packaging>kjar</packaging>  <name>test</name>  <description></description></project>".getBytes(StandardCharsets.UTF_8));
    // dmn
    jos.putNextEntry(new JarEntry("/config/rules/test.dmn"));
    jos.write(dmn.getBytes(StandardCharsets.UTF_8));
    jos.finish();
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
// 初始化KieServices
KieServices kieServices = KieServices.Factory.get();
// 获取KieServices的数据源
KieRepository repository = kieServices.getRepository();
// 向数据源中加入根据生成二进制流结构的KeiModule
KieModule kieModule = repository.addKieModule(kieServices.getResources().newInputStreamResource(inputStream));

如上就能够完结在没有maven环境的时分依然能够让KieServices获取到咱们需求DMN规矩。

可是仅仅如此还不够,在创立KieContainer的时分,内部会生成一个KieProject实例,KieProject在实例化的进程中会默许生成一个根据maven的MavenClassLoaderResolver用于查找jar,而缺少maven环境的状况下在生成MavenClassLoaderResolver的时分也会报错。为此,咱们需求一个替代MavenClassLoaderResolver的方法。

经过剖析Drools源码能够发现,在源码中,某些时分会运用ProjectClassLoader.findParentClassLoader()来获取根据当前运转环境的ClassLoader

所以,需求在创立KieContainer的时分运用ProjectClassLoader.findParentClassLoader()生成的ClassLoader来替代默许的MavenClassLoaderResolver:

// 运用另外一个接纳ClassLoader的接口来生成KieContainer
KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId(), ProjectClassLoader.findParentClassLoader());

如此一来,就能够彻底脱离maven环境来运用Drools引擎了,而Drools引擎也能够融入到任意的java项目之中,布置到任意的java环境之下。

4 总结

DMN在实践运用中有比较明显的长处:

  • 能够在不修改代码的状况下更新逻辑,削减上线进程中或许发生的问题
  • 能够经过一套代码完结各种场景的需求
  • 流程可视化,便于了解逻辑的运转
  • 能够让一部分不熟悉程序代码的人员也能够参与修改,避免流程悉数只由程序员了解

可是相对的也存在一些弊端或者体现不够好的当地:

  • 流程凌乱时图形化体现比较凌乱,尤其是多个决议计划节点依赖相同的上游时
  • 修改规矩时仍是会有一部分场景条件需求写代码才干完结,例如列表包括(能够运用DMN专用代码FEEL完结,或者引证现已完结的java方法完结)
  • 运转规矩的时间相较于运用java代码完结要慢,由于中间触及规矩文件解析和各个节点的计算,而java代码能够更直接的完结

转转图书对基于Drools引擎的DMN实践

整体而言,DMN首要运用在向程序员以外的人员供给决议计划办理的能力,以求更精确地反映意图,从程序员的视点讲或许和写ifelse没什么纷歧样,可是其他人物的参与人员能够经过较低的学习本钱来上手完结规矩,能够削减沟通本钱和不同人的了解差异发生的不符合预期的成果。

5 参考资料

Drools官方网站:www.drools.org/

Drools官方的DMN教育:www.drools.org/learn/dmn.h…

Drools官方的15分钟简易教育:learn-dmn-in-15-minutes.com/learn/intro…

Kogito在线修改网页:sandbox.kie.org/#/

Kie官方网站:www.kie.org/

OMG官方网站的DMN页:www.omg.org/dmn/

作者简介

项赢,转转资深java工程师。长期服务于转转图书项目。

转转研制中心及业界小伙伴们的技能学习沟通平台,定期分享一线的实战经验及业界前沿的技能话题。 重视公众号「转转技能」(综合性)、「大转转FE」(专心于FE)、「转转QA」(专心于QA),更多干货实践,欢迎沟通分享~