规则引擎之drools入门

现实生活中,规则无处不在。对于某些企业级应用,诸如欺诈检测软件,购物车,活动监视器,信用和保密应用之类的系统,经常会有大量的、错综复杂的业务规则配置,而且随着企业管理者的决策变化,这些业务规则也会随之发生更改。我们开发人员不得不一直处理软件中的各种复杂问题,不仅需要将所有数据进行关联,还要尽可能快地一次性处理更多的数据,甚至还需要以快速的方式更新相关机制。

规则是什么?

我们的日常生活是由规则驱动的。每次我们在开车的时候停在红灯处,之所以这样做,因为我们遵循一条规则,灯变红时我们应该停下来。如果你跳起来,最终会落到地面,这是由地球引力所决定的,它可以被表示成简单的数学方程。然而,对于我们生活日常中的规则,我们使用更加简单的结构来表示:当 XXX 的时候,做 XXX 的事。

这种结构对于组织复杂的业务逻辑非常适用。几乎所有复杂的业务场景都是由大量简单规则组成,它们共同提供了全面的复杂评估。整个评估过程开始于某一个简单的规则,然后不断地进行推导及冲突处理,最终得到一个评估结果。

不同的规则引擎的语法可能会有所不同,但基本都是如下这种经典结构,我们介绍的 Drools 也是如此。一旦某组数据满足条件匹配,则会用匹配到的数据执行某些特定的动作。

when 满足什么样的条件
then 最终结果

业务规则都是基于这种声明式的编程范例,而条件只是作为过滤器,只要数据被引入到符合条件的规则引擎,就可以确定需要执行的规则或规则组。这意味着流程的控制既不是由规则的编写顺序决定,也不是数据的输入顺序决定,而是由规则声明的条件确定的。

为什么需要使用规则?

在简单地了解过规则以后,你可能仍然对为什么使用规则而感到困惑。如果只是一个或几个逻辑判断,确实没有必要使用规则引擎,命令行语言可以更好地满足我们的需求。然而,业务规则往往是一个庞大且不断变化的规则组合,这使得系统非常复杂,如果只是使用常规代码,则会产生大量的维护工作。

随着业务规则的增长或应用场景的变化,需求会不断地变更,此时,我们可以通过调整规则而使其得到实现。主要是因为业务规则遵循以下原则:

  • 它们是独立的
  • 它们很容易更新
  • 每个规则控制所需的最小信息量
  • 它们允许不同背景的人进行合作

第一个 Drools 程序

学习一样新东西的最好的方法就是尝试使用它,下面编写一个简单的 Drools 应用程序。首先,我们需要创建一个 Maven 工程,然后在其 pom.xml 文件添加如下包依赖:

<dependencies>
    <dependency>
        <groupId>org.kie</groupId>
        <artifactId>kie-api</artifactId>
        <version>6.5.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
        <version>6.5.0.Final</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

在 resources 目录下创建文件 helloworld.drl 文件,内容如下:

package helloworld;

rule "HelloWorld"
    when
        eval(true)
    then
        System.out.println("HelloWorld");
end

在 resources/META-INF 目录下创建 kmodule.xml 文件,kmodule.xml 用来描述知识库资源的选择及知识库与会话的配置,内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="helloWorldBase">
        <ksession name="helloWorldSession"/>
    </kbase>
</kmodule>

创建单元测试类 HelloWorldTest,内容如下:

public class HelloWorldTest {
    @Test
    public void testHelloWorld() {
        KieServices kieServices = KieServices.Factory.get();
        KieContainer kieContainer = kieServices.newKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("helloWorldSession");
        kieSession.fireAllRules();
        kieSession.dispose();
    }
}

运行单元测试即可输出 HelloWorld 字样。

规则语法简介

现在我们已经执行了我们的第一条规则,是时候去了解一下规则语言了。我们先分析下我们之前写的规则,它的结构由条件和结果组成:

package 包名

rule "规则名"
when
    (条件) - 也叫作规则的 LHS(Left Hand Side)
then
    (动作/结果) - 也叫作规则的 RHS(Right Hand Side)
end

每个 drl 都必须声明一个包名,这个包名与 Java 里面的不同,它不需要与文件夹的层次结构一致。规则名是规则的唯一标识,所以规则编写过程中需要保证它是不重复的。规则的条件(LHS)是按照 DRL 语言编写的,条件eval(true)表示永远为真,即该条规则总会获得执行。而规则的结果(RHS)使用 Java 语言实现,简单地输出了 HelloWorld 字样。

