22.知识包Restful服务暴露与调用

由  创建, 最后一次修改 2019-09-19

22.知识包Restful服务暴露与调用

简介

在前面的内容当中,我们介绍的URule Pro的两种部署模式,分别是嵌入式和分布式计算模式。这里的分布式计算模式,就是把规则的计算分布到各个客户端节点上,从而可以大大减轻传统集中式计算时对单一服务器的压力。

有些时候客户端环境可能比较复杂,如采用非Java语言编写的客户端,如Javascript、C++或C#等,或者是客户端不希望加入URule Pro的相关Jar包等等,但这些客户端也需要调用规则引擎进行业务规则的计算, 这个时候,URule Pro提供的两种部署模式就无法满足需求,所以这里就提供了第三种模式,也就是传统的独立服务模式。

从2.1.7版本开始,URule Pro中提供了统一的Restful服务调用接口,通过在知识包上进行简单的配置,即可实现将业务规则计算暴露成Restful接口,对于客户端来说, 调用接口时,只需要符合要求的JSON格式数据即可实现业务规则计算,同时Restful接口也会返回统一的JSON格式数据作为计算结果输出。

这里需要特别强调的是,如果您当前打算把URule Pro用在一个构建于SpringBoot之上的项目中,那么在配置知识包的Rest服务前需要重写SpringBoot中的HiddenHttpMethodFilter拦截器。

SpringBoot在运行时会自动加入这个HiddenHttpMethodFilter拦截器,在这个拦截器里又调用了HttpServletRequest的getParameter方法, 这就导致后续在URule Pro中用于处理Rest服务请求的Servlet通过HttpServletRequest获取到的InputStream参数值为null,从而会产生输入数据为空的异常。

要解决这一问题就需要重写这个HiddenHttpMethodFilter拦截器,具体大家可以打开百度,搜索关键词“SpringBoot中request.getInputStream()”,就可以看到大量关于这一问题的讨论和解决办法,这里就不再赘述。

如果您单独使用了SpringMVC框架,那么也会存在上面的问题,解决方法大家同样可以在百度中搜索关键词“SpringMVC中request.getInputStream()”,就可以看到对应的解决问题的办法。

配置Restful接口

打开知识包管理页面,选中某一具体的知识包项目,然后点击工具栏上的“服务调用配置”按钮,如下图所示:

可以看到,配置窗口还是比较简单的,我们需要做的就是配置好输入、输出数据以及调用时是否启用用户名密码验证即可。需要注意的是,输入输出数据必须要都配置好后才能保存,不能只配置输入数据不配置输出数据,反过来也是一样。

上图上,我们勾选了部分输入输出数据,点击确定,再点击工具栏上的“保存”按钮将配置后的知识包进行保存,接下来,我们就可以查看针对这个知识包配置的Restful服务信息了。

再次选择目标知识包,点击工具栏上的“服务调用配置”按钮,在弹出的窗口中选择下方的“查看Restful描述”按钮,就可以看到当前知识包已配置好的Restful服务接口的描述数据,描述服务的格式为JSON,内容如下所示:

{
    "output": [
        {
            "name": "贷款信息",
            "fields": [
                {
                    "name": "id",
                    "label": "ID"
                },
                {
                    "name": "money",
                    "label": "贷款额度"
                },
                {
                    "name": "result",
                    "label": "结果"
                }
            ]
        },
        {
            "name": "参数",
            "fields": [
                {
                    "name": "env",
                    "label": "环境情况"
                }
            ]
        }
    ],
    "input": [
        {
            "name": "客户",
            "fields": [
                {
                    "name": "age",
                    "label": "年龄"
                },
                ......
                {
                    "name": "salary",
                    "label": "薪水"
                }
            ]
        },
        {
            "name": "贷款信息",
            "fields": [
                {
                    "name": "result",
                    "label": "结果"
                },
                {
                    "name": "money",
                    "label": "贷款额度"
                },
                {
                    "name": "id",
                    "label": "ID"
                }
            ]
        }
    ],
    "url": "http://localhost:8081/urule-pro-test/urule/rest/我的项目/loantest",
    "authentication": false
}

为减少内容占用,上面的JSON中"......"代表隐去的部分数据。

在上面的JSON格式调用描述数据当中,“input”和“output”属性值分别表示输入和输出数据,“url”表示的是调用的URL;“authentication”属性值表示的是当前Restful服务调用是否需要用户名密码验证,这里的值是false,表示不需要验证。

