談一談規(guī)則引擎在活動系統(tǒng)中的落地

本文從 “為什么需要規(guī)則引擎” “規(guī)則引擎的定義” “規(guī)則引擎在營銷活動系統(tǒng)中的落地” “規(guī)則引擎平臺內(nèi)部架構(gòu)” “現(xiàn)有的規(guī)則引擎” 來描述。

1.為何需要規(guī)則引擎

如果是做業(yè)務(wù)開發(fā),大家在編程時或多或少都有接觸過規(guī)則引擎,如果沒有可以仔細(xì)的來看看這篇文章。
我們的業(yè)務(wù)代碼中往往包含了大量的case,case by case 到處都是條件的判斷和選擇,當(dāng)這些if-else、switch龐大到一定程度之后,我們的代碼就開始變得難以維護(hù),很多邏輯可能需要看好半天才能盤清楚。除去這種繁重的歷史系統(tǒng),即使每次是新的,我們新寫和校對邏輯條件也是一種很痛苦的事情。當(dāng)寫了一遍又一遍的邏輯之后,無非就是在對于業(yè)務(wù)邏輯進(jìn)行了一次又一次的決策,我們無法避免大量的邏輯判斷,但是可以讓這些易變的復(fù)雜邏輯從業(yè)務(wù)中剝離出來。就比如給這團(tuán)耳機(jī)加個繞線器,關(guān)鍵節(jié)點咱來把控


image.png

核心問題域有了:大量無法避免的if-else充斥在我們的系統(tǒng)中,對于系統(tǒng)的維護(hù)造成了威脅。(系統(tǒng)架構(gòu)的質(zhì)量屬性:可擴(kuò)展性、研發(fā)質(zhì)量&效率 嚴(yán)重受損):
1、無法直觀表達(dá)現(xiàn)有業(yè)務(wù)邏輯,新人入手困難
2、新增&改動邏輯困難,極難擴(kuò)展
3、容易出現(xiàn)case遺漏,質(zhì)量堪憂
4、兜底&通用處理成本高,系統(tǒng)魯棒性差
5、每次變更邏輯時都需要經(jīng)歷一次完整的研發(fā)-測試-發(fā)布-回測-灰度 等漫長的研發(fā)活動,效率成本堪憂
所以隔離這部分無法避免的業(yè)務(wù)決策邏輯,讓邏輯變得純粹可獨立維護(hù),所有邏輯一目了然動態(tài)可配,就是規(guī)則引擎要做的事兒。


image.png

2.定義規(guī)則引擎

2.1 基礎(chǔ)定義

簡單抽象下業(yè)務(wù)邏輯判斷決策的過程:數(shù)據(jù)流入規(guī)則產(chǎn)生結(jié)果。
想剝離這部分?jǐn)?shù)據(jù),那就讓我們把整個邏輯判斷過程當(dāng)作黑盒
定義清楚輸入輸出:
輸入:各種條件的具體值(我們if-else決策的值,簡單來說就是個k-v的mapping)
輸出:決策的結(jié)果可能bool、字典值,而這些值又可以作為一組輸入來產(chǎn)生新的決策。
核心功能:就是接受輸入經(jīng)過邏輯判斷給出輸出。
這大體這就是規(guī)則引擎的外部概貌了:


image.png

所以一句話來說:規(guī)則引擎就是通過接受動態(tài)數(shù)據(jù)流入根據(jù)內(nèi)部的規(guī)則得出決策結(jié)果的處理器,以抽離業(yè)務(wù)邏輯保證其獨立維護(hù)和動態(tài)更新。

2.2 規(guī)則的樣子

規(guī)則應(yīng)該長什么樣子
到這里還有點沒盤清楚的點,我們的規(guī)則表達(dá)式應(yīng)該是什么樣子,具備什么樣的特點:
最簡單的我們可以直接用之前的代碼表達(dá):

if (AAAA > 10) {
    return 1;
} else if (AAAA == 10) {
    if (BBB != 0 || CCC >10) {
        retuen 2;
    } else {
        return 3;
    }
} else {
    return 0;
}

也可以把代碼變成真表達(dá)式的樣子, 順序匹配就可以了:

rule1: AAA > 10 return 1;
rule2: AAA == 10 && (BBB != 0 || CCC >10) return 2;
rule3: AAA == 10 && BBB == 0 && CCC >= 10 return 3;
rule4: AAA < 10 return 0;

