|
1. 為什么分層?
計算機領域的體系結構普遍采用了分層的方式。
從整體結構來看:
從最底層的硬件往高層依次有:
操作系統 -> 驅動程序 -> 運行庫 -> 系統程序 -> 應用程序等等。
從網絡分層模型OSI來講,由上至下為:
應用層 -> 表示層 -> 會話層 -> 傳輸層 -> 網絡層 -> 數據鏈路層 -> 物理層
當然實際應用的TCP/IP協議的分層就沒OSI標準這么復雜。
從C語言文件編寫到生成可執行文件的過程來看:
預處理(展開后的C語言代碼) -> 編譯成匯編語言(特定CPU體系結構的匯編語言源文件) -> 匯編器生成目標文件(CPU可執行的二進制指令機器碼) -> 鏈接器連接目標文件生成可執行文件(操作系統可以加載執行的二進制文件)
這不算是軟件的分層結構,但是可以理解為一種通過分層來簡化復雜問題的思想。那么php語言可以認為是建立在C語言之上的層——其解釋器Zend引擎是用C語言實現。畢竟用php這樣的腳本語言編寫動態網頁要比用C語言方便得多。
當然還有我們最熟悉的MVC分層技術了,后面會做詳細介紹。
那么分層的好處想必大家都已經比較熟悉,這是一種 “分而治之 大而化小” 的思想。說到分層就不得不提模塊了,其實分層和模塊是從兩種維度來進行“分而治之”的方式。模塊是從橫向維度來將一個整體分割成若干個獨立的部分,每個部分行使獨立自己的職責,當然它們之間也可能有依賴關系,這通過其對外提供的服務來實現。如果將整個系統比做中國版圖的話,那么模塊方式將中國分為省,自治區等。 分層則是從縱向緯度將一個整體從高至低劃分為若干個獨立的層,一個完整的服務由底層至上層,層層傳遞最終產出。分層和模塊可以同時運用,例如中國用模塊方式分為省之后,然后每個省的行政機構利用分層方式來行使職責,從低到高有戶,村,鄉,區,市,省等等,每一層都向上一層匯報。 分層和模塊會提高系統復雜度并影響效率(戶不能直接向省匯報,而需要一級一級向上匯報),但是這樣有利系統的擴展和維護,每一層只需要關注自己提供的服務接口以及它下一層所提供的服務接口,試想一下如果省需要接受來自市,區,鄉,村等所有下級層的匯報,那些省干部會很頭疼的。
2. HTTP服務傳統的三層架構MVC
HTTP服務中最經典的分層架構非MVC莫屬了, 幾乎任何一個php開發框架都是支持MVC分層模式,此模式歷史也比較悠久,是在上個世紀八十年代為編程語言Smalltalk設計的軟件模式,至今已經被廣泛引用。
這里引用了百度百科中的圖片:
那么關于MVC的優點我不做介紹,搜索引擎中能找到大量相關資料。
本文的標題是HTTP服務七層架構技術探討,比MVC多出了四層,這樣復雜的分層是否有必要呢?
關于這個問題仁者見仁智者見智,本人認為MVC分層粒度不夠精細,當然你也可以繼續堅持傳統的三層,那么后文你也沒必要看下去了。
那么為什么MVC分層不夠精細呢,在我曾經使用開源框架的MVC模式的經驗中發現,V和C層功能職責一般都很清晰穩定,但是M層卻常常顯得臃腫笨拙。
C層主要是負責整體流程控制,一般規范的架構中,流程都可以用一張或幾張流程圖畫出,那么表明流程一般都是固定的。
V層主要是負責頁面呈現,可能使用smarty模板引擎,也可能是自帶的模板引擎,顯示的頁面可能是HTML,XML或則JSON,這些種類再多也都是可以度量的,所以M層也可以說是固定的。
而M層卻關系到系統的業務邏輯,隨著系統不斷迭代更新,M層中的內容也會不斷演變,而這一層中也有很多復雜的處理,如文件讀取,數據庫查詢,緩存策略,邏輯運算,數據加工,數據打包等等。
所以MVC三層模型中,M層是還能再做細分的,當M層有一個更精細合理的分層方式之后,我們的業務邏輯演變過程會更加的得心應手。
3. 七層架構
由上面的介紹,那么我們對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服務。
4. 七層架構運用
現在運用這樣的架構來設計一個簡單的博客系統,服務端用php語言實現。當然,架構是思想,不區分語言。
整個系統包含以下功能
1. 發布博文 2.修改博文 3.刪除博文 4.評論博文 5.修改用戶信息
要求每個功能都記錄操作日志。
設計的數據存儲包括
1. 博文數據表 2.用戶數據表 3.評論數據表 4. 日志(存文件系統)
在表結構設計的時候我們加入了一些冗余字段信息,例如在博文表中有評論數量字段comment_nums, 博文每被評論一次其值加1,每刪除一個評論其值減1
用戶數據表中我們添加了用戶發布的博文數量字段blog_nums,用戶每發布一篇博文其值加1,每刪除一篇博文其值減1
下面設計分層:
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 用戶組件
提供方法包括
1> updateUser() 修改用戶信息
4. LogComponent 日志組件
提供方法包括
1>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
現在以發布博文操作來介紹流程。假設接口地址為: http://www.xxxxx.com/postBlog:
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. 考慮轉移一篇博文涉及到的操作 1.將博文表中對應的用戶ID字段由A的ID切換到B的ID;2.A用戶的博文數量減1;3.B用戶的博文數量加1。
這三個操作需要兩個組件來完成,這兩個組件我們系統中已經有了,BlogComponent的updateBlog ()完成操作1,UserComponent的updateUser()完成操作2,3。
從用例2可以看出,當新增了這么一個需求的時候,我們在應用層和業務層添加了實例,組件層以下都不變,這是因為現在的組件層已經能夠滿足新的業務的需求,當我們現有組件無法滿足新的一業務需求的時候,我們則需要對組件層做修改。
通過這兩個簡單的用例我們發現我們對系統的修改要么可以很明確的確定在哪些層,要么就是從上層組件往下層進行,操作起來很方便。
5. 小結
這種縱向的分層和橫向的模塊結合起來,能讓整個系統的結構清晰流暢,在本人設計的一款框架里面,原生的支持這樣的分層架構和模塊,使用者只需要按照同一個模式簡單的操作,層之間的接口和協議已經由框架本身約定好,框架還不夠完善,自己內部用用可以,暫時不發布了。
it知識庫:HTTP服務七層架構技術探討,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。