需要注意的是,上述过程必须要保证是在配置完成保存后才能操作。

Restful接口调用测试

回到服务调用配置窗口,点击窗口下方的“Restful服务调用测试”按钮,就可以打开当前知识包的服务调用测试页面,如下图所示:

在测试页面当中,左边为要提交的JSON格式数据,这里引擎已将我们配置中定义好的输入数据转换成标准的JSON格式,我们只需要填充具体数据即可。

从图中可以看出,这里的JSON格式的输入数据,与我们想象中的对象的标准JSON格式略有不同。 这里,要提交的输入数据有两个,分别是“客户”和“贷款信息”,所以JSON以"[ ]"包裹,表示为一个集合类型的数据(当然,如果你配置的输入数据对象只有一个,那就直接提交一个对象就行);这里的“客户”和“贷款信息”两个对象,都用name属性来标明对象类型, 这里的name属性用的是我们规则变量库里定义的对象分类名,实际上,name属性值也可以是变量库里对象类路径全名,无论用哪个引擎都可以正确识别;接下来就是“fields”属性,它是一个对象类型, 里面具体的属性用于标明当前对象需要的属性名及属性值,所有的属性值都是一个空的字符串,实际填写时需要根据对应属性数据类型进行具体值的填充。可以看到,这里对象属性名采用的是变量库里具体属性的“字段名”,而非“标题”, 实际使用时即可以使用“字段名”,也可以使用“标题”,引擎都能正确识别。

了解输入的JSON数据格式后,接下来就可以填充JSON数据,填充完成后,点击工具栏上的“提交数据”按钮,即可将输入的数据提交到目标知识包所暴露的Restful服务接口。

在上图的输入数据当中,“客户”对象有个名为“cards”属性,这个属性是一个List类型,如果我们不为其填充集合类型的数据,采用默认的空字符串,那么提交后会产生如下图所示的JSON解析错误:

所以我们要么为“cards”属性填充一个具体的集合类型的值,要么给一个空的集合,这里我们给的是一个空的集合,提交数据后结果如下图所示:

可以看到,计算后的输出数据是一个标准的JSON对象格式,“duration”属性值表示当前业务规则计算耗时,单位为毫秒(ms),这里是0,表示时间非常的短(通常第一次计算时间较慢,这由Java特性导致);“output”属性值为一个集合类型, 里面有两个对象,是我们在服务调用配置中定义的两个输出数据对象,分别是“贷款信息”和“参数”,“贷款信息”对象是一个我们定义在变量库里的业务对象,而“参数”则是我们定义在参数库里的固定对象。在“贷款信息”这个对象里,通过“name”属性值来 标明对象名称,与变量库里定义的对象分类名保持一致;“class”属性则标明对象类全名,与变量库里定义的对象类路径一致;“fields”属性值是一个对象类型,用于标明当前对象的具体属性名及其值,这里的属性名采用的是属性的字段名, 主要是方便后续JSON数据对象化处理。

上面的测试是通过URule内置的Restful服务测试页面完成,实际使用时也可以用第三方测试工具实现,比如上面的Restful服务就可以通过postman来进行测试,如下图所示:

如截图所示,在postman中,输入好请求的URL,我们这里是“http://localhost:8081/urule-pro-test/urule/rest/我的项目/loantest”,提交类型改为POST(URule Pro提供的Restful服务只接收POST类型的请求, 所以这个URL无法直接用浏览器查看),输入要提交的数据,我们这里是要提交的JSON数据,数据格式与上面介绍的保持一致,点击“Send”按钮,就可以得到响应结果。

在配置Restful服务时,还可以打开“用户名密码验证”选项,打开该选项后,我们需要输入用户名及密码信息,保存后再次请求这个Restful服务我们就需要在请求的Header里加上用户和密码信息,否则请求将不被允许,在URule Pro内置的Restful服务测试页面里, 如果当前Restful服务需要用户名密码验证,它会自动加上用户名密码信息;而如果我们使用postman来请求这个Restful服务,若不在Header里提供用户名密码信息,那么请求将会得到如下图所示信息:

我们可以在请求的Header中添加用户名密码信息,Header的Key分别是Username和Password,如下图所示:

