13.代码中调用规则

概述

在URule Pro当中,是不能直接调用具体的规则文件的,我们需要先将定义好的规则文件放到知识包中,然后才可以对规则文件进行测试和调用。

在代码中调用知识包,需要先通过KnowledgeService接口可获取指定的知识包ID对应的构建好的资源包信息,然后通过知识包来创建具体的KnowledgeSession对象,接下来插入相关业务对象,最后执行具体的规则调用。

KnowledgeService接口源码如下:

package com.bstek.urule.runtime.service;
import java.io.IOException;
import com.bstek.urule.runtime.KnowledgePackage;
/**
 * @author Jacky.gao
 * @since 2015年1月28日
 */
public interface KnowledgeService {
    public static final String BEAN_ID="urule.knowledgeService";
    /**
     * 根据给定的资源包ID获取对应的KnowledgePackage对象
     * @param packageId 项目名称加资源包ID,格式为:projectName/packageId
     * @return 返回与给定的资源包ID获取对应的KnowledgePackage对象
     * @throws IOException
     */
    KnowledgePackage getKnowledge(String packageId) throws IOException;
    /**
     * 根据给定的一个或多个资源包ID获取对应的KnowledgePackage对象的集合
     * @param packageIds 资源包ID数组
     * @return 返回与给定的一个或多个资源包ID获取对应的KnowledgePackage对象集合
     * @throws IOException
     */
    KnowledgePackage[] getKnowledges(String[] packageIds) throws IOException;
}

可以看到,这个接口中有两个方法可供使用,一个是给一个知识包ID(格式为:projectName/packageId)返回一个对应的KnowledgePackage对象;另一个是给一个或多个知识包ID,返回一个集合类型的KnowledgePackage对象。在URule Pro当中,对于一个知识包,在使用时引擎会将其构建成KnowledgePackage对象,在这个KnowledgePackage对象中包含了所有由向决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡以及决策流等文件构建的RuleSet对象,以及由规则流构成的FlowDefinition对象。

在使用getKnowledge方法获取某个指定的package时,要给一个资源包ID,需要注意的是资源包的ID在定义要要包含资源包所在项目名称,格式为:projectName/packageId

通过KnowledgeService接口获取到KnowledgePackage对象后,接下来就可通过KnowledgePackage对象创建com.bstek.urule.runtime.KnowledgeSession对象,这个对象就是引擎提供的与业务数据交互的接口,通过这个接口,可将需要的业务数据对象插入到引擎当中,最后根据需要执行规则或规则流。

package com.bstek.urule.runtime;
import java.util.Map;
import com.bstek.urule.runtime.agenda.AgendaFilter;
/**
 * @author Jacky.gao
 * @since 2015年1月8日
 */
public interface KnowledgeSession extends WorkingMemory{
    /**
     * 执行当前WorkMemory中所有满足条件的规则
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules();
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行
     * @param filter 对满足条件的规则进行过滤
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(AgendaFilter filter);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行,并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @param filter 对满足条件的规则进行过滤
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(Map<String,Object> parameters,AgendaFilter filter);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行执行,并定义执行的最大数目,超出后就不再执行
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行执行,并定义执行的最大数目,超出后就不再执行,<br>
     * 并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(Map<String,Object> parameters,int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行,并定义执行数目的最大值
     * @param filter 对满足条件的规则进行过滤
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(AgendaFilter filter,int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行,并定义执行数目的最大值,<br>
     * 并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @param filter 对满足条件的规则进行过滤
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(Map<String,Object> parameters,AgendaFilter filter,int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行执行,并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    ExecutionResponse fireRules(Map<String,Object> parameters);
    /**
     * 根据规则流ID,执行目标规则流
     * @param processId 要执行的规则流ID
     * @return 返回一个ExecutionResponse对象,其中包含规则流执行耗时信息
     */
    ExecutionResponse startProcess(String processId);
    /**
     * 根据规则流ID,执行目标规则流,并向WorkingMemory中设置一个Map的参数对象
     * @param processId 要执行的规则流ID
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @return 返回一个ExecutionResponse对象,其中包含规则流执行耗时信息
     */
    ExecutionResponse startProcess(String processId,Map<String,Object> parameters);
}

可以看到KnowledgeSession接口扩展自WorkingMemory接口,WorkingMemory接口源码如下:

package com.bstek.urule.runtime;
import java.util.List;
import java.util.Map;

/**
 * @author Jacky.gao
 * @since 2015年1月8日
 */
