最近的开发涉及到了规则引擎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里面也是这样使用。

Drools-核心概念入门插图
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中

Drools-核心概念入门插图1

在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目录下会存在如下的图片中的内容

Drools-核心概念入门插图2
├── 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"));
  1. KieModule、KieBase和KieSession

KieBase是什么呢?KieBase就是一个知识仓库,包含了若干的规则、流程、方法等,在Drools中主要就是规则和方法,KieBase本身并不包含运行时的数据之类的,如果需要执行规则KieBase中的规则的话,就需要根据KieBase创建KieSession。在KIESession中加载了在Drools规则引擎中真正要运行的规则rules

  1. 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中动态加载字符串形式的规则



Drools-核心概念入门插图3

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.choupangxia.com/2021/06/06/drools-core-concepts/