或者說取一下折中,主要看規(guī)則的形態(tài)和復(fù)雜程度是什么樣子的,如果是復(fù)雜的邏輯各種case并且結(jié)果無重合第一種相對合適,如果是層層過濾并且邏輯清晰 結(jié)果有重合或優(yōu)先級,第二種就相對合適了,很多時候取折中就挺舒服,業(yè)界也大部分是這樣做的。
對于規(guī)則的描述核心是:保證功能的前提下,好維護(hù),更清晰。

2.3 規(guī)則該如何執(zhí)行

我們知道規(guī)則是做什么的了,也知道規(guī)則怎么去做判斷長什么樣子,但是規(guī)則該以何種形態(tài)在我們代碼中執(zhí)行呢
目前大致有三種模式:

2.3.1 直接解釋執(zhí)行

這個模式相對好理解,在我們的系統(tǒng)中內(nèi)嵌了一個對于規(guī)則語言的解釋器,在規(guī)則腳本中描述規(guī)則邏輯,然后系統(tǒng)傳參給解釋器并調(diào)用對應(yīng)的腳本,最常見的就是lua/js 這種。

2.3.2 動態(tài)編譯成我們的代碼

這部分可以直接將所使用的規(guī)則腳本編譯成我們系統(tǒng)的代碼或者一些中間碼比如JVM 字節(jié)碼,運行時動態(tài)加載,在運行時狀態(tài)來看,和我們直接寫在代碼里幾乎沒有區(qū)別,并且性能要幾乎無差。

2.3.3 自定義解釋規(guī)則

這一種方式是我們自己定義腳本規(guī)范,最經(jīng)典的就是逆波蘭表達(dá)式或者自定義Json條件結(jié)構(gòu),按照約定去編寫規(guī)則,執(zhí)行時解析規(guī)則然后完成數(shù)據(jù)匹配及計算得出結(jié)果。

3.各種場景的使用情況

一開始的問題域已經(jīng)把需要規(guī)則的引擎的場景拋出來了,那更無腦點來說業(yè)界常見的需要規(guī)則引擎來解決問題的系統(tǒng)通常有哪些。

3.1 風(fēng)控系統(tǒng)

風(fēng)控系統(tǒng)的簡單來說就是告訴業(yè)務(wù)系統(tǒng)這個動作這個人有沒有風(fēng)險,輸入有很多:當(dāng)前用戶的設(shè)備信息、當(dāng)前cookie信息、過往操作記錄、接入渠道、四項信息 都是有的,然后過程中經(jīng)過一些列的規(guī)則判斷,得出風(fēng)險的結(jié)論或者風(fēng)險的等級,用于業(yè)務(wù)系統(tǒng)判斷該動作是否可以發(fā)生或者以什么樣的等級進(jìn)行。
首先風(fēng)控系統(tǒng)對接的業(yè)務(wù)會非常多,信貸、營銷、下單等,這些業(yè)務(wù)還需要區(qū)分具體產(chǎn)品線,每個業(yè)務(wù)還會有不同的動作場景,整體來看動作場景的量級已經(jīng)非常龐大了。對于一個動作場景而言:一系列輸入數(shù)據(jù)還需要經(jīng)過n層規(guī)則匹配和算法過濾出一些中間態(tài)因子數(shù)據(jù),這些因子再作為規(guī)則輸入產(chǎn)生結(jié)果。很顯然整個風(fēng)控判斷規(guī)則的數(shù)量是非常非常龐大的,這時候規(guī)則引擎的存在就非常有必要了,風(fēng)控系統(tǒng)對于規(guī)則場景的訴求也幾乎是最強(qiáng)的。

3.2 分發(fā)場景(&推薦場景)

這個跟風(fēng)控場景相對類似,如果需要對于不同的人給予不同的內(nèi)容,就必定會有規(guī)則存在,這個規(guī)則的數(shù)量也是十分龐大的(大致跟需要分發(fā)的內(nèi)容數(shù)成整理)

3.3 資金決策場景

在信貸、理財、支付場景會存在一個資金流轉(zhuǎn)的問題,一筆資金并不是像我們所想象的,是一個點到另一個點這樣的簡單,往往中間會因為合規(guī)、收益等n多問題發(fā)生資金的流轉(zhuǎn)決策,每一筆交易的過程可能對于業(yè)務(wù)上:出資賬戶、中間戶是不同的,技術(shù)上:經(jīng)過的系統(tǒng)也是不同的,這就需要我們根據(jù)不同的場景來決定這筆錢該怎么走,這就需要各種各樣的規(guī)則來完成這些流轉(zhuǎn)的控制。

3.4 數(shù)據(jù)打標(biāo)場景