实际应用当中,我们会在应用在外层加上业务系统的安全管理功能,比如使用系统需要先进行登录等,这时要保证URule Pro中内置的Restful服务可用,那么就需要让/urule/rest这个URL可匿名访问,这点非常重要。

在使用这个内置的Restful服务过程中,如果出现错误,比如用户名密码不正确或规则计算过程出现异常等,类似这些错误信息也会以一个标准的JSON格式返回,所有的错误消息(如果是异常则是异常的堆栈信息)会放在返回的JSON对象的error属性中, 如果没有错误,则返回的JSON中就不会包含error属性,这点从上面的示例中我们也已经看到。

复杂对象类型的支持

在上面的示例当中,输入数据“客户”对象里有个名为“cards”属性,这个属性是一个集合类型的属性,所以如果该属性为空时我们需要给该属性添加一个[]字符,表示一个空的集合属性,如下所示:

[
    {
        "name": "客户",
        "fields": {
            "cards": [],
            "company.id": "",
            "gender": "",
            "company.level": "",
            "degree": "",
            "name": "",
            "company.name": "",
            "salary": "",
            "married": "",
            "age": ""
        }
    },
    {
        "name": "贷款信息",
        "fields": {
            "result": "",
            "money": "",
            "id": ""
        }
    }
]

如果我们需要填充这个集合属性,那么需要先看看变量库里定义的“卡”对象以及与卡对象相关的对象的结构,然后仿照上面的JSON写出来就好;还有种简单的方式,那就是在“服务配置”窗口中直接勾选“卡”对象以及与卡对象相关的对象,如下图所示:

保存后再次进入页面,就可以看到如下图所示的输入数据结构:

接下来,我们需要填充“cards”属性,填充好的结构如下图所示:

可以看到,填充后的“cards”属性是一个集合类型,里面由若干个“卡”对象构成,每个卡对象有四个属性,其中“cardDetails”也是一个集合对象,这个集合对象是由若干个“卡明细”对象组成。按照这样的规则, 我们在构建输入数据时就可以把输入数据按照业务的需要构建的足够复杂,可最大限度满足业务需求。

在上面的截图中,“客户”对象还有“company.id”以及“company.level”两个属性,从命名以及变量库里对这两个属性的声名可以看出,“客户”这个对象下还有一个名为company的子对象,“company.id”和“company.level”两个属性 是用来填充company子对象的id以及level属性,这里我们直接输入这两个属性值即可实现对company子对象的id以及level两个属性值的填充。

实际使用时,如果company对象在变量库中也有定义,那么上面的写法可直接改为下面的样子,依次类推:

    {
        "name": "客户",
        "fields": {
            "cards": [],
            "company":{
                "name":"公司",
                "fields":{
                    "id":"bstek",
                    "level":12
                }
            },
            "degree": "",
            "name": "",
            "company.name": "bstek",
            "salary": "",
            "married": "",
            "age": ""
        }
    }

上面的写法中,要求我们在变量库里必须定义好名为“公司”的对象,否则解析的时候产生错误,如果没有定义,那么需要采用“company.id”、“company.level”的定义方式。

集群部署

通过Restful服务对外提供规则引擎计算服务,很明显这是一种典型的集中式计算模式,如果请求计算量过大,性能容易出现问题,这时就需要我们来配置集群来解决这种问题。

URule Pro中提供的Restful服务默认就支持集群,在引擎内部,为了不频繁访问知识包中的配置的Restful服务信息,所以会把知识包的配置信息缓存下来,如果我们没有采用集群,那么默认不需要做任何修改,如果采用集群,那么需要在属性文件里 定义“urule.resourcePackageCheckCycle”属性值,这些属性值的含义是每隔多久去检查一次知识包中配置的服务信息有没有变化(单位是秒),它的默认值为0,表示不检查,每次修改知识包时会自动刷新这个缓存,所以在没有采用集群时不用做任何修改;但采用集群后, 因为有多个集群节点,所以知识包在修改保存时只能刷新一个节点的知识包缓存信息。通过修改“urule.resourcePackageCheckCycle”属性值,可以决定在Restful服务调用时每隔多久检查一次知识包配置有没有变化,如果有则刷新。

比如,定义“urule.resourcePackageCheckCycle”属性值为100,那么表示在Restful服务被调用时将每隔100秒检查一次知识包有没有更新,如果有则加载到缓存。

以上内容是否对您有帮助:
二维码
建议反馈
二维码