关于pipeline
最近要將系統(tǒng)改造成pipeline架構(gòu),所以研究了一下pipeline相關(guān)的理論,servlet filter, struts2 interceptor, spring mvc interceptor, webx pipeline和netty pipeline的一些源碼。一些總結(jié):
結(jié)構(gòu):
Pipeline的類模型由Pipeline, Valve 和 Context 組成。 Pipeline代表一個執(zhí)行流,Valve代表執(zhí)行流中的一個節(jié)點,Context是執(zhí)行時的上下文信息,它一般由兩部分組成: request/response + 當前流的執(zhí)行狀態(tài)。
有的系統(tǒng)只能有一個Pipeline, 有的則允許配多個。 在struts2中,一種interceptor的組合,就代表了一個Pipeline;多種組合,意味著多個Pipeline.
用法:
有的pipeline流是在同一層次上,每個valve處理的request/response對象基本上是同質(zhì)的。比如servlet filter和各種mvc框架,所處理的事情都是web層的,所處理的context對象就是http request或框架自定義的javabean.
有的pipeline流則是縱向的,從上層流到下層,或從下層流到上層,這時每個valve所處理的request/response對象一般是異構(gòu)的。協(xié)議棧就是個典型的例子:下層valve處理字節(jié)流,上層Valve處理字符流。 不過,為了適應pipeline架構(gòu),這些異構(gòu)的context對象必須有共同的基類,并且把這個基類作為各個valve的輸入?yún)?shù)。
從請求處理的角度來說, Pipeline可以代表整個執(zhí)行流,也可以只用作請求被最終處理前的Interceptor. Webx, netty中的Pipeline會將最后一個Valve作為請求處理者(一般稱為Request Handler),而servlet filter和struts2 interceptor只作請求攔截、加工,真正的請求處理者則是servlet和action.
程序流:
一個完整的Pipeline執(zhí)行流一般是個環(huán)路: Valve1 => Valve2 => Valve3 => Valve2 => Valve1. 從攔截的角度說,valve既是前置攔截(下一個valve執(zhí)行前),又是后置拉截(下一個 valve執(zhí)行后)。
為了達到前后雙攔截的目標,程序?qū)崿F(xiàn)有兩種方法。 有一種方法是使用forEach方式分別執(zhí)行每個攔截器的前置攔截方法,然后又以相反的順序分別執(zhí)行每個攔截器的后置攔截方法。這種做法比較直觀,可以說直接映射了人類的思考方式。 Spring MVC Interceptor差不多就是這種做法。
另一種做法,也是主流的作法,則會把一個valve的執(zhí)行嵌套在前一個valve的執(zhí)行里面: 每個valve的執(zhí)行中代碼中會有一句"nextValve.invoke()",然后在這句代碼的前后做一些攔截。這種方式有點費解,而且由于嵌套過多容易造成比較深的調(diào)用棧。 不過它有一個好處: 更方便地中斷執(zhí)行流并處理異常(下詳)。
執(zhí)行流須提供中斷機制和異常處理機制,比如一個鑒權(quán)filter在發(fā)現(xiàn)用戶未登錄時需要立即返回403并中斷流,當一個valve出現(xiàn)異常時這個異常需要被捕捉、處理。 如果Valve是嵌套執(zhí)行的,這些機制會很方便實現(xiàn): 鑒權(quán)filter發(fā)現(xiàn)用戶未登錄時,不調(diào)用nextValve.invoke()即可中斷pipeline; 至于異常處理,Struts2默認使用ExceptionMappingInterceptor作為Pipeline中的第1個valve, 它會把nextIntercetpor.invoke()放在自己的try/catch塊中。如果valve不是嵌套執(zhí)行,而是由pipeline通過forEach編排執(zhí)行的,那么這些非正常流的“扳道工”職責就必須由pipeline自己來承擔, 這可能會使pipeline不夠精簡;如果來了新的邏輯(比如異步執(zhí)行),又得改pipeline,導致pipeline不太穩(wěn)定。
設(shè)計模式:
狀態(tài)/無狀態(tài), 單例/非單例. 如果不怕麻煩,可以每來一個請求,就生成一個Pipeline對象和一堆Valve對象,再把request和上下文信息用作這些對象的狀態(tài);但是,用單例的風格更符合當代程序員的習慣,也更容易與spring結(jié)合。 那么,全單例并且都是無狀態(tài)? 不盡然。雖然Pipeline的結(jié)構(gòu)本身是靜態(tài)的,一個pipeline結(jié)構(gòu)可以作成一個單例;但Pipeline的執(zhí)行流是有狀態(tài)的,比如當前執(zhí)行到了哪個valve,是需要一個變量來維護的。 所以,可以做一個單例的對象Pipeline, 代表Pipeline的結(jié)構(gòu);另做一個對象PipelineContext,代表Pipeline當前的執(zhí)行流;至于Valve,它相當于一個stateless service, 做成單例即可.
轉(zhuǎn)載于:https://www.cnblogs.com/wdmx/p/9895015.html
總結(jié)
以上是生活随笔為你收集整理的关于pipeline的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL------Hint
- 下一篇: 【sql】牛客网练习题 (共 61 题)