標籤:

系統開發之設計模式

上周五同事分享了design patterns in networks,裡面很多patterns都是做路由器防火牆這樣的轉發設備之所以高效的精髓所在。「程序人生」的讀者多為互聯網應用(系統)開發者,對這些design patterns未必了解,所以這篇文章我乾脆抽取同事分享內容和互聯網系統開發關聯較大的patterns,講講在互聯網項目上的應用場景,借花獻佛。

Control plane和data plane分離

這兩個概念幾乎是networks 101的入門概念。Juniper上世紀末興起的重要原因之一就是嚴格區分界定control plane和data plane,然後用ASIC實現data plane。Data plane是指一個網路設備用於報文轉發的component,它的效率決定整個設備的效率,一般會由硬體完成。Control plane是指一個設備協議相關的部分,可以沒有數據轉發那麼高效。

當你打開瀏覽器訪問google時,internet上面的網路設備就開始緊鑼密鼓地工作,目的只有一個,把你的請求轉發到google的伺服器。學過網路課程的人都知道,這其中運行的網路設備就是路由器。路由器需要有足夠快的轉發速度,延時越小越好 —— 這考量的是data plane的效率;而data plane轉發決策的依據 —— 路由,則由control plane的協議處理來完成。

在一個互聯網系統上,似乎沒有control plane和data plane較為清晰的界定。我們不妨粗暴地認為用戶訪問的路徑為data plane,而admin相關的路徑為control plane。對於data plane上的工作,我們可以單獨劃分一個集群來處理,力求每個request都得到最高效地處理,而control plane上的工作,則可以儘可能用比較小的資源完成。這裡最重要的原則是:data plane和control plane做到路徑分離,讓data plane上的大量requests不致於影響control plane的正常工作;同時control plane上的慢速任務不致於拖累data plane的訪問速度。

First path vs Fast path

做防火牆,少不了會遇到first path和fast path的概念。防火牆處理的是雙向的數據流,需要記錄狀態,所以有session的概念。在first path裡面,走一個慢速的全路徑,創建session,在fast path裡面,則可以利用session裡面的各種信息快速處理數據報文。

在互聯網系統上,類似的mapping很好建立。在一個需要用戶登錄的系統里,用戶登錄的整個過程可以被視作first path,隨後的訪問可以被視作fast path。

用戶登錄是一個複雜的過程,不僅僅是驗證用戶合法性這麼簡單。在前台儘快給出用戶登錄後頁面的同時(responsiveness很重要),後台需要載入一系列用戶相關的數據到緩存(比如redis)中,以便用戶在隨後的訪問中能夠快速獲取。載入的數據可以是用戶的朋友信息,用戶可能會訪問的熱點數據,各種各樣的counters等等。

當然,first path/fast path的概念不僅僅適用於登錄和登錄後的訪問,還有很多其它的應用場景。比如說一個規則系統,首次訪問時從規則引擎中抽取用戶相關的規則進行編譯和緩存,之後的訪問則直接從編譯好的規則緩存中高效讀取。

注意first path/fast path的概念是相對的,就像分形幾何一樣,first path裡面可以再區分中first path/fast path,fast path里也可以再區分出first path/fast path,不斷迭代下去。這樣做的目的是,不斷地優化系統中最常用的80%的路徑,讓它們的效率最大化。

Slow path vs Fast path

Slow path/fast path和first path/fast path很類似,但又不盡相同。就用戶登錄而言,我們假定(或者有實際數據)80%的用戶通過用戶名/密碼登錄,那麼用戶名/密碼登錄就要置於fast path下,而其它的諸如LDAP,OpenID,XAuth登錄方式置於slow path下。

這樣區分fast path/slow path的好處是,一旦有需要,我們可以把對應的代碼用更高效的方式實現,比如說整個系統是python實現的,系統中的一些fast path處在用戶訪問的熱點區域,那麼可以考慮用go來實現。

Queue based design

在網路設備中,queue無處不在,幾乎成了最基本的操作。一個數據報文從硬體上來之後被放到了driver的queue上,然後在系統處理的各個層級,不斷地被enqueue/dequeue。Queue有很多好處,比如說延遲處理,優先順序,流量整形(traffic shaping)。