現(xiàn)在都是數(shù)據(jù)為營的時代,如果想把這些數(shù)據(jù)高效的利用起來,肯定就要對這些數(shù)據(jù)進(jìn)行清洗、打標(biāo)、加工,才能產(chǎn)生對應(yīng)的價值。而清洗、打標(biāo)的過程實際上就是數(shù)據(jù)過濾規(guī)則產(chǎn)生結(jié)果的過程,

3.5 n多場景

如最開始提到的,當(dāng)我們的系統(tǒng)面臨那些易變復(fù)雜邏輯的時候,當(dāng)靠硬編系統(tǒng)代碼開始吃力時,就需要引入規(guī)則引擎來解決問題了。
一點資料:
https://zhuanlan.zhihu.com/p/47472836(考拉)
https://zhuanlan.zhihu.com/p/140916822(美團(tuán))
https://zhuanlan.zhihu.com/p/364546754(B站)

4.規(guī)則引擎在營銷活動系統(tǒng)中的落地

4.1 營銷活動的需求特點

上面也提到了規(guī)則引擎對于各類業(yè)務(wù)系統(tǒng)實際上是比較常見的,那么對于活動系統(tǒng)為什么需要呢。從活動出發(fā),先來看下具體的需求特點是否跟規(guī)則引擎的要解決的問題域相同:
1、需求量大。
2、倒排需求特別多,大都比較著急,標(biāo)準(zhǔn)迭代模式往往不適用。
最核心的:
3、活動邏輯變動大,每次都是全新的活動邏輯,但每次確又十分相似,具象點:玩法兒就那么多,但是每次針對的人群、獎勵規(guī)則、玩法兒之間的串聯(lián)規(guī)則都不盡相同。
針對這些特點,引入規(guī)則引擎 第一能解決人群規(guī)則、獎勵規(guī)則等易變的問題,在這之上也就同時滿足了需求量大、緊急等問題。

4.2 具體使用場景(哪些地方可以用)

在說具體使用場景前先來看下整個營銷活動系統(tǒng)的架構(gòu),按照交互分層來看一個營銷系統(tǒng)大致是這樣的:


image.png

表現(xiàn)層主要是完成界面的展示;
觸達(dá)層主要完成玩法兒與用戶的交互;
規(guī)則層包括:各種玩法兒內(nèi)部的規(guī)則&玩法兒之間的串聯(lián)
權(quán)益層包括:權(quán)益類:現(xiàn)金紅包、代幣、各種券等;觸達(dá)類:push、短信、私信等若干通知

4.2.1 玩法兒內(nèi)規(guī)則

很顯然規(guī)則引擎主要用于規(guī)則層的處理:
1、抽獎,不同的人&不同的場景對應(yīng)不同的獎池(不同的中獎概率、不同的獎品集合)
2、任務(wù),任務(wù)領(lǐng)取規(guī)則、任務(wù)完成指標(biāo)動態(tài)可配(不同的人不同的任務(wù),指標(biāo)條件可動態(tài)配置&組合)
3、通用激勵模型,不同的用戶特征 對應(yīng)不同的激勵程度(不同的人在不同的場景下,對于獎勵的感知程度都是不同的)
4、通用觸達(dá)模型,差異化文案內(nèi)容 分別具備不同的情感特征

4.2.2 玩兒串聯(lián)中的決策

這個單拎出來說,玩法兒的串聯(lián)通常是由 事件總線 + 用戶參與上下文信息組成的。事件總線就是直接表達(dá)事件與事件之間的關(guān)聯(lián)關(guān)系,用戶參與上下文信息通常是用戶參與過程的狀態(tài)(參與了哪幾個玩法兒,已獲得多少獎勵,是否達(dá)成預(yù)期目標(biāo))。事件匹配后,根據(jù)上下文信息進(jìn)行最終決策的過程就是規(guī)則執(zhí)行的過程。其實如果對于這部分規(guī)則及事件之間的串聯(lián)關(guān)系進(jìn)行集中描述,就更上一層作為活動流程引擎存在了。
整個觸發(fā)過程可以粗暴的解釋為:由源事件匹配所有需要擴(kuò)散的事件,根據(jù)上下文信息進(jìn)行時間過濾及部分動態(tài)計算得出要觸發(fā)的事件及對應(yīng)的觸發(fā)值。

4.3 舉個例子

