架構風格:數據流


本文探討:

  • 什么是管道過濾器風格(Pipe-and-filter Style)
  • 管道過濾器風格的約束
  • 管道過濾器風格的適用場景
  • 什么是批量順序處理風格(Batch-sequential Style)
  • 批量順序處理風格的約束
  • 批量順序處理風格的適用場景
  • 批量順序處理與管道過濾器的差異是什么
  • 什么是過程控制風格(Process Control Style)
  • 過程控制處理風格的約束
  • 過程控制處理風格的適用場景

不論是出國還是看電影,不知道你有沒有發現,很多國家的自來水是可以直接喝的,而中國的就不行。你有沒有想過,為什么我們國家的自來水不能直接喝呢?而且好像就只有中國人特別喜歡喝熱水!感冒、咳嗽、不舒服,發燒、流感、大姨媽,只要身體有點什么不舒服了,不管三七二十一,先喝點熱水!

這主要和以前的瘟疫、寄生蟲、腸胃炎有關系。以前的水基本沒有過濾,寄生蟲病菌比較多,經常引起各種疾病,所以很多國家都對水進行了凈化處理,能達到直接喝的標准。而以前我們國家還沒有能力做這種凈化處理,但是發現水燒開了也可以有效的殺死這些細菌,所以喝熱水的傳統就這么延續下來了。

對水的凈化處理其實原理很簡單,就是「過濾」!如果你家里安裝過凈化水系統,應該會發現凈化器一般都會有幾個過濾器,一般是三個。作用就是自來水經過一個個的過濾器,去除了水中的各種雜質,能達到直接飲用的程度。其中,每一個過濾器都是獨立的,可以獨立的替換。

數據流風格就是類似的處理方式,將數據看做水,一個個的處理單元看過一層層的過濾,數據經過一個個的處理單元,最終達到我們需要的結果。

說到過濾器,我們的第一印象應該就是Web開發中遇到的「過濾器/攔截器」了,繼而想到的應該就是設計模式中的職責鏈模式。

所以我們就從我們熟悉的職責鏈模式開始,來聊聊「數據流風格」。

職責鏈模式

職責鏈的類圖如下:

從結構上看,職責鏈模式有兩個對象:Client和Handler(包括具體的Handler實現)

  • Client發送消息給Handler
  • Handler將消息委托給具體的Handler鏈條處理
  • 處理完成后,返回給Client

Handler的具體實現就是一個個的過濾器,獨立的處理數據,也可以獨立的替換,這就解耦了請求發送者和接收者。請求沿着Handler鏈條依次傳遞,每個Handler判斷是否需要處理該請求,直到所有Handler都處理完成為止。

職責鏈主要適用於如下場景:

  • 有多個對象可以處理一個請求,但是哪個對象處理該請求需要運行時自動確定
  • 需要在不明確指定接收者的情況下,向多個對象中的一個提交一個請求
  • 可處理一個請求的對象集合是動態指定的

下面是一個簡單的clojure例子:

(defn handler-request1 [condition]
    (if (= "ConcreteHandler1" condition)
        (println "ConcreteHandler1 handled ")
        (println "ConcreteHandler1 passed ")))

(defn handler-request2 [condition]
    (if (= "ConcreteHandler2" condition)
        (println "ConcreteHandler2 handled ")
        (println "ConcreteHandler2 passed ")))

(defn handler-requestn [condition]
    (println "ConcreteHandlern handled "))

(->> "ConcreteHandler2"
    handler-request1
    handler-request2
    handler-requestn)
;handler-request2和handler-requestn會處理此字符串

管道過濾器風格

管道過濾器風格和職責鏈模式在結構上比較類似。管道過濾器風格包含了四個組件:

  • 讀端口:用於讀取數據,如標准輸入流、文本文件或傳感器采集的數據等。類似職責鏈的Client發送消息。
  • 寫端口:用於寫出數據,如文本文件、數據庫、標准輸出等。類似職責鏈的Client接收處理完的消息。
  • 管道:用於連接各個過濾器,使得消息能在各個過濾器之間進行流轉,也包含數據緩沖作用。類似職責鏈里的調用(消息發送)
  • 過濾器:處理消息組件,過濾器可以通過pull(后續過濾器從當前過濾器中拉取數據),push(前面的過濾器向當前過濾器推送數據)或主動方式(不斷從前面的過濾器中拉取數據,並向后面的過濾器推送數據)來獲取消息。類似職責鏈模式里的Handler。

