端智慧系列文章|端側如何實現實時CEP引擎
服務端要對客戶端埋點進行資料清洗,考慮到閒魚的DAU已經突破2000w,這個量是非常龐大的,非常消耗服務端資源;
Blink的策略是實時執行的,同樣因為資源問題,現在只能同時上線十幾個策略。
狀態(State):狀態是根據flink指令碼里面的程式碼來決定的,最終會有一個
$end$
的Final狀態轉換(StateTransition):State的轉換條件,包括
take/proceed/ignore
take
: 滿足條件,獲取當前元素,進入下一狀態proceed
:不論是否滿足條件,不獲取當前元素,直接進入下狀態(如optional)並進行判斷是否滿足條件。ignore
:不滿足條件,忽略,進入下一狀態。
Pattern
來構建這個NFA,首先用它描述這個不確定性狀態機。首先是構建一個 Pattern
的一個連結串列,得到這個連結串列之後,會將每個Pattern對映成為 State
的圖,點與點之間會透過 StateTransition
來連線。以下面的Python程式碼為例,看下如何API是如何工作的:以start事件開始,後續跟隨一個middle的事件,後面緊跟著一個end事件作為結尾
Pattern.begin("start").where(SimpleCondition())\.followed_by('middle').where(SimpleCondition())\.next_('end').where(SimpleCondition())
start
、 middle
、 end
。Pattern裡面儲存了指向前面節點的引用 previous
,整個Pattern連結串列構建完如下圖所示:end
節點的一個引用 Ref
,Pattern中會有一個變數指向前一個節點,這樣就可以得到一個Pattern的反向連結串列。classPattern:# 靜態方法,用來生成起始的pattern@staticmethoddef begin(self, name):pass# 標記緊接著的事件def followed_by(self, name):pass# 標記不需要緊跟的事件def not_followed_by(self, name):pass# 標記緊跟的事件def next_(self, name):pass# 標記事件迴圈次數def times(self, times):pass# 標記當前事件觸發的條件def where(self, condition):pass# 標記當前事件的and條件def and_(self, condition):pass# 標記當前事件的or條件def or_(self, condition):pass# 用於聚合def group_by(self, fields):pass# 用於聚合,渠道特定欄位的值def fields(self, key_by_state_name, field):pass# 用於聚合,統計事件具體的數量def count(self, field, condition):pass
StateTransition
。有了Pattern連結串列,接下來就需要編譯器(Compiler)了,它主要是將Pattern連結串列轉化成NFA圖,首先來看下NFA的2個核心元件:State
和 StateTransition
。
classState(object):
def __init__(self, name, state_type):
self.__name = name # 節點的名稱,同Pattern的名稱
self.__state_type = state_type # 節點的型別:Start/Normal/Stop/Final
self.__state_transitions = [] # 到其他節點的邊
Start/Final/Normal/Stop
。建立一個
$end$
的結束節點(Final
)再從後往前建立每個state節點,作為中間節點(
Normal/Stop
)最後建立一個開始節點(
Start
)
StateTransition
。
classStateTransition:
def __init__(self, source_state, action, target_state, condition):
self.__source_state = source_state # 開始的State節點
self.__action = action # 具體action型別:take/ignore/proceed
self.__target_state = target_state # 結束的State節點
self.__condition = condition # 節點之間的判斷條件
classConsumingStrategy: STRICT = 0# 嚴格匹配下個 SKIP_TILL_NEXT = 1# 跳過下一個 SKIP_TILL_ANY = 2# 跳過任意一個 NOT_FOLLOW = 3# 非跟隨模式 NOT_NEXT = 4# 非緊鄰模式
STRICT
: 如果命中了事件了,會進到下個狀態SKIP_TILL_NEXT
: 如果命中了會進入下一個狀態,否則會再當前節點迴圈,進入ignore的邊SKIP_TILL_ANY
: 不管是否命中條件,都會一直在當前狀態迴圈NOT_FOLLOW
: 如果遇到了一個匹配的,就會進入Stop狀態NOT_NEXT
: 如果命中一條,則進入Stop狀態
followed_by
介面會建立 SKIP_TILL_NEXT
的節點。Pattern.begin('e1').where(SimpleCondition()).times(3);
Times=3
的Pattern,編譯器在拿到這個Pattern之後,一樣先建立一個$end$的Final節點,在處理times的時候,會建立重複的節點,只不過名稱不同,不同的點之間用take連結起來,如下圖所示:_pattern = Pattern.begin("start").where(self.start_filter)\.followed_by('middle').where(SimpleCondition())\.next_('end').where(self.end_filter)\.group_by('group_by').fields('start', 'userId')
start
節點中的 userId
作為聚合的節點,我們就會得到如下的 Pattern
連結串列:group_by
節點的時候,我們需要做個特殊處理,判斷如果有聚合節點,我們就需要再 $end$
節點和前面節點之間插入一個聚合的節點和哨兵位節點,哨兵位節點命名為 $aggregationStartState$
,最終效果如下圖所示:$aggregationStartState$
節點和 group_by
節點之間,是透過proceed結合,不需要滿足特定條件就可以執行。State
節點 AggregationState
:
# 建立聚合節點
def __create_aggregation_state(self, sink_state):
# 渠道聚合節點的condition
_aggregation_condition = self.__current_pattern.get_aggregation_condition()
# 建立AggregationState
not_next = AggregationState(
self.__current_pattern.get_name(),
StateType.Normal,
_aggregation_condition.get_key_by_state_name(),
_aggregation_condition.get_field())
self.__states.append(not_next)
# 獲取take的條件
take_condition = self.__get_take_condition(self.__current_pattern)
not_next.add_take(sink_state, take_condition)
# 將遊標指向上一個節點
self.__following_pattern = self.__current_pattern
self.__current_pattern = self.__current_pattern.get_previous()
return not_nex
Show me the code
講了太多原理的東西,接下來看下程式碼裡面如何工作的,先來看下如何來編寫一個CEP策略。
需要匹配使用者檢視3次寶貝詳情頁
# 1. 建立用來匹配的Pattern
_pattern = Pattern.begin('e1').where(KVCondition('scene', 'Page_xyItemDetail')).times(3)
# 2. 將需要匹配的事件流_batch_data和待匹配的Pattern
# CEP內部會先將pattern轉化成NFA,然後再用NFA去匹配事件流
_cep = CEP.pattern(_batch_data['eventSeq'], _pattern)
# 用來選擇的邏輯
def select_function(data):
pass
# 3. 匹配完成,透過cep的select介面查詢匹配到的結果
self.result = _cep.select(select_function)
CEP.pattern()
函式里面,會先建立 NFA
,然後去進行匹配,可見整個匹配策略指令碼非常的短小精悍。如下程式碼用來將 Pattern
連結串列轉化成 NFA
圖:
# 最後一個Pattern節點不允許是NotFollowedByif self.__current_pattern.get_quantifier().get_consuming_strategy() == ConsumingStrategy.NOT_FOLLOW:raiseException('NotFollowedBy is not supported as a last part of a Pattern!')# 校驗Pattern的名稱,必須唯一self.__check_pattern_name_uniqueness()# 校驗Pattern的策略self.__check_pattern_skip_strategy()# 首先建立Final節點sink_state = self.__create_ending_state()# 判定是否有聚合節點if self.__current_pattern.get_aggregation_condition() isnotNone:# 首先建立聚合節點 sink_state = self.__create_aggregation_state(sink_state)# 然後建立聚合幾點的起始節點 sink_state = self.__create_aggregation_start_state(sink_state)# 建立狀態機中的中間節點,此函式會迴圈知道Start節點的Patternsink_state = self.__create_middle_states(sink_state)# 最後建立Start節點self.__create_start_state(sink_state)# 根據state列表和window來建立NFAreturn NFA(self.__states, self.__window_time, False)
執行時間
記憶體使用
端計算是用Python實現,無法做到像Flink的狀態機常駐記憶體,每次都要重新建立匹配,帶來了額外的消耗
在事件流的清洗上面,現在是透過回朔拿到之前的事件流,存在大量的重複計算,後續可以借鑑Flink的Window機制來進行最佳化。
目前編譯器暫時還不支援Group Pattern,後續還要對其進行擴充套件。
Python指令碼現在還是需要手動編寫,後續還可以考慮透過DSL來自動生成。
對於Flink的理解
CEP in Flink(1) - CEP規則解析
《Efficient Pattern Matching over Event Streams》
閒魚團隊是Flutter+Dart FaaS前後端一體化新技術的行業領軍者,就是現在!客戶端/服務端java/架構/前端/質量工程師面向社會招聘,base杭州阿里巴巴西溪園區,一起做有創想空間的社群產品、做深度頂級的開源專案,一起擴充技術邊界成就極致!
*投餵簡歷給小閒魚→guicai.gxy@alibaba-inc.com
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900359/viewspace-2675164/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MaterialDesgin系列文章(二)NavigationView和DrawerLayout實現側滑功能NavigationView
- MaterialDesign系列文章(二)NavigationView和DrawerLayout實現側滑功能NavigationView
- 玩轉雲端 | 看天翼雲iBox智慧盒子如何實現邊緣側的“神機妙算”
- Storm系列(五)DRPC實現遠端呼叫ORMRPC
- ubuntu如何實現遠端操控Ubuntu
- 文章評論功能前後端實現方案總結後端
- pc端控制安卓如何實現安卓
- NAS如何實現遠端訪問
- 運動App如何實現端側後臺保活,讓運動記錄更完整?APP
- 如何實現遠端桌面連線操作
- Java伺服器端如何實現跨越Java伺服器
- 最高增強至1440p,阿里雲釋出端側實時超分工具,低成本實現高畫質阿里
- 端到端的實時計算:TiDB + Flink 最佳實踐TiDB
- 《遠端控制》-服務端實現(一)服務端
- web 端展現報表資料時如何實現摺疊展開效果?Web
- Linux 實現本、異地遠端資料實時同步功能Linux
- 如何遠端投屏實現螢幕共享
- HTML+CSS實現時間軸(移動端)HTMLCSS
- web端 網頁端分享功能的實現Web網頁
- web 端展現報表時查詢表單如何實現引數聯動Web
- 如何實現 Markdown 撰寫文章
- pytorch實現yolov3(5) 實現端到端的目標檢測PyTorchYOLO
- 阿里開源!輕量級深度學習端側推理引擎 MNN阿里深度學習
- Go實現ssh執行遠端命令及遠端終端Go
- Zoho CRM系統如何實現遠端辦公?
- 移動端的車牌識別如何實現
- golang實現tcp客戶端服務端程式GolangTCP客戶端服務端
- 移動端頁面不滿一屏時如何實現滿屏背景?
- underscore 系列之實現一個模板引擎(上)
- Netty系列文章之服務端啟動分析Netty服務端
- 實現服務端和客戶端的實時雙向資料傳輸-WebSocket簡單瞭解服務端客戶端Web
- 客戶端骨架屏實現客戶端
- 實現SSR服務端渲染服務端
- Java實現後端分頁Java後端
- Web 端 APNG 播放實現原理Web
- pc端實現支付寶支付
- 如何實現文章AI偽原創?AI
- 工業閘道器如何助力垃圾電廠實現遠端監控智慧管理?