一個複雜的互聯網系統很多時候也需要queue來控制任務處理的節奏。比如說email驗證這樣的事情,可以不必在當前的request里完成,而放到message queue中,由後台的worker來處理。另外,queue可以有不同的優先順序,發送email和將圖片轉換成不同的size顯然可以放入不同的優先順序隊列中調度。

對於互聯網項目而言,有很多成熟的message queue system,比如RabbitMQ,ZeroMQ。

Pipeline

在網路系統裡面,如果一個任務很複雜,需要很多CPU時間,那麼該任務可以分解成多個小任務來執行,否則的話,這個任務佔用CPU時間過長,導致其他任務無法執行。當一個任務分解成多個小任務後,每個小任務之間由queue連接,上一次處理完成之後,放入下一個queue。這樣可以任務調度更均衡。

在互聯網項目中,pipeline有很多應用場合。比如說一個workflow裡面狀態機的改變,可能會執行一系列的操作,然後最終遷移到新的狀態。如果這一系列的操作在一個大的function里執行,而非分解成若干個通過queue相連的小操作,那麼整個處理過程中的慢速操作會影響整個系統的吞吐量。而且,這樣做非常不利於concurrency。

在一個大型系統中,pipeline的程度決定了concurrency的程度。而pipeline的應用程度會影響整個系統架構的吞吐量。有些編程語言,如golang,天然就讓你的思維模式往pipeline的方式去轉(通過go/chan)。

Finite State Machine

既然提到了狀態機,就講講狀態機。狀態機由兩個元素組成:狀態;以及狀態遷移。狀態遷移是由動作引起的,因此一個狀態機可以表示為 state machine = {state, event} -> (action, new state)。只要畫出一個二維表,就能分析系統所有可能的路徑,而且很難有遺漏。在網路設備中,大部分協議都由狀態機來表述,比如說ospf,igmp,tcp等等。

在互聯網項目中,狀態機無處不在。比如說訂單處理。一個訂單的處理流程用狀態機表述再完美不過。下面是我曾經寫過的一段示例代碼(python):

ORDER_EVENTS = {n (const.ORDER_EVENT_PAYED, const.ORDER_STATE_CREATED): {n new_state: const.ORDER_STATE_PAYED,n callback: on_order_event_payed,n },n (const.ORDER_EVENT_PAY_EXPIRED, const.ORDER_STATE_CREATED): {n new_state: const.ORDER_STATE_CANCELLED,n callback: on_order_event_cancelled,n },n (const.ORDER_EVENT_CONFIRMED, const.ORDER_STATE_PAYED): {n new_state: const.ORDER_STATE_CLOSED,n callback: on_order_event_confirmed,n },n (const.ORDER_EVENT_CONFIRM_EXPIRED, const.ORDER_STATE_PAYED): {n new_state: const.ORDER_STATE_CLOSED,n callback: on_order_event_confirm_expired,n },n ...n}n

Watchdog

最後稍提一下watchdog。一般來說,路由器防火牆這樣的網路系統是實時系統,任何一個任務,都應在規定的時間內結束,否則就是系統錯誤。所以我們需要watchdog來監控任務(有硬體watchdog,也有軟體的)。watchdog還可以幫助開發者發現系統中的死鎖,過長的循環,任務分配不合理等問題。如果某一任務執行時間過長,它就會阻塞其他任務,如果所有的CPU都被這類任務佔用了,系統就無法響應事件,也有可能無法將這些任務調度出去。

在互聯網項目中,處理request,處理async task等等都有一系列數量有限的worker。如果某個worker死鎖,或者執行時間過長(可能是異常情況)導致「假死」,我們可以用watchdog進程來殺掉這些已經「假死」的worker,讓系統的吞吐量恢復到正常水平。如果不這樣做,「假死」的worker越積越多,可能會最終導致整個系統 out of service。

歡迎訂閱公眾號『程序人生』(搜索微信號 programmer_life)。每篇文章都力求原汁原味,早8點與您相會。


推薦閱讀:

抽象的能力
學習的力量
當我旅行的時候,我在想些什麼(4)
[雜談] 談談讀書(續)
[讀者留言] 程序人生 - 且行且珍惜

TAG:迷思 |