23.本地运行模式

URule Pro的运行有四种模式,这里要介绍的是本地运行模式。

本地模式类似于嵌入式模式,所不同的是嵌入到我们客户端应用中的URule Pro模块仅仅为其规则计算部分(core部分),不含设计器部分(console部分)。

将测试好的知识包导出为一个.data格式文件,然后把文件放在客户端应用的一个指定目录下(当然也可以通过实现KnowledgePackageFileService接口,将导出的知识包文件存储在别的地方), 这样客户端应用在调用知识包时就直接到这个指定目录下查找目标.data文件并加载。

这种模式非常适用于规则运行环境封闭,且需要对外部屏蔽规则设计细节的应用需要;其部署模式简单、快捷,一旦有新的知识包放入指定目录中,客户端应用会自动检测并加载新的版本。

配置本地应用

首先我们需要搭建一个本地模式的应用,这种应用结构与我们客户端服务器配置介绍的“配置客户端应用”方式完全相同, 其核心就是在应用当中只加载URule Pro的规则计算部分,也就是urule-core-pro包及其依赖的第三方jar包;不加载urule-console-pro包及其第三方jar包。

接下来需要在上面搭建好的本地应用中配置好urule.knowledgePackageFileStorePath属性,该属性用于指定加载规则数据文件的目录,这个目录必须是一个真实存在的目录。

同时还需要配置urule.knowledgeUpdateCycle属性,其值等于1就可以,这样当前应用中调用通过api调用知识包时会首先检查内存中有没有这个知识包。

如果内存中存在这个知识包,那么就取这个知识包对应文件的时间戳与urule.knowledgePackageFileStorePath属性指定的目录中对应的知识包文件时间戳进行比较, 如果相同,说明知识包没有更新,否则就重新加载知识包文件并更新内存中知识包对象信息。

如果内存中没有这个知识包信息,那么引擎就会到urule.knowledgePackageFileStorePath属性指定的目录中加载知识包文件,然后缓存到内存当中,以备下次使用。

可以看到,在urule.knowledgeUpdateCycle属性等于1时,知识包会自动加载最新的知识包文件。

导出知识包

在规则的设计端,我们在设计、测试好规则文件并打包到知识包中后,就可以对其进行发布,操作方式是点击知识包操作菜单里的“发布新知识包”菜单项进行,如下图所示:

知识包发布完成后,我们可以通过点击知识包操作菜单里的“已发布的知识包”菜单项,查看已发布的知识包信息,如下图所示:

在这个窗口当中,就可以导出某个我们希望导出的知识包版本,我们只需要点击上图中红框内所示按钮,即可导出这个版本对应的知识包数据,导出的数据是一个.data格式的文件, 该文件是不可读的,同时文件名是由当前知识包所在项目的项目名+“#”+知识包id构成,所以这个文件名是不可以修改的。

知识包对应的.data文件导出后,就可以放在前面搭建的本地应用中urule.knowledgePackageFileStorePath属性指定的目录里,这样在这个应用中调用规则引擎api时就会 尝试到这个属性指定的目录下去查找目标.data文件,如果存在就加载,否则就会报找不到知识包的错误。

可以看到,这种本地模式的使用非常的简单、灵活,特别适合封闭环境运行规则引擎,同时知识包更新也非常的简单。

URule Pro提供的这四种模式各有优缺点,我们可以根据实际情况灵活选择运行模式。

自定义知识包存储

某些时候,如果我们希望采用这种导出的.data格式的知识包文件来运行我们的规则,但又不希望知识包存储在文件系统的目录当中,这时我们可以通过实现com.bstek.urule.runtime.service.KnowledgePackageFileService接口来自定义知识包文件的存储位置, 该接口源码如下:

package com.bstek.urule.runtime.service;

import com.bstek.urule.runtime.KnowledgePackage;

/**
 * @author Jacky.gao
 * @since 2019年12月15日
 */
public interface KnowledgePackageFileService {
    /**
     * @return 是否启用,这里直接返回true即可
     */
    boolean isEnable();
    /**
     * @param packageId 知识包ID,如:项目A/测试包A
     * @return 返回一个KnowledgePackage对象
     */
    KnowledgePackage loadKnowledgePackage(String packageId);

    /**
     * 通过与内存中缓存的知识包对象的时间戳与知识包ID对比,判断当前知识包有没有更新
     * @param packageId 知识包ID
     * @param fileModifyDate 内存中缓存的知识包对象的时间戳
     * @return 如果有更新就返回新的知识包对象,否则返回null即可
     */
    KnowledgePackage verifyKnowledgePackage(String packageId,long fileModifyDate);
}

