|
1. 為什么分層?
計(jì)算機(jī)領(lǐng)域的體系結(jié)構(gòu)普遍采用了分層的方式。
從整體結(jié)構(gòu)來(lái)看:
從最底層的硬件往高層依次有:
操作系統(tǒng) -> 驅(qū)動(dòng)程序 -> 運(yùn)行庫(kù) -> 系統(tǒng)程序 -> 應(yīng)用程序等等。
從網(wǎng)絡(luò)分層模型OSI來(lái)講,由上至下為:
應(yīng)用層 -> 表示層 -> 會(huì)話層 -> 傳輸層 -> 網(wǎng)絡(luò)層 -> 數(shù)據(jù)鏈路層 -> 物理層
當(dāng)然實(shí)際應(yīng)用的TCP/IP協(xié)議的分層就沒OSI標(biāo)準(zhǔn)這么復(fù)雜。
從C語(yǔ)言文件編寫到生成可執(zhí)行文件的過(guò)程來(lái)看:
預(yù)處理(展開后的C語(yǔ)言代碼) -> 編譯成匯編語(yǔ)言(特定CPU體系結(jié)構(gòu)的匯編語(yǔ)言源文件) -> 匯編器生成目標(biāo)文件(CPU可執(zhí)行的二進(jìn)制指令機(jī)器碼) -> 鏈接器連接目標(biāo)文件生成可執(zhí)行文件(操作系統(tǒng)可以加載執(zhí)行的二進(jìn)制文件)
這不算是軟件的分層結(jié)構(gòu),但是可以理解為一種通過(guò)分層來(lái)簡(jiǎn)化復(fù)雜問(wèn)題的思想。那么php語(yǔ)言可以認(rèn)為是建立在C語(yǔ)言之上的層——其解釋器Zend引擎是用C語(yǔ)言實(shí)現(xiàn)。畢竟用php這樣的腳本語(yǔ)言編寫動(dòng)態(tài)網(wǎng)頁(yè)要比用C語(yǔ)言方便得多。
當(dāng)然還有我們最熟悉的MVC分層技術(shù)了,后面會(huì)做詳細(xì)介紹。
那么分層的好處想必大家都已經(jīng)比較熟悉,這是一種 “分而治之 大而化小” 的思想。說(shuō)到分層就不得不提模塊了,其實(shí)分層和模塊是從兩種維度來(lái)進(jìn)行“分而治之”的方式。模塊是從橫向維度來(lái)將一個(gè)整體分割成若干個(gè)獨(dú)立的部分,每個(gè)部分行使獨(dú)立自己的職責(zé),當(dāng)然它們之間也可能有依賴關(guān)系,這通過(guò)其對(duì)外提供的服務(wù)來(lái)實(shí)現(xiàn)。如果將整個(gè)系統(tǒng)比做中國(guó)版圖的話,那么模塊方式將中國(guó)分為省,自治區(qū)等。 分層則是從縱向緯度將一個(gè)整體從高至低劃分為若干個(gè)獨(dú)立的層,一個(gè)完整的服務(wù)由底層至上層,層層傳遞最終產(chǎn)出。分層和模塊可以同時(shí)運(yùn)用,例如中國(guó)用模塊方式分為省之后,然后每個(gè)省的行政機(jī)構(gòu)利用分層方式來(lái)行使職責(zé),從低到高有戶,村,鄉(xiāng),區(qū),市,省等等,每一層都向上一層匯報(bào)。 分層和模塊會(huì)提高系統(tǒng)復(fù)雜度并影響效率(戶不能直接向省匯報(bào),而需要一級(jí)一級(jí)向上匯報(bào)),但是這樣有利系統(tǒng)的擴(kuò)展和維護(hù),每一層只需要關(guān)注自己提供的服務(wù)接口以及它下一層所提供的服務(wù)接口,試想一下如果省需要接受來(lái)自市,區(qū),鄉(xiāng),村等所有下級(jí)層的匯報(bào),那些省干部會(huì)很頭疼的。
2. HTTP服務(wù)傳統(tǒng)的三層架構(gòu)MVC
HTTP服務(wù)中最經(jīng)典的分層架構(gòu)非MVC莫屬了, 幾乎任何一個(gè)php開發(fā)框架都是支持MVC分層模式,此模式歷史也比較悠久,是在上個(gè)世紀(jì)八十年代為編程語(yǔ)言Smalltalk設(shè)計(jì)的軟件模式,至今已經(jīng)被廣泛引用。
這里引用了百度百科中的圖片:
那么關(guān)于MVC的優(yōu)點(diǎn)我不做介紹,搜索引擎中能找到大量相關(guān)資料。
本文的標(biāo)題是HTTP服務(wù)七層架構(gòu)技術(shù)探討,比MVC多出了四層,這樣復(fù)雜的分層是否有必要呢?
關(guān)于這個(gè)問(wèn)題仁者見仁智者見智,本人認(rèn)為MVC分層粒度不夠精細(xì),當(dāng)然你也可以繼續(xù)堅(jiān)持傳統(tǒng)的三層,那么后文你也沒必要看下去了。
那么為什么MVC分層不夠精細(xì)呢,在我曾經(jīng)使用開源框架的MVC模式的經(jīng)驗(yàn)中發(fā)現(xiàn),V和C層功能職責(zé)一般都很清晰穩(wěn)定,但是M層卻常常顯得臃腫笨拙。
C層主要是負(fù)責(zé)整體流程控制,一般規(guī)范的架構(gòu)中,流程都可以用一張或幾張流程圖畫出,那么表明流程一般都是固定的。
V層主要是負(fù)責(zé)頁(yè)面呈現(xiàn),可能使用smarty模板引擎,也可能是自帶的模板引擎,顯示的頁(yè)面可能是HTML,XML或則JSON,這些種類再多也都是可以度量的,所以M層也可以說(shuō)是固定的。
而M層卻關(guān)系到系統(tǒng)的業(yè)務(wù)邏輯,隨著系統(tǒng)不斷迭代更新,M層中的內(nèi)容也會(huì)不斷演變,而這一層中也有很多復(fù)雜的處理,如文件讀取,數(shù)據(jù)庫(kù)查詢,緩存策略,邏輯運(yùn)算,數(shù)據(jù)加工,數(shù)據(jù)打包等等。
所以MVC三層模型中,M層是還能再做細(xì)分的,當(dāng)M層有一個(gè)更精細(xì)合理的分層方式之后,我們的業(yè)務(wù)邏輯演變過(guò)程會(huì)更加的得心應(yīng)手。
3. 七層架構(gòu)
由上面的介紹,那么我們對(duì)MVC中的M層再進(jìn)行分層規(guī)劃,我這里給出的是一種對(duì)M層分五層的方式,讀者如果覺得五層太多或則太少那么可以參考這個(gè)再進(jìn)行規(guī)劃。
原來(lái)的M層被分為:
A層:Application 應(yīng)用層
B層:Business 業(yè)務(wù)層
C層:Component 組件層
D層:Datadriver 數(shù)據(jù)驅(qū)動(dòng)層
S層:Systemdriver 系統(tǒng)驅(qū)動(dòng)層
那么整個(gè)七層架構(gòu)則為:
1. Controller
2. View
3. Application
4. Business
5. Component
6. Datadriver
7. Systemdriver
結(jié)構(gòu)圖還是參考經(jīng)典MVC,將其中M層換成新的五層即可。
現(xiàn)在依次介紹這幾個(gè)新的層:
1. Application
應(yīng)用層在最上面,其針對(duì)實(shí)際中的單個(gè)頁(yè)面或則單個(gè)接口。Controller通過(guò)HTTP請(qǐng)求地址中的參數(shù)找到對(duì)應(yīng)的Application,然后執(zhí)行中指定的公共方法,比如main(),然后應(yīng)用就開始啟動(dòng)。應(yīng)用層的職責(zé)包括接受HTTP參數(shù)(一般是間接接受,比如從request對(duì)象中獲取),調(diào)用Business層的特定業(yè)務(wù),保存業(yè)務(wù)執(zhí)行結(jié)果,這些結(jié)果最終會(huì)由View顯示出來(lái),當(dāng)然是通過(guò)Controller協(xié)調(diào)。應(yīng)用層是M層分解成五層之后最高的層,Controller會(huì)與此層直接通信。
2. Business
業(yè)務(wù)層在應(yīng)用層之下,通常一個(gè)應(yīng)用實(shí)例對(duì)應(yīng)一個(gè)業(yè)務(wù)實(shí)例,而一個(gè)業(yè)務(wù)有可能為多個(gè)應(yīng)用服務(wù),業(yè)務(wù)是一個(gè)執(zhí)行流,它通過(guò)執(zhí)行一系列的操作來(lái)完成應(yīng)用的需求。這些操作來(lái)自下層的組件層Component,可能一個(gè)業(yè)務(wù)需要一個(gè)或則多個(gè)組件來(lái)完成一個(gè)完整的需求。因?yàn)橐粋€(gè)業(yè)務(wù)實(shí)例通常只對(duì)應(yīng)一個(gè)功能,所以只有一個(gè)固定的方法會(huì)被上層的應(yīng)用調(diào)用,比如flow()。業(yè)務(wù)層的職責(zé)是幫應(yīng)用層執(zhí)行業(yè)務(wù)流并且有必要的時(shí)候返回?cái)?shù)據(jù)給應(yīng)用層,它會(huì)調(diào)用下層Component的方法。
3. Component
從組件層開始和上面兩層有一個(gè)本質(zhì)的區(qū)別,組件層開始有了類庫(kù)的概念。 前面兩層的實(shí)例通常只暴露一個(gè)特殊約定的公共的方法讓上層調(diào)用,從這一層開始一個(gè)實(shí)例會(huì)提供多個(gè)方法給上層。組件層通常和系統(tǒng)中一個(gè)角色對(duì)應(yīng),例如在博客系統(tǒng)中,博文是一個(gè)角色,用戶是一個(gè)角色,那么就會(huì)有博文組件BlogComponent,用戶組件UserComponent,每個(gè)角色都有對(duì)應(yīng)的操作,例如博文和用戶都可以添加刪除修改。
需要注意組件層中不應(yīng)該有任何數(shù)據(jù)讀取的操作,數(shù)據(jù)讀取是下層數(shù)據(jù)驅(qū)動(dòng)層來(lái)做的。如果組件層從下層獲取了數(shù)據(jù),那么它的一個(gè)職責(zé)就是對(duì)數(shù)據(jù)進(jìn)行加工。例如BlogComponent有一個(gè)方法是獲取一個(gè)博文getBlog($id),那么getBlog()方法中,從數(shù)據(jù)驅(qū)動(dòng)層中取得了對(duì)應(yīng)id的博文數(shù)據(jù)之后,需要對(duì)博文數(shù)據(jù)進(jìn)行一定的處理,比如將博文中的HTML特殊標(biāo)簽過(guò)濾等等。組件層不關(guān)心數(shù)據(jù)的讀取方式,但是會(huì)關(guān)心數(shù)據(jù)的結(jié)果,比如數(shù)據(jù)不存在或則數(shù)據(jù)已經(jīng)過(guò)期。
4. Datadriver
數(shù)據(jù)驅(qū)動(dòng)層的職責(zé)是為組件層提供源數(shù)據(jù),此層關(guān)心數(shù)據(jù)的存取介質(zhì),存取方式等等。數(shù)據(jù)可能被存儲(chǔ)在DB,MC,F(xiàn)ilesystem或則遠(yuǎn)程的HTTP服務(wù)器上。數(shù)據(jù)驅(qū)動(dòng)層不關(guān)心數(shù)據(jù)的內(nèi)容,只關(guān)心數(shù)據(jù)讀取的操作結(jié)果,例如假設(shè)數(shù)據(jù)存在DB中,但是數(shù)據(jù)驅(qū)動(dòng)層在執(zhí)行數(shù)據(jù)庫(kù)查詢的時(shí)候出錯(cuò)了,那么需要在此層處理。 假設(shè)數(shù)據(jù)存儲(chǔ)在遠(yuǎn)程的HTTP服務(wù)器上,那么數(shù)據(jù)驅(qū)動(dòng)層需要關(guān)心HTTP返回碼是否為正確的200系列或則錯(cuò)誤的400,500系列,哪怕HTTP請(qǐng)求返回了錯(cuò)誤的數(shù)據(jù)實(shí)體,但是返回碼為200,那么數(shù)據(jù)驅(qū)動(dòng)層也不關(guān)心,這種情況需要上層組件層來(lái)處理。
5. Systemdriver
系統(tǒng)驅(qū)動(dòng)層是系統(tǒng)環(huán)境提供的數(shù)據(jù)訪問(wèn)實(shí)例,例如數(shù)據(jù)庫(kù)服務(wù)的Systemdriver可能是一個(gè)db handler,HTTP服務(wù)的Systemdriver可能是一個(gè)http handler,文件存儲(chǔ)系統(tǒng)驅(qū)動(dòng)層可能是一個(gè)file handler, 系統(tǒng)驅(qū)動(dòng)層相對(duì)簡(jiǎn)單,這層可以和數(shù)據(jù)驅(qū)動(dòng)層進(jìn)行合并,其職責(zé)也較少。僅僅只是執(zhí)行數(shù)據(jù)驅(qū)動(dòng)層的數(shù)據(jù)訪問(wèn)指令。
通常情況下這五個(gè)層中,上層的實(shí)例數(shù)量比下層的實(shí)例數(shù)量要多, 整體類似一個(gè)倒置的梯形:
在上圖中一共有6個(gè)Application,5個(gè)Business,4個(gè)Component,3個(gè)Datadriver,2個(gè)Systemdriver。
每個(gè)Application都由一個(gè)Business為其服務(wù)。
每個(gè)Business都服務(wù)一個(gè)或則多個(gè)Application(B5同時(shí)服務(wù)A5 A6),都有一個(gè)或則多個(gè)CompoNET為其服務(wù)。
每個(gè)Component為一個(gè)或則多個(gè)Business服務(wù),都有一個(gè)或則多個(gè)Datadriver為其服務(wù)。
每個(gè)Datadriver為一個(gè)或則多個(gè)Component服務(wù),都有一個(gè)或則多個(gè)Systemdriver為其服務(wù)。
每個(gè)Systemdriver為一個(gè)或則多個(gè)Datadriver服務(wù)。
4. 七層架構(gòu)運(yùn)用
現(xiàn)在運(yùn)用這樣的架構(gòu)來(lái)設(shè)計(jì)一個(gè)簡(jiǎn)單的博客系統(tǒng),服務(wù)端用php語(yǔ)言實(shí)現(xiàn)。當(dāng)然,架構(gòu)是思想,不區(qū)分語(yǔ)言。
整個(gè)系統(tǒng)包含以下功能
1. 發(fā)布博文 2.修改博文 3.刪除博文 4.評(píng)論博文 5.修改用戶信息
要求每個(gè)功能都記錄操作日志。
設(shè)計(jì)的數(shù)據(jù)存儲(chǔ)包括
1. 博文數(shù)據(jù)表 2.用戶數(shù)據(jù)表 3.評(píng)論數(shù)據(jù)表 4. 日志(存文件系統(tǒng))
在表結(jié)構(gòu)設(shè)計(jì)的時(shí)候我們加入了一些冗余字段信息,例如在博文表中有評(píng)論數(shù)量字段comment_nums, 博文每被評(píng)論一次其值加1,每刪除一個(gè)評(píng)論其值減1
用戶數(shù)據(jù)表中我們添加了用戶發(fā)布的博文數(shù)量字段blog_nums,用戶每發(fā)布一篇博文其值加1,每刪除一篇博文其值減1
下面設(shè)計(jì)分層:
1. PostBlogApplication 發(fā)布博文
2. UpdateBlogApplication 修改博文
3. DeleteBlogApplication 刪除博文
4. CommentBlogApplication 評(píng)論博文
5. UpdateUserApplication 修改用戶信息
業(yè)務(wù)層:這5個(gè)應(yīng)用分別有5個(gè)業(yè)務(wù)對(duì)其服務(wù)
1. PostBlogBusiness 博文發(fā)布業(yè)務(wù)
2. UpdateBlogBusiness 博文修改業(yè)務(wù)
3. DeleteBlogBusiness 博文刪除業(yè)務(wù)
4. CommentBlogBusiness 博文評(píng)論業(yè)務(wù)
5. UpdateUserBusiness 用戶修改業(yè)務(wù)
組件層:系統(tǒng)一共有4個(gè)角色對(duì)應(yīng)4個(gè)組件
1. BlogComponent 博文組件
提供方法包括
1> postBlog() 發(fā)布博文
2> deleteBlog()刪除博文
3> updateBlog()修改博文
4>getBlog()獲取博文內(nèi)容
2. CommentComponent 評(píng)論組件
提供方法包括
1>postComment() 發(fā)布評(píng)論
2> deleteComment() 刪除評(píng)論
3. UserComponent 用戶組件
提供方法包括
1> updateUser() 修改用戶信息
4. LogComponent 日志組件
提供方法包括
1>logMsg() 記錄日志信息
數(shù)據(jù)驅(qū)動(dòng)層:和4個(gè)組件對(duì)應(yīng)
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
系統(tǒng)驅(qū)動(dòng)層: DB類型和FS類型
1. MySqlSystemdriver DB的handler
2. FileSystemdriver FS的handler
現(xiàn)在以發(fā)布博文操作來(lái)介紹流程。假設(shè)接口地址為: http://www.xxxxx.com/postBlog:
1. Controller通過(guò)重寫規(guī)則發(fā)現(xiàn)其對(duì)應(yīng)的Application為PostBlogApplication,于是PostBlogApplication被實(shí)例化,并且其中的特殊方法main()會(huì)被自動(dòng)調(diào)用。
2. postBlogApplication需要PostBlogBusiness業(yè)務(wù)來(lái)完成博文發(fā)布操作,PostBlogBusiness被實(shí)例化,并且其中的特殊方法flow()會(huì)被調(diào)用。
3. 根據(jù)需求,發(fā)布博文的時(shí)候需要在博文表中插入一條博文,然后修改用戶信息中的博文數(shù)量字段。那么postBlogBusiness業(yè)務(wù)流就包括兩個(gè)操作,這兩個(gè)操作分別由BlogComponent中的postBlog()方法和UserComponent 中的updateUser()方法來(lái)實(shí)現(xiàn),其中前者往博文表中插入博文信息,后者將用戶信息中的博文數(shù)量字段加1。由于系統(tǒng)要求任何操作都需要記錄日志,因此還有第三個(gè)操作就是記錄日志,通過(guò)BlogComponent的logMsg()方法實(shí)現(xiàn)。那么PostBlogBusiness業(yè)務(wù)流一共包括了三個(gè)操作,分別由三個(gè)組件來(lái)完成。
4. 下面就需要分別考慮上面三個(gè)組件的下層調(diào)用了
其中BlogCompoNET的postBlog()調(diào)用BlogDatadriver的insert相關(guān)方法來(lái)插入博文數(shù)據(jù),BlogDatadriver是DB類型,因此通過(guò)MySqlSystemdriver 來(lái)實(shí)現(xiàn)。
UserComponent的updateUser()調(diào)用UserDatadriver的update相關(guān)方法來(lái)實(shí)現(xiàn)博文數(shù)量更新,UserDatadriver也是DB類型,因此也通過(guò)MySqlSystemdriver 來(lái)實(shí)現(xiàn)。
LogComponent的logMsg()調(diào)用LogDatadriver的write相關(guān)方法,LogDatadriver是FS類型,因此通過(guò)FileSystemdriver 來(lái)實(shí)現(xiàn)。
5. 三個(gè)組件的操作都執(zhí)行成功后,PostBlogBusiness告訴postBlogApplication博文發(fā)布成功,然后postBlogApplication通過(guò)Controller來(lái)調(diào)用View相關(guān)的方法顯示執(zhí)行結(jié)果。
其他幾個(gè)操作流程讀者可以舉一反三,這里不再多介紹了。
那么現(xiàn)在我們通過(guò)幾個(gè)系統(tǒng)功能的演變用例來(lái)看看這個(gè)分層帶來(lái)的益處。
用例1:為了方便對(duì)日志的管理,現(xiàn)在希望能夠?qū)⑷罩?a href=/pingce/cunchu/ target=_blank class=infotextkey>存儲(chǔ)在DB中而不是FS中。
解決方法:這是對(duì)數(shù)據(jù)存儲(chǔ)的改造,我們知道應(yīng)該從數(shù)據(jù)驅(qū)動(dòng)層入手。日志功能是由日志組件LogComponent實(shí)現(xiàn)的, 其中LogCompoNET的了logMsg()方法調(diào)用LogDatadriver 來(lái)存日志。我們將LogDatadriver由FS類型改造成DB類型,接口方法保持不變,這樣很快就完成了改造。
用例2:新增需求-用戶A可以將一篇博文轉(zhuǎn)移給另外一個(gè)用戶B。
解決方法:
步驟1. 首先這個(gè)新需求對(duì)應(yīng)了一個(gè)新的應(yīng)用,于是我們新增了一個(gè)SendBlogApplication。
步驟2. 需要有一個(gè)業(yè)務(wù)完成操作,新增業(yè)務(wù)為SendBlogBusiness。
步驟3. 考慮轉(zhuǎn)移一篇博文涉及到的操作 1.將博文表中對(duì)應(yīng)的用戶ID字段由A的ID切換到B的ID;2.A用戶的博文數(shù)量減1;3.B用戶的博文數(shù)量加1。
這三個(gè)操作需要兩個(gè)組件來(lái)完成,這兩個(gè)組件我們系統(tǒng)中已經(jīng)有了,BlogComponent的updateBlog ()完成操作1,UserComponent的updateUser()完成操作2,3。
從用例2可以看出,當(dāng)新增了這么一個(gè)需求的時(shí)候,我們?cè)?a href=/pingce/yingyong/ target=_blank class=infotextkey>應(yīng)用層和業(yè)務(wù)層添加了實(shí)例,組件層以下都不變,這是因?yàn)楝F(xiàn)在的組件層已經(jīng)能夠滿足新的業(yè)務(wù)的需求,當(dāng)我們現(xiàn)有組件無(wú)法滿足新的一業(yè)務(wù)需求的時(shí)候,我們則需要對(duì)組件層做修改。
通過(guò)這兩個(gè)簡(jiǎn)單的用例我們發(fā)現(xiàn)我們對(duì)系統(tǒng)的修改要么可以很明確的確定在哪些層,要么就是從上層組件往下層進(jìn)行,操作起來(lái)很方便。
5. 小結(jié)
這種縱向的分層和橫向的模塊結(jié)合起來(lái),能讓整個(gè)系統(tǒng)的結(jié)構(gòu)清晰流暢,在本人設(shè)計(jì)的一款框架里面,原生的支持這樣的分層架構(gòu)和模塊,使用者只需要按照同一個(gè)模式簡(jiǎn)單的操作,層之間的接口和協(xié)議已經(jīng)由框架本身約定好,框架還不夠完善,自己內(nèi)部用用可以,暫時(shí)不發(fā)布了。
it知識(shí)庫(kù):HTTP服務(wù)七層架構(gòu)技術(shù)探討,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。