为了简单起见,这里不对 DRL 作完全的描述,更详细的语法请参考:http://docs.jboss.org/drools/release/6.5.0.Final/drools-docs/html_single/index.html#d0e4235

Drools 原理

DRL 解释执行流程

Drools 规则是在 Java 应用程序上运行的,其要执行的步骤顺序由代码确定。为了实现这一点,Drools 规则引擎将业务规则转换成执行树,如下图所示:

如上图所示,每个规则条件分为小块,在树结构中连接和重用。每次将数据添加到规则引擎中时,它将在与此类似的树中进行求值,并到达一个动作节点,在该节点处,它们将被标记为准备执行特定规则的数据。

规则引擎工作方式

Drools 规则引擎基于 ReteOO 算法(对面向对象系统的Rete算法进行了增强和优化的实现),它将事实(Fact)与规则进行匹配,以推断相应的规则结果,这个过程称之为模式匹配。

规则引擎默认不会在规则评估时立即执行业务规则,除非我们强制指定。当我们到达一个事实(Fact)与规则相匹配的节点时,规则评估会将规则操作与触发数据添加到一个叫作议程(Agenda)的组件中,如果同一个事实(Fact)与多个规则相匹配,就认为这些规则是冲突的,议程(Agenda)使用冲突解决策略(Conflict Resolution strategy)管理这些冲突规则的执行顺序。整个生命周期中,规则评估与规则执行之间有着明确的分割。规则操作的执行可能会导致事实(Fact)的更新,从而与其它规则相匹配,导致它们的触发,称之为前向链接。

应用场景

规则引擎虽然非常强大,但并非所有场景都适用。一般来说,规则引擎适用的项目都具有以下一个或多个特征:

  • 存在一个非常复杂的场景,即使对于商业专家也难以完全定义
  • 没有已知或定义明确的算法解决方案
  • 有不稳定需求,需要经常更新
  • 需要快速做出决策,通常是基于部分数据量

附录

Drools Jar 包介绍:

  • knowledge-api.jar - 提供接口和工厂。它清楚地描述用户 API 的职责,还有什么引擎 API。
  • knowledge-internal-api.jar - 提供内部接口和工厂。
  • drools-core.jar - 核心引擎,运行时组件。包含 RETE 引擎和 LEAPS 引擎。
  • drools-compiler.jar - 包含编译器/构建器组件,以获取规则源,并构建可执行规则库。
  • drools-decisiontables.jar - 决策表编译器组件,在 drools-compiler 组件中使用。支持 Excel 和 CSV 输入格式。

原文发布于微信公众号 - Linyb极客之路(gh_c420b2cf6b47)

原文发表时间:2018-04-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏吉浦迅科技

DAY25: 阅读硬件的多线程

13040
来自专栏牛客网

C++后台腾讯WXG实习面经(已拿offer)

时间:2018年4月16日 岗位:C/C++后台开发(Linux) BG:WXG 关于我:本科大三 预计2019年毕业 一面(普通技术面) 过程:递交简历 ->...

839100
来自专栏zhangdd.com

zabbix监控交换机出图断断续续问题解决

Zabbix通过snmp监控交换机流量时,之前经常出现出图很不稳定的情况。我先对zabbix以及snmp是怎么来监控流量的做个简单的说明:

32440
来自专栏java一日一条

Java Fork/Join 框架

响应式编程(Reactive Programming / RP)作为一种范式在整个业界正在逐步受到认可和落地,是对过往系统的业务需求理解梳理之后对系统技术设计/...

30710
来自专栏小樱的经验随笔

BugkuCTF web基础$_GET

前言 写了这么久的web题,算是把它基础部分都刷完了一遍,以下的几天将持续更新BugkuCTF WEB部分的题解,为了不影响阅读,所以每道题的题解都以单独一篇文...

333100
来自专栏木子昭的博客

Python3好用的原生api

对列表进行反序是一个很常见的操作, 但python反向切片的玩法实在是非常简洁, 让人无法拒绝, 其实对某一数据结构进行"反向"是一个很有意...

9510
来自专栏施炯的IoT开发专栏

微软移动 Nokia Lumia SensorCore SDK 介绍及上手体验

    早在今年的BUILD大会上,诺基亚就宣布了SensorCore以及它的部分演示。今天,它终于面世了,大家可以去Building Apps for Win...

28570
来自专栏牛客网

C++后台腾讯WXG实习面经(已拿offer)

21750
来自专栏向治洪

Java中的ReentrantLock和synchronized两种锁机制的对比

原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什...

25850
来自专栏纯洁的微笑

小明历险记:规则引擎Drools教程一

16430

扫码关注云+社区

领取腾讯云代金券