我们需要做的就是实现这个接口,然后将实现实现类配置到Spring上下文中成为一个标准的Spring Bean即可。

实际上在大多数情况下,我们为了实现应用可以集群部署,往往希望将知识包存储在数据库当中,URule Pro的core模块中已经提供了将知识包存储于数据库中的功能。

将知识包存储于数据库中

在使用本地运行模式时,我们的应用往往需要集群部署,这时将知识包存储于文件系统肯定无法满足这一需求。为此,URule Pro提供了一个将知识包存储于数据库中的功能, 我们需要做的就是添加一个名为urule.knowledgePackageDatabaseStore.dataSource的属性,属性的值为一个在Spring当中定义好的DataSource数据源的BEAN ID, 通过这个属性来指定存储知识库时使用的数据源信息,配置好这个属性后就可以启动应用,启用过程中,系统会通过urule.knowledgePackageDatabaseStore.dataSource的属性自动检测数据库类型, 并在其中创建名为URULE_KP_STORE的数据库表(如果表不存在的话),用于存储知识包信息。

URULE_KP_STORE表的结构如下表所示:

字段名 类型 描述
ID_ 字符串 主键,存储知识包信息信息,格式为:项目名#知识ID,如:测试项目#知识包A
UPDATE_DATE_ 数字 当前行的更新时间戳,不可为空
CREATE_USER_ 字符串 当前行数据的提交人,可为空
DATA_ BLOB 存储具体的知识包内容信息,不可为空

目前支持的数据库类型有三种,分别是:MySQL、SQL Server、Oracle,后续还会根据客户反馈支持其它类型数据库。

将知识包文件保存到数据库

当我们配置了urule.knowledgePackageDatabaseStore.dataSource的属性后,就启用了从数据库中读取.data格式知识包文件存的功能, 但在引擎当中中没提供可以上传知识包文件到数据库中的功能,这是因为该功能是存在于urule-core-pro模块,所以无法添加上传知识包到数据库表中的UI页面, 如果我们需要上传知识包文件到数据库的URULE_KP_STORE表中,可以使用通过调用引擎中提供的KnowledgePackageManager实现。

下面的内容我们就以一个标准的Java Web项目为例,采用HTML+Servlet来介绍如何使用KnowledgePackageManager将知识包保存到数据库当中。

  • 首先第一步,我们创建一个用于上传知识包文件的HTML文件,内容如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>上传知识包文件</title>
</head>
<body>
<form action="saveKnowledgePackageServlet" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="file">
    <div><input type="submit" value="上传文件"></div>
</form>
</body>
</html>

这个HTML页面非常的简单,只有一个用于上传文件的Form表单,表单提交的目标页面为名为saveKnowledgePackageServlet的Servlet,接下来我们就需要编写这个Servlet,该Servlet中用于接受请求的doPost源码如下:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    KnowledgePackageManager manager=(KnowledgePackageManager)Utils.getApplicationContext().getBean(KnowledgePackageManager.BEAN_ID);
    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload fileUpload = new ServletFileUpload(factory);
    try {
        List<FileItem> fileItems = fileUpload.parseRequest(req);
        for(FileItem item:fileItems) {
            String fieldName=item.getFieldName();
            if(!fieldName.equals("file")) {
                continue;
            }
            String name=item.getName();
            int pos=name.lastIndexOf(".");
            if(pos>-1) {
                name=name.substring(0,pos);
            }
            InputStream inputStream=item.getInputStream();
            manager.saveKnowledgePackage(inputStream, name, "admin");
            inputStream.close();
        }
    } catch (Exception e) {
        throw new ServletException(e);
    }
}

在上面的代码中可以看到,将取到的文件信息,通过KnowledgePackageManager的saveKnowledgePackage方法保存到数据库,保存时CREATEUSER字段给的是一个静态的字符串,实际使用时可根据情况提供一个具体的用户名即可。 在调用saveKnowledgePackage方法时,如果指定的知识包ID在库表中已存在,那么该方法会替换存在的记录,如果不存在,则添加一条新的记录。

在KnowledgePackageManager类中,除了提供保存知识包到数据库的saveKnowledgePackage方法外,还有根据知识包ID删除知识包记录的removeKnowledgePackage方法,所以实际使用时, 可以自己做个管理页面实现对数据库中知识包信息增删改查功能。

results matching ""

    No results matching ""