拿上面的所有點合并舉個例子:{} 內(nèi)的內(nèi)容為可配置規(guī)則 用戶進(jìn)入活動后{根據(jù)一定規(guī)則指派任務(wù)} ,用戶達(dá)成{若干組合指標(biāo)}后任務(wù)完成,由于任務(wù)完成{根據(jù)用戶已收激勵給予用戶抽獎機(jī)會(幾次)或直接獎勵,并根據(jù)參與狀態(tài)判斷決定是否發(fā)放私信留存}, 用戶拿到抽獎機(jī)會后進(jìn)行抽獎{由于是新用戶,將面向現(xiàn)金等獎品池進(jìn)行抽獎,中獎概率高},抽中隨機(jī)現(xiàn)金獎品,{根據(jù)用戶特征計算出用戶受用的紅包金額},發(fā)放獎勵。
拆開來看:
{根據(jù)一定規(guī)則指派任務(wù)}{若干指標(biāo)}
{根據(jù)用戶已收激勵給予用戶抽獎機(jī)會(幾次)或直接獎勵,并根據(jù)參與狀態(tài)判斷決定是否發(fā)放私信留存}{由于是新用戶,將面向現(xiàn)金等獎品池進(jìn)行抽獎,中獎概率高}
${根據(jù)用戶特征計算出用戶受用的紅包金額}
可以很清楚的看出來,整個活動玩法主體邏輯是穩(wěn)定的,那些易變規(guī)則都可以抽象出來可配置,并且活動之間的串聯(lián)規(guī)則都是可隨時修改并根據(jù)實時情況計算的,這些決策的規(guī)則的執(zhí)行者就是規(guī)則引擎。

4.4 作出的技術(shù)選型

對于營銷活動場景來說:
1、使用人員:運營+部分技術(shù)人員
2、規(guī)則輸入:用戶標(biāo)簽信息、參與的上下文信息(已收激勵數(shù)量、第幾次參與、是否轉(zhuǎn)化等)
3、規(guī)則輸出:一些key值:事件key、任務(wù)key、獎池key、金額
4、規(guī)則執(zhí)行:標(biāo)簽匹配過程,大多是bool邏輯判斷
很顯然相對于其他場景來看營銷活動的邏輯相對簡單,現(xiàn)有幾乎所有的規(guī)則引擎都能支持,但是考慮到操作人員有運營同學(xué)并且規(guī)則量大且非常不穩(wěn)定(一個活動n條),所以足夠簡單&清晰、易操作 變成了剛需,個人感覺最適合的就是用逆波蘭表達(dá)式做一個最基礎(chǔ)的規(guī)則引擎,雖然一定程度上損耗了性能,常規(guī)十萬級別qps根本問題不大(比起IO消耗根本不算啥,瓶頸通常不是這里)。對于那種峰值流量巨大要求苛刻的營銷活動,可以直接干掉規(guī)則引擎部分裸寫代碼實現(xiàn)。

4.5 避免誤用

上面說了那么多規(guī)則引擎的好處,但是規(guī)則引擎適用的場景其實也就這些了,對于一項技術(shù)千萬不能過度癡迷,需要針對場景來綜合評估落地。如果roi不夠,盲目的去落地往往會得不償失,不僅增加了系統(tǒng)的復(fù)雜程度,還要處理新技術(shù)帶來的一系列問題。
之前看過一個架構(gòu)設(shè)計的思路,以風(fēng)險作為驅(qū)動主導(dǎo)架構(gòu)設(shè)計,在綜合評估要設(shè)計系統(tǒng)的各種質(zhì)量屬性之后,選用現(xiàn)有的一個和完備的一組技術(shù)方案 根據(jù)面臨的風(fēng)險的嚴(yán)重程度依次解決,往往能設(shè)計出更適合現(xiàn)狀的架構(gòu)。對于規(guī)則引擎的選擇也是這樣,如果真的存在上面說的那些風(fēng)險,如果引入規(guī)則引擎是最合適的解決方式,并且?guī)淼娘L(fēng)險較小或可以接受那么就可以選用。
總之,系統(tǒng)越簡單越好。


image.png

5.規(guī)則引擎架構(gòu)設(shè)計

規(guī)則引擎從出現(xiàn)到現(xiàn)在已經(jīng)發(fā)展好多好多年了,業(yè)界已經(jīng)有非常多的現(xiàn)有解決方案了,上面提到的三種模式都是支持的,那三種其實也可以近似的認(rèn)為是第1,2,3代規(guī)則引擎。
我們在使用規(guī)則引擎時通常有兩種構(gòu)建思路:
1、當(dāng)作單純的sdk來用,一個執(zhí)行函數(shù)而已,我們只需要引入核心庫或者自己寫個庫就能用了。
2、構(gòu)建統(tǒng)一的規(guī)則引擎的平臺,這種場景往往是需要大規(guī)模的管理規(guī)則和執(zhí)行規(guī)則,并且具有完備的降級方案。

