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