public interface WorkingMemory {
    /**
     * 插入一个业务数据对象,对应到规则当中就是一个变量对象
     * @param fact 目标业务数据对象
     * @return 插入是否成功
     */
    boolean insert(Object fact);
    /**
     * 利用当前WorkingMemory中的规则信息来评估当前与业务对象,看其是否会满足相关规则的条件,同时将该对象插入到WorkingMemory
     * @param fact 要评估的对象
     */
    void assertFact(Object fact);
    /**
     * 更新一个在当前WorkingMemory中已存在的业务对象,如果对象存在,那么WorkingMemory会重新评估这个对象
     * @param fact 要更新的对象
     * @return 更新是否成功,如果对象不在WorkingMemory中,则返回false
     */
    boolean update(Object fact);
    /**
     * 移除一个在WorkingMemory中的对象,如果对象存在,那么会尝试对已满足条件的规则进行重新评估,
     * 看看当前对象的移除是否会影响已满足条件的规则,如果有影响,则同样移除已满足条件的规则
     * @param obj 要移除的对象
     * @return 是否移除成功,如果在WorkingMemory中存在,则返回true,否则返回false
     */
    boolean retract(Object obj);
    /**
     * 获取当前WorkingMemory中的某个参数值
     * @param key 参数对应的key值
     * @return 返回具体的值
     */
    Object getParameter(String key);
    /**
     * @return 返回所有的参数对象
     */
    Map<String,Object> getParameters();
    /**
     * @return 返回当前WorkingMemory中所有的业务数据对象
     */
    List<Object> getAllFacts();
    /**
     * @return 返回所有WorkingMemory中所有历史业务数据对象
     */
    List<Object> getHistoryFacts();
}

要通过KnowledgePackage对象或这个对象的数组创建一个KnowledgeSession对象,可以通过com.bstek.urule.runtime.KnowledgeSessionFactory类中下面两个静态方法实现:

/**
 * 创建一个普通的KnowledgeSession对象
 * @param knowledgePackage 创建KnowledgeSession对象所需要的KnowledgePackage对象
 * @return 返回一个新的KnowledgeSession对象
 */
public static KnowledgeSession newKnowledgeSession(KnowledgePackage knowledgePackage){
    return new KnowledgeSessionImpl(knowledgePackage);
}

/**
 * 创建一个普通的KnowledgeSession对象
 * @param knowledgePackage 创建KnowledgeSession对象所需要的KnowledgePackage集合对象
 * @return 返回一个新的KnowledgeSession对象
 */
public static KnowledgeSession newKnowledgeSession(KnowledgePackage[] knowledgePackages){
    return new KnowledgeSessionImpl(knowledgePackages);
}

单次调用

由于这两个方法都是静态方法,所以可以直接调用,下面的代码中演示了完整的调用过程:

package tt;
import rete.test.Dept;
import rete.test.Employee;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"projectName/test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        Employee employee=new Employee();
        Dept dept=new Dept();
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则
        session.fireRules();
    }
}

在上面的示例当中,获取到KnowledgeSession对象后,向其中插入一个名为Employee业务数据对象,这样引擎在计算时,会直接采用Employee中相关数据,如有条件满足,同时有对Employee中相关数据赋值,那么会直接反映到当前插入的这个Employee对象当中。

在实际使用中,可能还会向KnowledgeSession中添加参数数据(以Map形式添加),对应URule中的参数库文件中定义的信息,引擎计算完成后,我们要通KnowledgeSession中的getParameter来获取具体的参数对象,而不 能通过原添加的Map中获取,如下代码:

package tt;
import java.util.HashMap;
import java.util.Map;
import rete.test.Dept;
import rete.test.Employee;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        Employee employee=new Employee();
        Dept dept=new Dept();
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则

        Map<String,Object> parameter=new HashMap<String,Object>();
        parameter.put("count", 10);
        parameter.put("result", true);
        //触发规则时并设置参数
        session.fireRules(parameter);

        //获取计算后的result值,要通过KnowledgeSession,而不能通过原来的parameter对象
        boolean result=(Boolean)session.getParameter("result");
        System.out.println(result);
    }
}

从上面的代码中可以看到,在规则计算完成后,在获取计算后的参数中的result值时,我们并没有用提供参数的parameter,而是通过KnowledgeSession的getParameter来实现,这是因为在向KnowledgeSession设置参数时,引擎会将参数中所有的值取出并放入到引擎中内置的一个Map中,以避免影响原参数的值。所以计算完成后,我们要通过KnowledgeSession的getParameter来获取计算后的参数值。

如果我们的资源包中包含有规则流,那么在插入好相关业务数据对象后,可以通过KnowledgeSession中提供的startProcess来实现规则流的调用,如下面的代码所示:

package tt;
import java.util.HashMap;
import java.util.Map;
import rete.test.Dept;
import rete.test.Employee;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        Employee employee=new Employee();
        Dept dept=new Dept();
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则

        Map<String,Object> parameter=new HashMap<String,Object>();
        parameter.put("count", 10);
        parameter.put("result", true);

        //开始规则流并设置参数
        session.startProcess("flow-test",parameter);

        //获取计算后的result值,要通过KnowledgeSession,而不能通过原来的parameter对象
        boolean result=(Boolean)session.getParameter("result");
        System.out.println(result);
    }
}

在URule Pro当中,规则流中是不存在人工任务的,也就是说规则流的执行是一次性完成的,这点与包含人工任务的工作流引擎不同,比如UFLO,在UFLO中有人工任务,所以开启流程实例后可能需要多次完成人工任务才能完成一个流程实例。

批处理支持