5.1 領(lǐng)域設(shè)計(相對粗暴哈)

一個規(guī)則對象由條件、優(yōu)先級、結(jié)果構(gòu)成,其中條件包含:特征、操作符、閾值
由該領(lǐng)域?qū)ο笾隙x規(guī)則引擎的領(lǐng)域服務(wù):規(guī)則管理、規(guī)則核心的加載模式支持,最核心的執(zhí)行(包括入?yún)⑻幚?、?guī)則匹配、邏輯運算、結(jié)果給出)


image.png
5.2 規(guī)則引擎服務(wù)架構(gòu)

規(guī)則引擎服務(wù)通常是在核心的規(guī)則引擎之上,增加了一些執(zhí)行時門面服務(wù)、可視化規(guī)則創(chuàng)建、多種規(guī)則引擎支持、更加系統(tǒng)的規(guī)則管理體、調(diào)用上下文、附加數(shù)據(jù)支持等服務(wù)而已,大致長這樣子:


image.png
5.3 解決規(guī)則引擎的一些問題

規(guī)則引擎一直到現(xiàn)在看起來都是非常好用但是有幾個問題始終比較難解:
1、易用性問題
對于復(fù)雜規(guī)則通常的直接解釋型、編譯類的實現(xiàn)對于操作人員來說只不過是代碼的位置從系統(tǒng)中牽出來了,整體的書寫難度并沒有降低太多,反而還要感知新的一些語法規(guī)則,著實很頭痛。
簡單的邏輯表達(dá)式類型的規(guī)則雖然能通過拖拽來搞定,但是實際操作難度仍然比較高,無腦上手是絕對不可能的,得有一定的學(xué)習(xí)成本。
2、性能問題
自己定義語法樹解析,性能比寫代碼差了一些,結(jié)構(gòu)的解析,程序的執(zhí)行棧比寫代碼要高一個數(shù)量級。
那種內(nèi)嵌解釋器或直接編譯的場景,雖然性能要好一些,但規(guī)則引擎執(zhí)行冷啟動和熱替換時還是有不少性能代價的。

5.常用規(guī)則引擎

Java 中的規(guī)則引擎還是挺多的,Go語言的目前貌似不多,生態(tài)還是硬傷啊

現(xiàn)有的平臺:
1、Drools
Java 語言編寫的開放源碼規(guī)則引擎,基于Apache協(xié)議,基于RETE算法,于2005年被JBoss收購,用來減少硬編碼還是不錯的。
2、Easy Rules
Easy Rules 提供了規(guī)則抽象來創(chuàng)建帶有條件和操作的規(guī)則,以及運行一組規(guī)則來評估條件和執(zhí)行操作的RulesEngine API。相對易于學(xué)習(xí)的API。并且支持創(chuàng)建復(fù)合規(guī)則。能夠使用表達(dá)式語言定義規(guī)則
3、Ilog JRules:
IBM WebSphere ILOG JRules 是一個業(yè)務(wù)規(guī)則管理平臺,它盡可能減少了研發(fā)人員的參與。順道提供了一整套全生命周期管理工具。

常見的表達(dá)式引擎(可用來構(gòu)建規(guī)則引擎):
1、能產(chǎn)生獨立class文件的編譯型:fel、simpleEL、groovy
2、產(chǎn)生獨立內(nèi)存執(zhí)行的解析型:aviator、JEXL

直接使用內(nèi)置解釋器
1、Java:Lua/JS
需要新學(xué)一份腳本語言了,不過lua 還是很值得學(xué)習(xí),openrestry、redis 用起來還挺舒服。

一點擴(kuò)展

哦對,寫到最后發(fā)現(xiàn)里面出現(xiàn)規(guī)則引擎、表達(dá)式引擎、決策引擎還有流程引擎,感覺有些凌亂,談一下區(qū)分觀點哈,懇請指正:
1、表達(dá)式引擎 核心關(guān)注點在于易變邏輯的抽離,通過表達(dá)式替換硬編碼。
2、規(guī)則引擎 是表達(dá)式引擎更上一層,核心關(guān)注點在于匹配,主要解決規(guī)則分散難以維護(hù)的問題。
3、決策引擎是規(guī)則引擎更上一層,核心關(guān)注點在于判斷,主要解決選擇的問題。
4、流程引擎 是表達(dá)式引擎或規(guī)則引擎之上,核心關(guān)注點在于整體的流程劉莊,主要解決流程節(jié)點分散管理不直觀的問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容