本篇和大家談談一種通用的設計與處理模型——Pipeline(管道)。
Pipeline簡單介紹
Pipeline模型最早被使用在Unix操作系統中。據稱,假設說Unix是計算機文明中最偉大的發明,那么,Unix下的Pipe管道就是尾隨Unix所帶來的還有一個偉大的發明【1】。我覺得管道的出現,所要解決的問題,還是軟件設計中老生常談的設計目標——高內聚,低耦合。它以一種“鏈式模型”來串接不同的程序或者不同的組件,讓它們組成一條直線的工作流。這樣給定一個完整的輸入,經過各個組件的先后協同處理,得到唯一的終於輸出。
Pipeline模型的應用
以下列舉了,我熟悉或者有所了解的典型pipeline模型的應用。
公司.net web程序猿非常多,那么首先就談談asp.net。一個http請求到達httpserverIIS之后,就是經過pipeline模型被處理的。參見下圖:

說明一下,這幅圖,並沒有以下的圖形來得直觀。它的側重點在於展示管道中各個組件處理事件觸發的時序圖,而不是pipeline模型。但假設你思考以下,也能體會到當中“管道”的概念(注意右面循環圖標,假設須要比划一下的話,就是一個U逆時針旋轉90度的形狀)。
最后,我還是決定上個清晰點的圖:

上圖能夠請求到達IIS,經過HttpApplication工廠得到一個HttpApplication,創建一個HttpContext上下文,然后就會進入Http Pipeline。好了,這篇的目標並非談論http處理行為以及asp.net底層架構,所以到此為止。
又一個大家熟悉的web container,特別是java web人員——Tomcat
Tomcat接受請求之后,請求從被接受,被分發,被處理,到最后轉變成http響應,會走例如以下的管道【2】:

在《Tomcat系統架構與設計模式,第2部分: 設計模式分析》【3】中,你能夠清晰地發現一個最為顯而易見的設計模式——責任鏈模式(這是實現管道模型比較經常使用的一種設計模式)

可見,pipeline模型差點兒是大部分主流http server處理請求的通用模型。這樣的設計並不意外,由於pipeline模型特定的理念會讓你感覺到它似乎就是為了處理請求而生的。事實上,它的應用原不止這些web server架構。
以下,給大家帶來的還有一個典型演示樣例也是在web架構里廣為人知的MVC模型的良好實踐——Struts:

我覺得用這幅圖來闡述Pipeline最為清晰,簡潔。從上面這幅圖中你能夠看到對於pipeline模型的多處使用(單向、雙向都有)。它也非常好地展示了高內聚,低耦合的設計目標,展示了各個組件以類似“搭建積木”的形式來組合功能(見圖中Interceptor),我近期有空也在讀struts2的源代碼,假設你也有興趣,能夠看看這個專題。
最后一個演示樣例了,公司Javaserver的開發者,相信都會對Mina框架有所了解。以下是Mina的處理模型圖:

不再廢話了,相同是pipeline的優秀實踐。
上面介紹了非常多pipeline的優秀實踐,他們並非來自同一個領域,有web端,有處理socket的等。但對於他們的一個歸納,能夠是——優秀的服務端數據處理模型,我覺得公司在數據處理上比較頻繁,這也是選擇介紹pipeline模型的原因。
Pipeline模型帶來的啟發
事實上,關於它的優點已經在上面各種優秀的實踐中得以體現。但你還是應該能夠從中去發現一些能夠為我們所用的設計思想。我總結了我得一些觀點,歡迎各位拍磚:
(1) 工作流的參考模型
上面的各個模型圖非常難讓我不把pipeline模型與工作流模型聯想到一塊兒去。他們都是鏈式的(或者說流程式的),就像一條生產線一樣。各個組件的前后協同,會讓你聯想到生產流水線上得工人處理流過自己的產品環節。事實上,我在去年年末的時候在雲方圓徐工基礎任務流程里面以前嘗試使用了該模型,作為工作流模型。
(2) 服務framework的參考構建模型
Pipeline模型的一個特點是,在其內部是以組建的模式來實現組合的(盡管這些組建有先后順序之分),它假如你把側重點放在它的鏈式組合上,而不是將側重點放在上面的工作流上(工作流的關注點是流程的先后順序),那么全然能夠用這樣的方式來搭建一個復雜的服務,當然這樣做的前提是,單個組件的粒度必須盡可能小而且耦合性極低。
在這里我冒昧吐槽幾句:
在我的印象中,公司非常多服務都喜歡採用WebService,即使不是Web Service也是Http GET請求。當然,這當中的非常多情況是不得不採用它來和別的系統或者業務平台交互。但我一直堅持覺得,僅僅有在理論上你根本沒有可能拿到那些數據時,你才會採用別人提供的服務,比方:股票行情、天氣預報、各大開放平台(新浪、支付寶)的API的等。本公司之內的,原則上事實上能夠訪問的某些數據,有時我們反而退而求其次選擇採用Web Service這樣的模式。批量數據走http或者之上的協議(SOAP)在網絡上傳輸,有時web系統還有可能公布在遠端。想要性能從哪兒來?我了解你操心安全性,希望保持本業務平台數據庫的獨立性。告訴我,事實上你也明確有些操心是沒有意義的。我直接連你的庫,僅僅做一些查詢會有什么問題?假設你真的比較慎重的話,你也應該操心一下你的系統有被攻擊的可能性,為什么你沒有呢。甚至有人希望,某些類似的業務邏輯也把他抽象出來在dll外面包裝成web service。假設真得是這樣的話,我覺得“可復用的組件”這個詞就沒有必要存在了。
Pipeline模型應用
剛才談到,我覺得Pipeline模型帶來的啟發,我個人更看好第二點。我覺得在NGP構建API的時候,這樣的模型也能夠派上大用場。
就拿Redis舉個樣例(在一些場景下):
讀取數據流程
(1) 客戶程序從Redis讀取數據,假設讀取到則返回
(2) 假設沒有讀取到,則從數據庫抓取數據
(3) 從數據庫抓取到的數據存儲到Redis
寫入數據流程
(1) 客戶程序將數據寫入Redis
(2) 將數據寫入數據庫
假如有一天,你不打算採用Redis,或者Redis服務所有不可用。你怎么讓client自己能夠“智能感知”,讓這些巨大的后端變動對於client透明,而且不會產生調用異常?那么Pipeline模型,就能夠派上用場。由於上面這些流程都是可配置的,而開放的API是唯一的。
你是否會覺得普通的封裝也能夠實現上面的讀取數據流程?沒錯,也能夠。但我覺得Pipeline模型帶來的:流程式(有序)+可拆卸(配置),比普通的封裝機動性更好。
當然,這里僅僅是選擇了一個簡單的場景來舉個樣例。
Pipeline模型實現
事實上在上面那個Tomcat的設計模式截圖中已經看到,實現該模型最通常的設計模式就是責任鏈模式,在上面工作流那篇文章中,也是採用責任鏈模式來實現,但我當時忽略了一個非常重要的東西——Context,這是串聯整個Pipeline的重要前提。
你找到一篇不論什么介紹責任鏈模式的文章,然后搭配淘寶的《基於管道模式的容器設計》【4】就基本能夠全然了解Pipeline。
Pipeline模式的缺點
沒有那種模式是完美的。Pipeline模式的缺點是,每次它對於一個輸入(或者一次請求)都必須從鏈頭開始遍歷(參考Http Server處理請求就能明確),這確實存在一定的性能損耗。
引用
【1】:Unix Pipes 管道原稿
【2】:Servlet工作原理解析
【3】:Tomcat系統架構與設計模式,第 2 部分: 設計模式分析
【4】:基於管道模式的容器設計