实际业务当中,我们除了会做单条规则计算外,还有可能需要运行规则引擎来处理一大批数据,这些数据可能有几万条,几十万条,甚至更多。在这种情况下,如果我们还是采用普通的KnowledgeSession在一个线程里处理大批量数据的话,那么引擎还是只能在当前线程里运行,这样就会需要很长的时间才能可能将这几十万条甚至更多的数据处理完成,在这个时候,为了充分利用服务器较强的CPU性能,我们可以使用BatchSession利用多线程并行处理这些数据。顾名思义BatchSession是用来做批处理任务的会话对象,它是 URule Pro当中提供的多线程并行处理大批量业务数据的规则会话对象。

要得到一个BatchSession对象,我们也需要提供一个或多个KnowledgePackage对象,获取KnowledgePackage对象的方法与上面介绍的方法相同,有了KnowledgePackage对象后,就可以利用KnowledgeSessionFactory类创建一个BatchSession对象。

在com.bstek.urule.runtime.KnowledgeSessionFactory类中除上面提到的两个构建KnowledgeSession的静态方法外,还提供了另外八个可用于构建BatchSession的静态方法,其源码如下:

/**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,默认将每100个任务放在一个线程里处理
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage knowledgePackage){
        return new BatchSessionImpl(knowledgePackage,BatchSession.DEFAULT_THREAD_SIZE,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,默认将每100个任务放在一个线程里处理
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @param threadSize 线程池中可用的线程个数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByThreadSize(KnowledgePackage knowledgePackage,int threadSize){
        return new BatchSessionImpl(knowledgePackage,threadSize,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,第二个参数用来决定单个线程处理的任务数
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByBatchSize(KnowledgePackage knowledgePackage,int batchSize){
        return new BatchSessionImpl(knowledgePackage,BatchSession.DEFAULT_THREAD_SIZE,batchSize);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,第三个参数用来决定单个线程处理的任务数
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @param threadSize 线程池中可用的线程个数
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage knowledgePackage,int threadSize,int batchSize){
        return new BatchSessionImpl(knowledgePackage,threadSize,batchSize);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,默认将每100个任务放在一个线程里处理
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage[] knowledgePackages){
        return new BatchSessionImpl(knowledgePackages,BatchSession.DEFAULT_THREAD_SIZE,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,默认将每100个任务放在一个线程里处理
     * @param knowledgePackages 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @param threadSize 线程池中可用的线程个数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByThreadSize(KnowledgePackage[] knowledgePackages,int threadSize){
        return new BatchSessionImpl(knowledgePackages,threadSize,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,第二个参数用来决定单个线程处理的任务数
     * @param knowledgePackages 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByBatchSize(KnowledgePackage[] knowledgePackages,int batchSize){
        return new BatchSessionImpl(knowledgePackages,BatchSession.DEFAULT_THREAD_SIZE,batchSize);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,第三个参数用来决定单个线程处理的任务数
     * @param knowledgePackages 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @param threadSize 线程池中可用的线程个数
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage[] knowledgePackages,int threadSize,int batchSize){
        return new BatchSessionImpl(knowledgePackages,threadSize,batchSize);
    }

前面介绍规则流中的决策节点时,了解到决策节点中支持百分比分流,这种百分比分流就要求必须是在使用BatchSession处理一批数据的时候,或者是一个用一个普通的KnowledgeSession一次性处理多条数据才有效,否则规则流只会走比例最高的那个分支。

BatchSession接口比较简单,它只定义了两个方法:

/**
     * 添加一个具体要执行Business对象
     * @param business Business对象实例
     */
    void addBusiness(Business business);


    /**
     * 等待线程池中所有业务线程执行完成,在进行批处理操作时一定要以此方法作为方法调用结尾
     */
    void waitForCompletion();

可以看到,它可以接收若干个名为com.bstek.urule.runtime.Business接口实例,Business接口比较简单,它只有一个方法:

package com.bstek.urule.runtime;
/**
 * @author Jacky.gao
 * @since 2015年9月29日
 */
public interface Business {
    void execute(KnowledgeSession session);
}

在Business实现类中,我们的业务写在execute方法当中,在这个方法中,只有一个KnowledgeSession对象,这个session对象就是我们与规则引擎操作的对象,示例代码如下:

//从Spring中获取KnowledgeService接口实例
KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
//通过KnowledgeService接口获取指定的资源包"aaa"
KnowledgePackage knowledgePackage=service.getKnowledge("projectName/aaa");

//通过取的KnowledgePackage对象创建BatchSession对象,在这个对象中,我们将开启5个线程,每个线程最多放置10个Bussiness接口实例运行
BatchSession batchSession=KnowledgeSessionFactory.newBatchSession(knowledgePackage, 5, 10);

for(int i=0;i<100;i++){
    batchSession.addBusiness(new Business(){
        @Override
        public void execute(KnowledgeSession session) {
            Employee employee=new Employee();
            employee.setSalary(11080);
            //将业务数据对象Employee插入到KnowledgeSession中
            session.insert(employee);
            session.startProcess("demo");
        }
    });
}
//等待所有的线程执行完成,对于BatchSession调用来说,此行代码必不可少,否则将导致错误
batchSession.waitForCompletion();

results matching ""

    No results matching ""