記一份關於HTTP服務的七層架構技術解析及運用
前言
今天簡單記錄一下關於HTTP服務的七層架構技術解析及運用
一般來說,計算機領域的體系結構普遍採用了分層的方式,從整體結構來看:
從最底層的硬體往高層依次有:
作業系統 -> 驅動程式 -> 執行庫 -> 系統程式 -> 應用程式等等。
從網路分層模型OSI來講,由上至下為:
應用層 -> 表示層 -> 會話層 -> 傳輸層 -> 網路層 -> 資料鏈路層 -> 物理層
當然實際應用的TCP/IP協議的分層就沒OSI標準這麼複雜。
從C語言檔案編寫到生成可執行檔案的過程來看:
預處理(展開後的C語言程式碼) -> 編譯成組合語言(特定CPU體系結構的組合語言原始檔) -> 彙編器生成目標檔案(CPU可執行的二進位制指令機器碼)
-> 連結器連線目標檔案生成可執行檔案(作業系統可以載入執行的二進位制檔案)
雖說這不算是軟體的分層結構,但是可以理解為一種通過分層來簡化複雜問題的思想,那麼,PHP語言可以認為是建立在C語言之上的層——其直譯器
Zend引擎是用C語言實現,畢竟用PHP這樣的指令碼語言編寫動態網頁要比用C語言方便得多。
當然還有我們最熟悉的MVC分層技術了,對於分層的好處,想必大家都已經比較熟悉了,這是一種“分而治之 大而化小”的思想。
說到分層就不得不提模組了,其實分層和模組是從兩種維度來進行“分而治之”的方式,模組是從橫向維度來將一個整體分割成若干個獨立的部分,
每個部分行使獨立自己的職責,當然它們之間也可能有依賴關係,這通過其對外提供的服務來實現。
如果將整個系統比做中國版圖的話,那麼模組方式將中國分為省,自治區等。分層則是從縱向緯度將一個整體從高至低劃分為若干個獨立的層,
一個完整的服務由底層至上層,層層傳遞最終產出。
分層和模組可以同時運用,例如中國用模組方式分為省之後,然後每個省的行政機構利用分層方式來行使職責,
從低到高有戶,村,鄉,區,市,省等等,每一層都向上一層彙報。
分層和模組會提高系統複雜度並影響效率(戶不能直接向省彙報,而需要一級一級向上彙報),但是這樣有利系統的擴充套件和維護,
每一層只需要關注自己提供的服務介面以及它下一層所提供的服務介面,試想一下如果省需要接受來自市,區,鄉,村等所有下級層的彙報,那些省幹部會很頭疼的。
一、HTTP服務傳統的三層架構MVC
HTTP服務中最經典的分層架構非MVC莫屬了,幾乎任何一個PHP開發框架都是支援MVC分層模式,此模式歷史也比較悠久,
是在上個世紀八十年代為程式語言Smalltalk設計的軟體模式,至今已經被廣泛引用,如下圖所示:
本文主要講的是HTTP服務七層架構技術,比MVC多出了四層,這樣複雜的分層是否有必要呢?
關於這個問題仁者見仁智者見智,有些人認為MVC分層粒度不夠精細,當然你也可以繼續堅持傳統的三層,那麼後文你也沒必要看下去了。
那麼為什麼MVC分層不夠精細呢,在我曾經使用開源框架的MVC模式的經驗中發現,V和C層功能職責一般都很清晰穩定,但是M層卻常常顯得臃腫笨拙。
C層主要是負責整體流程控制,一般規範的架構中,流程都可以用一張或幾張流程圖畫出,那麼表明流程一般都是固定的。
V層主要是負責頁面呈現,可能使用smarty模板引擎,也可能是自帶的模板引擎,顯示的頁面可能是HTML,XML或則JSON,
這些種類再多也都是可以度量的,所以M層也可以說是固定的。
而M層卻關係到系統的業務邏輯,隨著系統不斷迭代更新,M層中的內容也會不斷演變,而這一層中也有很多複雜的處理,
如檔案讀取,資料庫查詢,快取策略,邏輯運算,資料加工,資料打包等等。
所以MVC三層模型中,M層是還能再做細分的,當M層有一個更精細合理的分層方式之後,我們的業務邏輯演變過程會更加的得心應手。
二、HTTP服務七層架構
由上面的介紹,那麼我們對MVC中的M層再進行分層規劃,這裡給出的是一種對M層分五層的方式,
讀者如果覺得五層太多或則太少那麼可以參考這個再進行規劃。
原來的M層被分為:
A層:Application 應用層
B層:Business 業務層
C層:Component 元件層
D層:Datadriver 資料驅動層
S層:Systemdriver 系統驅動層
那麼整個七層架構則為:
1、Controller
2、View
3、Application
4、Business
5、Component
6、Datadriver
7、Systemdriver
結構圖還是參考經典MVC,將其中M層換成新的五層即可。
下面就依次給大家介紹一下這幾個新的層:
1、Application
應用層在最上面,其針對實際中的單個頁面或則單個介面,Controller通過HTTP請求地址中的引數找到對應的Application,
然後執行中指定的公共方法,比如main(),然後應用就開始啟動。
應用層的職責包括接受HTTP引數(一般是間接接受,比如從request物件中獲取),呼叫Business層的特定業務,
儲存業務執行結果,這些結果最終會由View顯示出來,當然是通過Controller協調。應用層是M層分解成五層之後最高的層,Controller會與此層直接通訊。
2、Business
業務層在應用層之下,通常一個應用例項對應一個業務例項,而一個業務有可能為多個應用服務,業務是一個執行流,它通過執行一系列的操作來完成應用的需求。
這些操作來自下層的元件層Component,可能一個業務需要一個或則多個元件來完成一個完整的需求。因為一個業務例項通常只對應一個功能,
所以只有一個固定的方法會被上層的應用呼叫,比如flow()。業務層的職責是幫應用層執行業務流並且有必要的時候返回資料給應用層,它會呼叫下層Component的方法。
3、Component
從元件層開始和上面兩層有一個本質的區別,元件層開始有了類庫的概念。前面兩層的例項通常只暴露一個特殊約定的公共的方法讓上層呼叫,
從這一層開始一個例項會提供多個方法給上層。
元件層通常和系統中一個角色對應,例如在部落格系統中,博文是一個角色,使用者是一個角色,那麼就會有博文元件BlogComponent,
使用者元件UserComponent,每個角色都有對應的操作,例如博文和使用者都可以新增刪除修改。
需要注意元件層中不應該有任何資料讀取的操作,資料讀取是下層資料驅動層來做的。如果元件層從下層獲取了資料,
那麼它的一個職責就是對資料進行加工。
例如BlogComponent有一個方法是獲取一個博文getBlog($id),那麼getBlog()方法中,從資料驅動層中取得了對應id的博文資料之後,
需要對博文資料進行一定的處理,比如將博文中的HTML特殊標籤過濾等等。
元件層不關心資料的讀取方式,但是會關心資料的結果,比如資料不存在或則資料已經過期。
4、Datadriver
資料驅動層的職責是為元件層提供源資料,此層關心資料的存取介質,存取方式等等,資料可能被儲存在DB,MC,Filesystem或則遠端的HTTP伺服器上。
資料驅動層不關心資料的內容,只關心資料讀取的操作結果,例如假設資料存在DB中,但是資料驅動層在執行資料庫查詢的時候出錯了,那麼需要在此層處理。
假設資料儲存在遠端的HTTP伺服器上,那麼資料驅動層需要關心HTTP返回碼是否為正確的200系列或則錯誤的400,500系列,
哪怕HTTP請求返回了錯誤的資料實體,但是返回碼為200,那麼資料驅動層也不關心,這種情況需要上層元件層來處理。
5、Systemdriver
系統驅動層是系統環境提供的資料訪問例項,例如資料庫服務的Systemdriver可能是一個db handler,
HTTP服務的Systemdriver可能是一個http handler,檔案儲存系統驅動層可能是一個file handler, 系統驅動層相對簡單,
這層可以和資料驅動層進行合併,其職責也較少。僅僅只是執行資料驅動層的資料訪問指令。
通常情況下這五個層中,上層的例項數量比下層的例項數量要多,整體類似一個倒置的梯形,如下圖所示:
在上圖中一共有6個Application,5個Business,4個Component,3個Datadriver,2個Systemdriver。
每個Application都由一個Business為其服務。
每個Business都服務一個或則多個Application(B5同時服務A5 A6),都有一個或則多個Componet為其服務。
每個Component為一個或則多個Business服務,都有一個或則多個Datadriver為其服務。
每個Datadriver為一個或則多個Component服務,都有一個或則多個Systemdriver為其服務。
每個Systemdriver為一個或則多個Datadriver服務。
三、HTTP服務的七層架構運用
現在運用這樣的架構來設計一個簡單的部落格系統,服務端用PHP語言實現,當然,架構是思想,不區分語言。
整個系統包含以下功能:
1、釋出博文
2、修改博文
3、刪除博文
4、評論博文
5、修改使用者資訊
要求每個功能都記錄操作日誌。
設計的資料儲存包括:
1、博文資料表
2、使用者資料表
3、評論資料表
4、日誌(存檔案系統)
在表結構設計的時候我們加入了一些冗餘欄位資訊,例如在博文表中有評論數量欄位comment_nums, 博文每被評論一次其值加1,每刪除一個評論其值減1。
使用者資料表中我們添加了使用者釋出的博文數量欄位blog_nums,使用者每釋出一篇博文其值加1,每刪除一篇博文其值減1。
下面設計分層:
應用層,一共有5個應用:
1、PostBlogApplication 釋出博文
2、UpdateBlogApplication 修改博文
3、DeleteBlogApplication 刪除博文
4、CommentBlogApplication 評論博文
5、UpdateUserApplication 修改使用者資訊
業務層,這5個應用分別有5個業務對其服務:
1、PostBlogBusiness 博文釋出業務
2、UpdateBlogBusiness 博文修改業務
3、DeleteBlogBusiness 博文刪除業務
4、CommentBlogBusiness 博文評論業務
5、UpdateUserBusiness 使用者修改業務
元件層,系統一共有4個角色對應4個元件:
1、BlogComponent 博文元件
提供方法包括:
(1)、postBlog() 釋出博文
(2)、deleteBlog()刪除博文
(3)、updateBlog()修改博文
(4)、getBlog()獲取博文內容
2、CommentComponent 評論元件
提供方法包括:
(1)、postComment() 釋出評論
(2)、deleteComment() 刪除評論
3、UserComponent 使用者元件
提供方法包括:
updateUser() 修改使用者資訊
4、LogComponent 日誌元件
提供方法包括:
logMsg() 記錄日誌資訊
資料驅動層和4個元件對應
1、BlogDatadriver DB型別 提供blog的select insert delete update
2、CommentDatadriver DB型別 提供comment的select insert delete update
3、UserDatadriver DB型別 提供user的select insert delete update
4、LogDatadriver FS型別 提供file的read write
系統驅動層: DB型別和FS型別
1、MySqlSystemdriver DB的handler
2、FileSystemdriver FS的handler
1、Controller通過重寫規則發現其對應的Application為PostBlogApplication,於是PostBlogApplication被例項化,
並且其中的特殊方法main()會被自動呼叫。
2、postBlogApplication需要PostBlogBusiness業務來完成博文釋出操作,PostBlogBusiness被例項化,
並且其中的特殊方法flow()會被呼叫。
3、根據需求,釋出博文的時候需要在博文表中插入一條博文,然後修改使用者資訊中的博文數量欄位。
那麼postBlogBusiness業務流就包括兩個操作,這兩個操作分別由BlogComponent中的postBlog()方法
和UserComponent中的updateUser()方法來實現,其中前者往博文表中插入博文資訊,後者將使用者資訊中的博文數量欄位加1。
由於系統要求任何操作都需要記錄日誌,因此還有第三個操作就是記錄日誌,通過BlogComponent的logMsg()方法實現,
那麼PostBlogBusiness業務流一共包括了三個操作,分別由三個元件來完成。
4、下面就需要分別考慮上面三個元件的下層呼叫了
其中BlogComponet的postBlog()呼叫BlogDatadriver的insert相關方法來插入博文資料,BlogDatadriver是DB型別,
因此通過MySqlSystemdriver 來實現。
UserComponent的updateUser()呼叫UserDatadriver的update相關方法來實現博文數量更新,UserDatadriver也是DB型別,因此也通過MySqlSystemdriver 來實現。
LogComponent的logMsg()呼叫LogDatadriver的write相關方法,LogDatadriver是FS型別,因此通過FileSystemdriver 來實現。
5、三個元件的操作都執行成功後,PostBlogBusiness告訴postBlogApplication博文釋出成功,然後postBlogApplication通過Controller來呼叫View相關的方法顯示執行結果。
對於其他幾個操作流程,大家可以舉一反三,這裡就不再多介紹了。
分層帶來的益處:
現在我們通過幾個系統功能的演變用例來看看這個分層帶來的益處:
用例1:為了方便對日誌的管理,現在希望能夠將日誌儲存在DB中而不是FS中。
解決方法:這是對資料儲存的改造,我們知道應該從資料驅動層入手。日誌功能是由日誌元件LogComponent實現的,
其中LogComponet的了logMsg()方法呼叫LogDatadriver來存日誌。我們將LogDatadriver由FS型別改造成DB型別,介面方法保持不變,這樣很快就完成了改造。
用例2:新增需求-使用者A可以將一篇博文轉移給另外一個使用者B。
解決方法:
步驟1:首先這個新需求對應了一個新的應用,於是我們新增了一個SendBlogApplication。
步驟2:需要有一個業務完成操作,新增業務為SendBlogBusiness。
步驟3:考慮轉移一篇博文涉及到的操作:①、將博文表中對應的使用者ID欄位由A的ID切換到B的ID;②、A使用者的博文數量減1;③、B使用者的博文數量加1。
這三個操作需要兩個元件來完成,這兩個元件我們系統中已經有了,BlogComponent的updateBlog ()完成操作1,UserComponent的updateUser()完成操作2,3。
從用例2可以看出,當新增了這麼一個需求的時候,我們在應用層和業務層添加了例項,元件層以下都不變,
這是因為現在的元件層已經能夠滿足新的業務的需求,當我們現有元件無法滿足新的一業務需求的時候,我們則需要對元件層做修改。
通過這兩個簡單的用例我們發現我們對系統的修改要麼可以很明確的確定在哪些層,要麼就是從上層元件往下層進行,操作起來很方便。