最近的开发涉及到了规则引擎Drools的一些开发,因为当中涉及到一些概念比如KieServices、KieContainer、KieModule、KieBase和KieSession他们各自是什么?各自起什么作用?当中有何种联系,基于这些疑问就对于其中的概念做了一个初步了解。
1. 首先需要解释的是什么是KIE?
KIE其实是 Knowledge is Everything 的简写
KIE是jBoss里面一些相关项目的统称,下图就是KIE代表的一些项目,其中我们比较熟悉的就有jBPM和Drools。
这些项目都有一定的关联关系,并且存在一些通用的API,比如说涉及到构建(building)、部署(deploying)和加载(loading)等方面的,这些API就都会以KIE作为前缀来表示这些是通用的API。前面看到的一些KieServices、KieContainer、KieSession类就都是KIE的公共API。 总的来说,就是jBoss通过KIE将jBPM和Drools等相关项目进行了一个整合,统一了他们的使用方式。像KieServices这些KIE类就是整合后的结果,在Drools中这样使用,在jBPM里面也是这样使用。
2. KieServices和KieContainer
KieServices是进行规则引擎操作的起始点。通过KieService可以获得Drools中的其他组件。
在Drools 6.x版本中,可以通过下面的方式来获取KieService
KieServices ks = KieServices.Factory.get();
在Drools7.x版本中,可以通过下面的方式获取KieService
KieServices ks = KieServices.get();
通过KieServices可以获取到KieContainer。KieContainer确定了当创建规则引擎实例时所包含的规则范围。
通过KieServices对象得到一个KieContainer,利用kieContainer对象创建一个新的KieSession,
创建session的时候我们传入了一个name:“ksession-name”,这个字符串很眼熟吧,这个就是我们定义的kmodule.xml文件中定义的ksession的name。kieContainer根据kmodule.xml定义的ksession的名称找到KieSession的定义,然后创建一个KieSession的实例。
KieSession就是一个到规则引擎的链接,通过它就可以跟规则引擎通讯,并且发起执行规则的操作。通过kSession.insert方法来将事实(Fact)插入到引擎中,也就是Working Memory中。
然后通过kSession.fireAllRules方法来通知规则引擎执行规则。
3. KieContainer和KieModule
3.1 概念解释
可以理解KieContainer就是一个KieModule的容器。在KieContainer中可以包含KieModule,以及KieModule自身的一些依赖。因此KieModule可以以一中等级组织的方式被加载到KieContainer中
在KieModule中包含了若干的规则、流程、方法等。在通常的情况下KieModule就是一个名称为kmobule.xml的配置文件,此文件的配置内如例如:
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="rules.cp.discount">
<ksession name="rules.cp.discount.session" type="stateful"/>
</kbase>
</kmodule>
而具体的规则定义在项目的resources目录下并且与上面kmodule.xml文件中定义一个kbase元素(注意:一个kmodule.xml文件中可以定义多个kbase元素)的name属性值的子目录中,例如,如果按照上述name属性值定义了kbase后,在项目的resource目录下会存在如下的图片中的内容
├── chapter-03-classpath-tests.iml
├── pom.xml
├── src
│ └── test
│ ├── java
│ │ └── org
│ │ └── drools
│ │ └── devguide
│ │ └── chapter03
│ │ └── KieContainerClasspathTests.java
│ └── resources
│ ├── META-INF
│ │ └── kmodule.xml
│ └── rules
│ └── cp
│ └── discount
│ └── classpath-discount-rules.drl
3.2 加载方式
当加载规则的时候可以通过两种方式来加载规则。
3.2.1 第一种方式:通过classpath进行规则的加载
例如在当前的目录下存在几个Maven的模块
chapter-03-classpath-tests
chapter-03-kjar-parent
chapter-03-kjar-premium-discounts
chapter-03-kjar-simple-discounts
chapter-03-classpath-tests
其pom文件关键内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<artifactId>chapter-03-classpath-tests</artifactId>
<name>Drools 6 Developer Guide - Chapter 3: Classpath Tests</name>
<packaging>jar</packaging>
<dependencies>
<!-- Start dependencies for the other Kie Modules -->
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-simple-discounts</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-premium-discounts</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-parent</artifactId>
<scope>test</scope>
</dependency>
<!-- End dependencies for the other Kie Modules -->
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>model</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>shared</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
chapter-03-kjar-parent
其pom文件关键内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<artifactId>chapter-03-kjar-parent</artifactId>
<name>Drools 6 Developer Guide - Chapter 3: Parent Knowledge Assets</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-simple-discounts</artifactId>
</dependency>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-premium-discounts</artifactId>
</dependency>
</dependencies>
</project>
chapter-03-kjar-premium-discounts
其pom文件关键内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<artifactId>chapter-03-kjar-premium-discounts</artifactId>
<name>Drools 6 Developer Guide - Chapter 3: Premium Discounts Knowledge Assets</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>model</artifactId>
<type>jar</type>
</dependency>
</dependencies>
</project>
chapter-03-kjar-simple-discounts
其pom文件关键内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<artifactId>chapter-03-kjar-simple-discounts</artifactId>
<name>Drools 6 Developer Guide - Chapter 3: Simple Discounts Knowledge Assets</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>model</artifactId>
<type>jar</type>
</dependency>
</dependencies>
</project>
由于在chapter-03-classpath-tests模块的pom文件有对其他三个模块chapter-03-kjar-parent、chapter-03-kjar-premium-discounts和chapter-03-kjar-simple-discounts的引用,因此当通过下面的创建KieContainer时,会加载这四个模块中的所定义的所有规则
@Test
public void loadingRulesFromLocalKieModule() {
System.out.println("### Running loadingRulesFromLocalKieModule() Test ###");
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.newKieClasspathContainer();
Results results = kContainer.verify();
results.getMessages().stream().forEach((message) -> {
System.out.println(">> Message ( "+message.getLevel()+" ): "+message.getText());
});
assertThat(false, is(results.hasMessages(Message.Level.ERROR)));
kContainer.getKieBaseNames().stream().map((kieBase) -> {
System.out.println(">> Loading KieBase: "+ kieBase );
return kieBase;
}).forEach((kieBase) -> {
kContainer.getKieSessionNamesInKieBase(kieBase).stream().forEach((kieSession) -> {
System.out.println("\t >> Containing KieSession: "+ kieSession );
});
});
// Let's load the configurations for the kmodule.xml file
// defined in the /src/test/resources/META-INF/ directory
KieSession kieSession = kContainer.newKieSession("rules.cp.discount.session");
Customer customer = new Customer();
customer.setCategory(Customer.Category.BRONZE);
Order order = new Order();
order.setCustomer(customer);
kieSession.insert(customer);
kieSession.insert(order);
int fired = kieSession.fireAllRules();
assertThat(1, is(fired));
assertThat(5.0, is(order.getDiscount().getPercentage()));
System.out.println("### Finished loadingRulesFromLocalKieModule() Test ###");
}
输出如下:
### Running loadingRulesFromLocalKieModule() Test ###
>> Loading KieBase: rules.simple
>> Containing KieSession: rules.simple.discount
>> Containing KieSession: rules.simple.sl.discount
>> Loading KieBase: rules.cp.discount
>> Containing KieSession: rules.cp.discount.session
>> Loading KieBase: rules.premium
>> Containing KieSession: rules.premium.discount
>> Loading KieBase: discount
>> Containing KieSession: rules.discount.all
### Finished loadingRulesFromLocalKieModule() Test ###
Process finished with exit code 0
3.2.2 第二种方式:通过Maven提供的工具进行加载(或者称之为Kie-CI)
通过Maven方式进行加载与通过classpath进行加载的最大不同在于,在pom文件中不需要依赖要加载的其他模块的GAV(GroupId, ArtifactId, and Version),取而代之的时需要加入下面的GAV
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<scope>test</scope>
</dependency>
此时就可以通过GAV方式来精确指定要加载的模块中的规则,如下所示
KieContainer kContainer = ks.newKieContainer(ks.newReleaseId( "org.drools.devguide", "chapter-03-kjar-simple-discounts", "1.0.0"));
-
KieModule、KieBase和KieSession
KieBase是什么呢?KieBase就是一个知识仓库,包含了若干的规则、流程、方法等,在Drools中主要就是规则和方法,KieBase本身并不包含运行时的数据之类的,如果需要执行规则KieBase中的规则的话,就需要根据KieBase创建KieSession。在KIESession中加载了在Drools规则引擎中真正要运行的规则rules
- KieHelper说明
有时候可以通过KieHelper来创建一个KieSession,例如
private KieSession createKieSessionFromDRL(String drl) {
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
List<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);
for (Message message : messages) {
System.out.println("Error: " + message.getText());
}
throw new IllegalStateException("Compilation errors were found. Check the logs.");
}
return kieHelper.build().newKieSession();
}
这个方法的实现很直接。它通过KieHelper来编译一个字符串形式规则。并且kieHelper可以在编译期间检查规则的错误,如果有错误的话,可以通过Results进行输出。如果没有错误的话,直接创建一个KieSession。
KieHelper工具类并不是Drools对外开房的API的一部分。这个类提供了一些很方便的可以省略很多模板代码的方式来创建KieSession。由于这个类并不是Kie-api(Drools中的一个依赖)的一部分,因此虽然其使用方便,但是当使用Drools最新的版本时有可能出现向后不兼容的问题(意思是,在最新的Drools版本中有可能会删除或者禁用掉这个类)
『Mastering JBoss Drools 6 for Developers P195』
KieHelper这个类提供了很多不同的方法可以向KieContainer中添加资源(例如规则),然后验证,最终创建出一个KieContainer。对于KieHelper的一个经典使用方式的代码片段类似上面的代码片段。
KieHelper不仅仅用来单元或者继承测试。你的应用可以使用KieHelper以细粒度的(fine-grained)方式类控制你所需要加入到KieContainer中的资源。典型的使用场景例如:你可以通过KieHelper来向KieContainer中动态加载字符串形式的规则
参考:https://blog.csdn.net/u012373815/article/details/53907340
参考:Mastering JBoss Drools 6 for Developers.pdf