轉(zhuǎn)轉(zhuǎn)圖書(shū)對(duì)基于Drools引擎的DMN實(shí)踐
1 背景介紹
1.1 DMN是什么
DMN全稱Decision Model and Notation(決策模型和符號(hào)、決策模型和表示法),是一種用于表示業(yè)務(wù)決策和規(guī)則的規(guī)范,旨在幫助參與決策的人都能簡(jiǎn)單快速理解決策過(guò)程。
DMN logo
DMN是由OMG(Object Management Group,對(duì)象管理組織)管理的一種規(guī)范,該組織下比較知名的還有UML等。
DMN示例
如圖所示,DMN的表現(xiàn)形式近似于流程圖,通過(guò)可視化來(lái)更直觀地體現(xiàn)出流轉(zhuǎn)和處理的過(guò)程,而各個(gè)節(jié)點(diǎn)使用附帶邏輯的表格來(lái)表現(xiàn)該節(jié)點(diǎn)處理數(shù)據(jù)的方式。
由于DMN是一個(gè)規(guī)范,所以在應(yīng)用上,主要依靠各個(gè)DMN工具供應(yīng)商提供具體實(shí)現(xiàn),近似于SQL語(yǔ)句和MySQL、Oracle的關(guān)系。
1.2 為什么要用DMN
應(yīng)用DMN主要目的是為了解決轉(zhuǎn)轉(zhuǎn)圖書(shū)項(xiàng)目定價(jià)邏輯過(guò)于復(fù)雜的問(wèn)題。
在引入DMN之前,圖書(shū)項(xiàng)目使用java代碼實(shí)現(xiàn)產(chǎn)品人員提供的定價(jià)邏輯。但是隨著邏輯越來(lái)越復(fù)雜,定價(jià)邏輯的代碼也越來(lái)越難以閱讀和維護(hù)。同時(shí),將產(chǎn)品人員的邏輯翻譯成代碼是一個(gè)單向過(guò)程,只有程序員能理解實(shí)現(xiàn)過(guò)程,產(chǎn)品人員只能通過(guò)結(jié)果反推是否正確合理,在邏輯變得復(fù)雜之后,很難快速發(fā)現(xiàn)問(wèn)題。
復(fù)雜規(guī)則的一部分,區(qū)間和系數(shù)
而通過(guò)DMN,可以可視化的表示出邏輯過(guò)程,產(chǎn)品人員也可以直觀的看到是否符合預(yù)期,還能夠進(jìn)行編輯,從而解決了只有程序員掌握實(shí)現(xiàn)而過(guò)于依賴程序員的問(wèn)題,也能減少表述交流的過(guò)程產(chǎn)生理解偏差進(jìn)而導(dǎo)致的錯(cuò)誤。
使用DMN實(shí)現(xiàn)的規(guī)則表現(xiàn)
1.3 什么是Drools
Drools是用java語(yǔ)言編寫(xiě)的開(kāi)源規(guī)則引擎,是DMN規(guī)范的一種實(shí)現(xiàn)。舉例來(lái)說(shuō)DMN規(guī)范就是接口,Drools就是實(shí)現(xiàn)了接口的其中一個(gè)工具類。
Drools logo
Drools屬于Kie開(kāi)源社區(qū),而kie社區(qū)由Red Hat贊助,有比較高的社區(qū)活躍度。
Kie旗下的產(chǎn)品
1.4 為什么選擇Drools
Drools引擎是基于java實(shí)現(xiàn)的,這使得不需要為Drools單獨(dú)部署運(yùn)行環(huán)境,運(yùn)維成本是0,十分友好。
其次,DMN規(guī)范規(guī)定實(shí)現(xiàn)其規(guī)范的軟件必須滿足三級(jí)遞增的符合性級(jí)別,三級(jí)最高,一級(jí)最低,滿足三級(jí)級(jí)別時(shí)必須同時(shí)滿足一級(jí)和二級(jí)級(jí)別。而Drools引擎對(duì)DMN規(guī)范的支持屬于三級(jí)級(jí)別,功能完善。
Drools引擎對(duì)DMN各個(gè)版本提供支持
同時(shí),Drools引擎社區(qū)活躍度高,同時(shí)提供了完整的工作組件,可靠性高。
2 Drools引擎應(yīng)用
以下內(nèi)容基于7.61.0版本,與8.x及以上的版本存在差異
2.1 官方推薦的最直接的應(yīng)用方式
首先需要部署運(yùn)行一個(gè)Kie Server用來(lái)執(zhí)行DMN規(guī)則,然后部署Drools官方提供的基于Web的工作站軟件用來(lái)編輯、測(cè)試、發(fā)布DMN規(guī)則。
Drools提供的工作站軟件
當(dāng)部署完成DMN規(guī)則之后,可以通過(guò)Kie Server REST API來(lái)運(yùn)行規(guī)則獲取結(jié)果,即通過(guò)Http的方式請(qǐng)求Kie Server提供的接口。
2.2 轉(zhuǎn)轉(zhuǎn)圖書(shū)的限制
首先說(shuō)圖書(shū)項(xiàng)目并沒(méi)有直接按照官方推薦的做法部署應(yīng)用,原因有幾個(gè)方面:
第一點(diǎn),轉(zhuǎn)轉(zhuǎn)內(nèi)部項(xiàng)目部署運(yùn)行有自有的框架體系,而Kie Server是基于JBoss運(yùn)行的服務(wù)器軟件,在現(xiàn)有體系中部署一個(gè)外部項(xiàng)目需要額外的運(yùn)維成本。
其次,Kie Server作為一個(gè)第三方工具,當(dāng)出現(xiàn)問(wèn)題時(shí)僅靠圖書(shū)項(xiàng)目的人員難以解決,而等待社區(qū)反饋對(duì)于轉(zhuǎn)轉(zhuǎn)圖書(shū)這種線上的商業(yè)項(xiàng)目難以接收。所以為了盡可能的減少出現(xiàn)問(wèn)題的概率,節(jié)約人力,圖書(shū)項(xiàng)目?jī)A向于盡可能少的引入外部依賴。
基于以上原因,圖書(shū)項(xiàng)目在應(yīng)用Drools的時(shí)候選擇了另外一種方式。
3 脫離Kie Server的Drools引擎實(shí)踐
脫離開(kāi)Kie Server,自然就沒(méi)有了REST API的http接口,同時(shí)也失去了官方工作站的支持,但是相對(duì)的,Drools提供了一些對(duì)這種場(chǎng)景下的支持。
3.1 在線編輯DMN規(guī)則
在Kie旗下有另一款名為Kogito的產(chǎn)品,是一個(gè)提供在線編輯BPMN和DMN的服務(wù)器軟件,同時(shí)其中有一個(gè)all-in-one的js文件,實(shí)現(xiàn)了在線編輯DMN的全部功能。
js提供的接口
圖書(shū)項(xiàng)目基于這個(gè)js文件進(jìn)行了包裝,增加易用性,使得DMN編輯功能融入到現(xiàn)有的工作后臺(tái)之中。
包裝頁(yè)面,下方是js提供的功能區(qū)
同時(shí)增加編輯記錄列表用來(lái)便于管理和回滾。
編輯列表
js工具編輯后的DMN規(guī)則內(nèi)容是xml格式的字符串,可以使用js提供的getContent()接口導(dǎo)出。
3.2 使用Drools引擎執(zhí)行DMN規(guī)則
沒(méi)有了Kie Server的支持之后,需要通過(guò)代碼的方式運(yùn)行Drools引擎。
首先在項(xiàng)目中引入Drools引擎的組件
引入Drools引擎組件
然后在項(xiàng)目中創(chuàng)建Drools引擎的引用并執(zhí)行。
由于Kie容器使用maven作為讀取DMN配置的手段,所以要求DMN規(guī)則內(nèi)容需要打包到Kie容器能夠識(shí)別的jar包里,并且部署到項(xiàng)目可訪問(wèn)的maven環(huán)境中。
3.3 完善處理流程
按照上述流程就可以實(shí)現(xiàn)脫離Kie server運(yùn)行Drools引擎。但是距離落地上線還有一小點(diǎn)距離。
官方的工作站可以提供DMN規(guī)則上線前的驗(yàn)證和測(cè)試功能,但是all-in-one的js文件沒(méi)有,所以為了保證準(zhǔn)確性和穩(wěn)定性,需要額外實(shí)現(xiàn)DMN規(guī)則的驗(yàn)證動(dòng)作。
這里可以使用KieContainer提供的verify接口來(lái)觸發(fā)Drools引擎的驗(yàn)證動(dòng)作并獲取結(jié)果。
將驗(yàn)證流程放到保存DMN規(guī)則的時(shí)候,就可以快速發(fā)現(xiàn)是否存在編寫(xiě)錯(cuò)誤。
3.4 DLC - 脫離maven環(huán)境運(yùn)行Drools引擎
前面說(shuō)了Drools引擎需要使用maven獲取運(yùn)行的DMNjar文件,但是在實(shí)際應(yīng)用中,線上環(huán)境不一定會(huì)部署maven。例如圖書(shū)項(xiàng)目需要在公司內(nèi)部公用的spark集群上運(yùn)行Drools,但是spark集群上沒(méi)有部署maven環(huán)境,按照上述流程運(yùn)行就會(huì)報(bào)錯(cuò)。
所以需要一個(gè)能夠使Drools脫離maven環(huán)境的手段。
通過(guò)分析源碼可以發(fā)現(xiàn),在創(chuàng)建KieContainer的時(shí)候,會(huì)使用KieServices.getRepository()方法獲取數(shù)據(jù)源,并通過(guò)maven坐標(biāo)查找其中的KieModule。所以需要做的就是直接我們需要的jar寫(xiě)入KieServices的數(shù)據(jù)源中:
如上就可以實(shí)現(xiàn)在沒(méi)有maven環(huán)境的時(shí)候仍然能夠讓KieServices獲取到我們需要DMN規(guī)則。
但是僅僅如此還不夠,在創(chuàng)建KieContainer的時(shí)候,內(nèi)部會(huì)生成一個(gè)KieProject實(shí)例,KieProject在實(shí)例化的過(guò)程中會(huì)默認(rèn)生成一個(gè)基于maven的MavenClassLoaderResolver用于查找jar,而缺少maven環(huán)境的情況下在生成MavenClassLoaderResolver的時(shí)候也會(huì)報(bào)錯(cuò)。為此,我們需要一個(gè)取代MavenClassLoaderResolver的辦法。
通過(guò)分析Drools源碼可以發(fā)現(xiàn),在源碼中,某些時(shí)候會(huì)使用ProjectClassLoader.findParentClassLoader()來(lái)獲取基于當(dāng)前運(yùn)行環(huán)境的ClassLoader。
所以,需要在創(chuàng)建KieContainer的時(shí)候使用ProjectClassLoader.findParentClassLoader()生成的ClassLoader來(lái)取代默認(rèn)的MavenClassLoaderResolver:
如此一來(lái),就可以完全脫離maven環(huán)境來(lái)使用Drools引擎了,而Drools引擎也可以融入到任意的java項(xiàng)目之中,部署到任意的java環(huán)境之下。
4 總結(jié)
DMN在實(shí)際應(yīng)用中有比較明顯的優(yōu)點(diǎn):
- 可以在不修改代碼的情況下更新邏輯,減少上線過(guò)程中可能產(chǎn)生的問(wèn)題
- 可以通過(guò)一套代碼實(shí)現(xiàn)各種場(chǎng)景的需求
- 流程可視化,便于理解邏輯的運(yùn)行
- 能夠讓一部分不熟悉程序代碼的人員也可以參與編輯,避免流程全部只由程序員了解
但是相對(duì)的也存在一些弊端或者表現(xiàn)不夠好的地方:
- 流程復(fù)雜時(shí)圖形化表現(xiàn)比較雜亂,尤其是多個(gè)決策節(jié)點(diǎn)依賴相同的上游時(shí)
- 編輯規(guī)則時(shí)還是會(huì)有一部分場(chǎng)景條件需要寫(xiě)代碼才能實(shí)現(xiàn),例如列表包含(可以使用DMN專用代碼FEEL實(shí)現(xiàn),或者引用已經(jīng)實(shí)現(xiàn)的java方法實(shí)現(xiàn))
- 運(yùn)行規(guī)則的時(shí)間相較于使用java代碼實(shí)現(xiàn)要慢,因?yàn)橹虚g涉及規(guī)則文件解析和各個(gè)節(jié)點(diǎn)的計(jì)算,而java代碼可以更直接的實(shí)現(xiàn)
復(fù)雜規(guī)則示例
總體而言,DMN主要應(yīng)用在向程序員以外的人員提供決策管理的能力,以求更準(zhǔn)確地反映目的,從程序員的角度講可能和寫(xiě)ifelse沒(méi)什么不一樣,但是其他角色的參與人員可以通過(guò)較低的學(xué)習(xí)成本來(lái)上手實(shí)現(xiàn)規(guī)則,能夠減少溝通成本和不同人的理解差異產(chǎn)生的不符合預(yù)期的結(jié)果。
5 參考資料
Drools官方網(wǎng)站:https://www.drools.org/
Drools官方的DMN教學(xué):https://www.drools.org/learn/dmn.html
Drools官方的15分鐘簡(jiǎn)易教學(xué):https://learn-dmn-in-15-minutes.com/learn/introduction
Kogito在線編輯網(wǎng)頁(yè):https://sandbox.kie.org/#/
Kie官方網(wǎng)站:https://www.kie.org/
OMG官方網(wǎng)站的DMN頁(yè):https://www.omg.org/dmn/
作者簡(jiǎn)介
項(xiàng)贏,轉(zhuǎn)轉(zhuǎn)資深java工程師。長(zhǎng)期服務(wù)于轉(zhuǎn)轉(zhuǎn)圖書(shū)項(xiàng)目。