22.知識包Restful服務暴露與調用
簡介
在前面的內(nèi)容當中,我們介紹的URule Pro的兩種部署模式,分别是嵌入式和(hé)分布式計(jì)算(suàn)模式。這裏的分布式計(jì)算(suàn)模式,就是把規則的計(jì)算(suàn)分布到各個(gè)客戶端節點上(shàng),從而可(kě)以大(dà)大(dà)減輕傳統集中式計(jì)算(suàn)時(shí)對單一服務器(qì)的壓力。
有(yǒu)些(xiē)時(shí)候客戶端環境可(kě)能比較複雜,如采用非Java語言編寫的客戶端,如Javascript、C++或C#等,或者是客戶端不希望加入URule Pro的相關Jar包等等,但(dàn)這些(xiē)客戶端也需要調用規則引擎進行(xíng)業務規則的計(jì)算(suàn), 這個(gè)時(shí)候,URule Pro提供的兩種部署模式就無法滿足需求,所以這裏就提供了第三種模式,也就是傳統的獨立服務模式。
在URule Pro中提供了統一的Restful服務調用接口,通(tōng)過在知識包上(shàng)進行(xíng)簡單的配置,即可(kě)實現将業務規則計(jì)算(suàn)暴露成Restful接口,對于客戶端來(lái)說, 調用接口時(shí),隻需要符合要求的JSON格式數(shù)據即可(kě)實現業務規則計(jì)算(suàn),同時(shí)Restful接口也會(huì)返回統一的JSON格式數(shù)據作(zuò)為(wèi)計(jì)算(suàn)結果輸出。
這裏需要特别強調的是,如果您當前打算(suàn)把URule Pro用在一個(gè)構建于SpringBoot之上(shàng)的項目中,那(nà)麽在配置知識包的Rest服務前需要重寫SpringBoot中的HiddenHttpMethodFilter攔截器(qì)。
SpringBoot在運行(xíng)時(shí)會(huì)自動加入這個(gè)HiddenHttpMethodFilter攔截器(qì),在這個(gè)攔截器(qì)裏又調用了HttpServletRequest的getParameter方法, 這就導緻後續在URule Pro中用于處理(lǐ)Rest服務請(qǐng)求的Servlet通(tōng)過HttpServletRequest獲取到的InputStream參數(shù)值為(wèi)null,從而會(huì)産生(shēng)輸入數(shù)據為(wèi)空(kōng)的異常。
要解決這一問題就需要重寫這個(gè)HiddenHttpMethodFilter攔截器(qì),具體(tǐ)大(dà)家(jiā)可(kě)以打開(kāi)百度,搜索關鍵詞“SpringBoot中request.getInputStream()”,就可(kě)以看到大(dà)量關于這一問題的討(tǎo)論和(hé)解決辦法,這裏就不再贅述。
如果您單獨使用了SpringMVC框架,那(nà)麽也會(huì)存在上(shàng)面的問題,解決方法大(dà)家(jiā)同樣可(kě)以在百度中搜索關鍵詞“SpringMVC中request.getInputStream()”,就可(kě)以看到對應的解決問題的辦法。
配置Restful接口
打開(kāi)知識包管理(lǐ)頁面,選中某一具體(tǐ)的知識包項目(知識包狀态處于啓用時(shí)可(kě)操作(zuò),停用狀态的知識無法配置),點擊目标知識包操作(zuò)菜單中的“服務調用配置”菜單項,即可(kě)彈出配置窗口,如下圖所示:
可(kě)以看到,配置窗口還(hái)是比較簡單的,我們需要做(zuò)的就是配置好輸入、輸出數(shù)據以及調用時(shí)是否啓用用戶名密碼驗證即可(kě)。需要注意的是,輸入輸出數(shù)據必須要都配置好後才能保存,不能隻配置輸入數(shù)據不配置輸出數(shù)據,反過來(lái)也是一樣。
上(shàng)圖上(shàng),我們勾選了部分輸入輸出數(shù)據,點擊确定,再點擊工具欄上(shàng)的“保存”按鈕将配置後的知識包進行(xíng)保存,接下來(lái),我們就可(kě)以查看針對這個(gè)知識包配置的Restful服務信息了。
輸入、輸出信息配置完成,點擊保存,點擊配置窗口下方的的“查看Restful描述”按鈕,就可(kě)以看到當前知識包已配置好的Restful服務接口的描述數(shù)據,描述服務的格式為(wèi)JSON,內(nèi)容如下所示:
{
"output": [
{
"name": "貸款信息",
"fields": [
{
"name": "id",
"label": "ID"
},
{
"name": "money",
"label": "貸款額度"
},
{
"name": "result",
"label": "結果"
}
]
},
{
"name": "參數(shù)",
"fields": [
{
"name": "env",
"label": "環境情況"
}
]
}
],
"input": [
{
"name": "客戶",
"fields": [
{
"name": "age",
"label": "年齡"
},
......
{
"name": "salary",
"label": "薪水(shuǐ)"
}
]
},
{
"name": "貸款信息",
"fields": [
{
"name": "result",
"label": "結果"
},
{
"name": "money",
"label": "貸款額度"
},
{
"name": "id",
"label": "ID"
}
]
}
],
"url": "http://localhost:8081/urule-pro-test/urule/rest/我的項目/loantest",
"authentication": false
}
為(wèi)減少(shǎo)內(nèi)容占用,上(shàng)面的JSON中"......"代表隐去的部分數(shù)據。
在上(shàng)面的JSON格式調用描述數(shù)據當中,“input”和(hé)“output”屬性值分别表示輸入和(hé)輸出數(shù)據,“url”表示的是調用的URL;“authentication”屬性值表示的是當前Restful服務調用是否需要用戶名密碼驗證,這裏的值是false,表示不需要驗證。
需要注意的是,上(shàng)述過程必須要保證是在配置完成保存後才能操作(zuò)。
Restful接口調用測試
回到服務調用配置窗口,點擊窗口下方的“Restful服務調用測試”按鈕,就可(kě)以打開(kāi)當前知識包的服務調用測試頁面,如下圖所示:
在測試頁面當中,左邊為(wèi)要提交的JSON格式數(shù)據,這裏引擎已将我們配置中定義好的輸入數(shù)據轉換成标準的JSON格式,我們隻需要填充具體(tǐ)數(shù)據即可(kě)。
從圖中可(kě)以看出,這裏的JSON格式的輸入數(shù)據,與我們想象中的對象的标準JSON格式略有(yǒu)不同。 這裏,要提交的輸入數(shù)據有(yǒu)兩個(gè),分别是“客戶”和(hé)“貸款信息”,所以JSON以"[ ]"包裹,表示為(wèi)一個(gè)集合類型的數(shù)據(當然,如果你(nǐ)配置的輸入數(shù)據對象隻有(yǒu)一個(gè),那(nà)就直接提交一個(gè)對象就行(xíng));這裏的“客戶”和(hé)“貸款信息”兩個(gè)對象,都用name屬性來(lái)标明(míng)對象類型, 這裏的name屬性用的是我們規則變量庫裏定義的對象分類名,實際上(shàng),name屬性值也可(kě)以是變量庫裏對象類路徑全名,無論用哪個(gè)引擎都可(kě)以正确識别;接下來(lái)就是“fields”屬性,它是一個(gè)對象類型, 裏面具體(tǐ)的屬性用于标明(míng)當前對象需要的屬性名及屬性值,所有(yǒu)的屬性值都是一個(gè)空(kōng)的字符串,實際填寫時(shí)需要根據對應屬性數(shù)據類型進行(xíng)具體(tǐ)值的填充。可(kě)以看到,這裏對象屬性名采用的是變量庫裏具體(tǐ)屬性的“字段名”,而非“标題”, 實際使用時(shí)即可(kě)以使用“字段名”,也可(kě)以使用“标題”,引擎都能正确識别。
了解輸入的JSON數(shù)據格式後,接下來(lái)就可(kě)以填充JSON數(shù)據,填充完成後,點擊工具欄上(shàng)的“提交數(shù)據”按鈕,即可(kě)将輸入的數(shù)據提交到目标知識包所暴露的Restful服務接口。
在上(shàng)圖的輸入數(shù)據當中,“客戶”對象有(yǒu)個(gè)名為(wèi)“cards”屬性,這個(gè)屬性是一個(gè)List類型,如果我們不為(wèi)其填充集合類型的數(shù)據,采用默認的空(kōng)字符串,那(nà)麽提交後會(huì)産生(shēng)如下圖所示的JSON解析錯誤:
所以我們要麽為(wèi)“cards”屬性填充一個(gè)具體(tǐ)的集合類型的值,要麽給一個(gè)空(kōng)的集合,這裏我們給的是一個(gè)空(kōng)的集合,提交數(shù)據後結果如下圖所示:
可(kě)以看到,計(jì)算(suàn)後的輸出數(shù)據是一個(gè)标準的JSON對象格式,“duration”屬性值表示當前業務規則計(jì)算(suàn)耗時(shí),單位為(wèi)毫秒(miǎo)(ms),這裏是0,表示時(shí)間(jiān)非常的短(duǎn)(通(tōng)常第一次計(jì)算(suàn)時(shí)間(jiān)較慢,這由Java特性導緻);“output”屬性值為(wèi)一個(gè)集合類型, 裏面有(yǒu)兩個(gè)對象,是我們在服務調用配置中定義的兩個(gè)輸出數(shù)據對象,分别是“貸款信息”和(hé)“參數(shù)”,“貸款信息”對象是一個(gè)我們定義在變量庫裏的業務對象,而“參數(shù)”則是我們定義在參數(shù)庫裏的固定對象。在“貸款信息”這個(gè)對象裏,通(tōng)過“name”屬性值來(lái) 标明(míng)對象名稱,與變量庫裏定義的對象分類名保持一緻;“class”屬性則标明(míng)對象類全名,與變量庫裏定義的對象類路徑一緻;“fields”屬性值是一個(gè)對象類型,用于标明(míng)當前對象的具體(tǐ)屬性名及其值,這裏的屬性名采用的是屬性的字段名, 主要是方便後續JSON數(shù)據對象化處理(lǐ)。
上(shàng)面的測試是通(tōng)過URule內(nèi)置的Restful服務測試頁面完成,實際使用時(shí)也可(kě)以用第三方測試工具實現,比如上(shàng)面的Restful服務就可(kě)以通(tōng)過postman來(lái)進行(xíng)測試,如下圖所示:
如截圖所示,在postman中,輸入好請(qǐng)求的URL,我們這裏是“http://localhost:8081/urule-pro-test/urule/rest/我的項目/loantest”,提交類型改為(wèi)POST(URule Pro提供的Restful服務隻接收POST類型的請(qǐng)求, 所以這個(gè)URL無法直接用浏覽器(qì)查看),輸入要提交的數(shù)據,我們這裏是要提交的JSON數(shù)據,數(shù)據格式與上(shàng)面介紹的保持一緻,點擊“Send”按鈕,就可(kě)以得(de)到響應結果。
在配置Restful服務時(shí),還(hái)可(kě)以打開(kāi)“用戶名密碼驗證”選項,打開(kāi)該選項後,我們需要輸入用戶名及密碼信息,保存後再次請(qǐng)求這個(gè)Restful服務我們就需要在請(qǐng)求的Header裏加上(shàng)用戶和(hé)密碼信息,否則請(qǐng)求将不被允許,在URule Pro內(nèi)置的Restful服務測試頁面裏, 如果當前Restful服務需要用戶名密碼驗證,它會(huì)自動加上(shàng)用戶名密碼信息;而如果我們使用postman來(lái)請(qǐng)求這個(gè)Restful服務,若不在Header裏提供用戶名密碼信息,那(nà)麽請(qǐng)求将會(huì)得(de)到如下圖所示信息:
我們可(kě)以在請(qǐng)求的Header中添加用戶名密碼信息,Header的Key分别是Username和(hé)Password,如下圖所示:
實際應用當中,我們會(huì)在應用在外層加上(shàng)業務系統的安全管理(lǐ)功能,比如使用系統需要先進行(xíng)登錄等,這時(shí)要保證URule Pro中內(nèi)置的Restful服務可(kě)用,那(nà)麽就需要讓/urule/rest這個(gè)URL可(kě)匿名訪問,這點非常重要。
在使用這個(gè)內(nèi)置的Restful服務過程中,如果出現錯誤,比如用戶名密碼不正确或規則計(jì)算(suàn)過程出現異常等,類似這些(xiē)錯誤信息也會(huì)以一個(gè)标準的JSON格式返回,所有(yǒu)的錯誤消息(如果是異常則是異常的堆棧信息)會(huì)放在返回的JSON對象的error屬性中, 如果沒有(yǒu)錯誤,則返回的JSON中就不會(huì)包含error屬性,這點從上(shàng)面的示例中我們也已經看到。
複雜對象類型的支持
在上(shàng)面的示例當中,輸入數(shù)據“客戶”對象裏有(yǒu)個(gè)名為(wèi)“cards”屬性,這個(gè)屬性是一個(gè)集合類型的屬性,所以如果該屬性為(wèi)空(kōng)時(shí)我們需要給該屬性添加一個(gè)[]字符,表示一個(gè)空(kōng)的集合屬性,如下所示:
[
{
"name": "客戶",
"fields": {
"cards": [],
"company.id": "",
"gender": "",
"company.level": "",
"degree": "",
"name": "",
"company.name": "",
"salary": "",
"married": "",
"age": ""
}
},
{
"name": "貸款信息",
"fields": {
"result": "",
"money": "",
"id": ""
}
}
]
如果我們需要填充這個(gè)集合屬性,那(nà)麽需要先看看變量庫裏定義的“卡”對象以及與卡對象相關的對象的結構,然後仿照上(shàng)面的JSON寫出來(lái)就好;還(hái)有(yǒu)種簡單的方式,那(nà)就是在“服務配置”窗口中直接勾選“卡”對象以及與卡對象相關的對象,如下圖所示:
保存後再次進入頁面,就可(kě)以看到如下圖所示的輸入數(shù)據結構:
接下來(lái),我們需要填充“cards”屬性,填充好的結構如下圖所示:
可(kě)以看到,填充後的“cards”屬性是一個(gè)集合類型,裏面由若幹個(gè)“卡”對象構成,每個(gè)卡對象有(yǒu)四個(gè)屬性,其中“cardDetails”也是一個(gè)集合對象,這個(gè)集合對象是由若幹個(gè)“卡明(míng)細”對象組成。按照這樣的規則, 我們在構建輸入數(shù)據時(shí)就可(kě)以把輸入數(shù)據按照業務的需要構建的足夠複雜,可(kě)最大(dà)限度滿足業務需求。
在上(shàng)面的截圖中,“客戶”對象還(hái)有(yǒu)“company.id”以及“company.level”兩個(gè)屬性,從命名以及變量庫裏對這兩個(gè)屬性的聲名可(kě)以看出,“客戶”這個(gè)對象下還(hái)有(yǒu)一個(gè)名為(wèi)company的子對象,“company.id”和(hé)“company.level”兩個(gè)屬性 是用來(lái)填充company子對象的id以及level屬性,這裏我們直接輸入這兩個(gè)屬性值即可(kě)實現對company子對象的id以及level兩個(gè)屬性值的填充。
實際使用時(shí),如果company對象在變量庫中也有(yǒu)定義,那(nà)麽上(shàng)面的寫法可(kě)直接改為(wèi)下面的樣子,依次類推:
{
"name": "客戶",
"fields": {
"cards": [],
"company":{
"name":"公司",
"fields":{
"id":"bstek",
"level":12
}
},
"degree": "",
"name": "",
"company.name": "bstek",
"salary": "",
"married": "",
"age": ""
}
}
上(shàng)面的寫法中,要求我們在變量庫裏必須定義好名為(wèi)“公司”的對象,否則解析的時(shí)候産生(shēng)錯誤,如果沒有(yǒu)定義,那(nà)麽需要采用“company.id”、“company.level”的定義方式。
部署
通(tōng)過Restful服務對外提供規則引擎計(jì)算(suàn)服務,在生(shēng)産環境下,我們還(hái)需要配置兩個(gè)參數(shù),分别是urule.debug=false,也就是關掉日志(zhì)輸出服務(當然這屬于不需要日志(zhì)記錄的情況);另一個(gè)參數(shù)就是urule.resourcePackageCheckCycle=1,這個(gè)屬性的默認值是0, 表示每次都會(huì)重新編譯知識,這對于開(kāi)發環境來(lái)說很(hěn)重要,但(dàn)在生(shēng)産環境下就會(huì)導緻問題,設置為(wèi)1,就可(kě)以避免這種問題。
如果我們客戶端的請(qǐng)求數(shù)量較大(dà),那(nà)麽還(hái)需要采用集群方式部署,Restful服務集群部署比較簡單,除了要配置好上(shàng)述兩個(gè)屬性外,還(hái)需要将知識包改為(wèi)數(shù)據庫存儲(數(shù)據庫存儲可(kě)參考14小(xiǎo)節介紹),每個(gè)節點都訪問同一個(gè)目标數(shù)據庫即可(kě)。
默認情況下下,每次進行(xíng)知識包調用時(shí),默認會(huì)檢查數(shù)據庫中存儲的知識包與當前緩存存儲的知識時(shí)間(jiān)戳是否一緻,以确定知識包是否有(yǒu)更新。這一動作(zuò)在某些(xiē)并發比較高(gāo)的場(chǎng)景下也可(kě)能會(huì)影(yǐng)響性能,為(wèi)避免這一操作(zuò),我們還(hái)可(kě)以将屬性urule.knowledgeSyncCheck的值設置為(wèi) false,這樣調用知識包是引擎就直接使用緩存裏的知識包,而不再檢查當前知識包是否有(yǒu)更新。如果我們設置了urule.knowledgeSyncCheck屬性為(wèi)false,同時(shí)還(hái)希望在知識包更新後還(hái)能更新緩存裏的知識包,實現一個(gè)節點修改了知識包內(nèi)容,其它集群節點也能更及時(shí)緩存裏的知識包, 那(nà)就需要做(zuò)一些(xiē)配置,方法就是點擊工具欄上(shàng)的“集群服務器(qì)節點配置”,如下圖:
在這個(gè)配置頁面中配置好各個(gè)集群節點的具體(tǐ)URL即可(kě),如下圖:
配置好這個(gè)集群服務器(qì)具體(tǐ)的URL後,當我們在任意一個(gè)節點上(shàng)發布知識包,引擎會(huì)自動通(tōng)知其它節點加載新的知識包,從而解決各個(gè)節點知識緩存同步問題。