管道過濾器風格的完整流程為:「讀端口」獲取需要處理的信息,通過管道傳遞給過濾器鏈,每個過濾器自行判斷是否需要對信息進行處理,一個過濾器處理完后通過管道將消息傳遞給下一個或多個過濾器,直到所有的過濾器全部處理完畢,通過寫端口,將處理完成的信息寫出到目標位置。

管道過濾器風格的典型應用有:

  • Linux的Shell
  • JavaEE Servlet Filter
  • 傳統的編譯器:一個階段(包括詞法分析、語法分析、語義分析和代碼生成)的輸出是另一個階段的輸入。

以Linux的Shell為例:

cat "app.log" | grep "^error" | cut -f 2
  • cat指令獲取app.log的內容,將結果通過管道傳遞給grep
  • grep指令過濾出所有以error開頭的行,將結果通過管道傳遞給cut
  • cut取出第二列的內容

在這里cat,grep,cut就是一個個的過濾器;而 | 就是管道;輸入輸出則是標准輸入輸出。

管道過濾器風格將處理邏輯封裝到獨立的過濾器中:

  • 可以通過新增過濾器的方式增加對信息的處理邏輯,相反可以通過移除過濾器的方式減少對信息的處理邏輯。提高了系統的擴展性
  • 過濾器之間沒有邏輯上的關聯關系,提高了復用性
  • 同時易於增加過濾器和移除過濾器,提高了系統的可維護性
  • 但是每個過濾器需要對信息進行解析,降低了系統性能,以及增加了過濾器自身的復雜度
  • 不過過濾器可以並行執行,這可以提高系統性能

管道過濾器風格主要用於處理數據的系統,不適用於處理交互的應用。

批量順序處理風格

同樣的「批量順序處理風格」也是主要用於處理數據的架構風格。一般它的處理組件稱為階段(stages)或者步驟(steps)。

它的處理流程如下:

  • 讀取一批數據,進行處理
  • 信息不是通過所謂的「管道」在各個階段之間進行流轉,而是通過類似臨時中間文件來進行階段之間的流轉,而文件可以被刪除
  • 下一階段需要在前一階段處理完后才能執行
  • 下一階段會從前一階段處理后的文件里,選擇自己需要的文件進行處理,處理后再寫到文件中

批量順序處理風格的每一步處理都是獨立的,並且每一步是順序執行的。只有當前一步處理完,后一步處理才能開始。數據傳達在每一步處理之間作為一個整體。比較適用於需要順序執行的某些固定操作的場景,並且這種流程不經常進行變化。Windows下的BAT程序就是這種應用的典型實例。

批量順序處理風格對架構屬性的影響與管道過濾器基本一致,這里不再贅述。

管道過濾器與批量順序處理差異

兩種風格都包含一系列的計算組件,通過將數據交給這一系列的計算組件來處理,來完成任務。

兩種風格的不同之處在:

管道過濾器風格 批量順序處理風格
細粒度 粗粒度
結果驅動處理 高延遲
內部輸入 外部訪問輸入
可以並發 不支持並發
交互不友好 不支持交互

內部輸入:過濾器之間直接傳遞數據
外部訪問輸入:批量順序處理一般都會先輸出到一個臨時文件,下一個階段再從臨時文件中獲取

過程控制風格

過程控制風格和上面的兩個風格處理方式差異較大,一般應用在嵌入式開發中,包含了四個組件:

  • 傳感器(Sensor):監聽某些重要信息
  • 控制器(Controller):邏輯控制
  • 執行器(Actuator):操作過程的物理方法
  • 過程(Process):你想要控制的內容

空調使用的就是過程控制風格,如下圖:

  • 我們打開空調,設置了21度
  • 空調的溫度傳感器(Sensor)檢測到室內溫度(Process)為15度,溫差為6度
  • 控制器(Controller)控制執行器(Actuator)吹熱風
  • 當室內溫度達到21度后,傳感器就通知控制器控制執行器停止工作

過程控制風格將系統拆分為一個個的子系統或模塊:

  • 各個子系統或模塊相互獨立,提高了模塊的復用性,以及系統的進化性可維護性
  • 子系統或模塊間的交互可能會影響性能

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM