tomcat架構分析(valve機制)
關於tomcat的內部邏輯單元的存儲空間已經在相關容器類的blog里闡述了。在每個容器對象里面都有一個pipeline及valve模塊。 它們是容器類必須具有的模塊。在容器對象生成時自動產生。Pipeline就像是每個容器的邏輯總線。在pipeline上按照配置的順序,加載各個 valve。通過pipeline完成各個valve之間的調用,各個valve實現具體的應用邏輯。
先看一下pipeline及valve的邏輯概念圖。

這些valve就是在tomcat的server.xml中配置,只要滿足一定條件,繼承ValveBase基類
引用
org.apache.catalina.valves.ValveBase
就可以在不同的容器中配置,然后在消息流中被逐一調用。每個容器的valve的作用域不一樣,在總體結構中已有說明。這里紅色標記的是配置的自定義的valve,這樣可以擴展成多個其他應用,例如cluster應用等。
Tomcat實現
Tomcat提供了Pipeline的標准實現:
引用
org.apache.catalina.core.StandardPipeline
四大容器類StandardEngine,StandardHost,StandardContext及StandardWrapper都有各自缺省的標准valve實現。它們分別是
- Engine:org.apache.catalina.core.StandardEngineValve
- Host: org.apache.catalina.core.StandardHostValve
- Context:org.apache.catalina.core.StandardContextValve
- Wrapper:org.apache.catalina.core.StandardWrapperValve
容器類生成對象時,都會生成一個pipeline對象,同時,生成一個缺省的valve實現,並將這個標准的valve對象綁定在其pipeline對象上。以StandardHost類為例:
- public class StandardHost extends ContainerBase implements Host {
- protected Pipeline pipeline = new StandardPipeline(this);
- public StandardHost() {
- super();
- pipeline.setBasic(new StandardHostValve());
- }
- }
Valve實現了具體業務邏輯單元。可以定制化valve(實現特定接口),然后配置在server.xml里。每層容器都可以配置相應的 valve,當只在其作用域內有效。例如engine容器里的valve只對其包含的所有host里的應用有效。定制化的valve是可選的,但是每個容 器有一個缺省的valve,例如engine的StandardEngineValve,是在StandardEngine里自帶的,它主要實現了對其子 host對象的StandardHostValve的調用,以此類推。
配置的例子有:
- <Engine name="Catalina" defaultHost="localhost">
- <Valve className="MyValve0"/>
- <Valve className="MyValve1"/>
- <Valve className="MyValve2"/>
- ……
- <Host name="localhost" appBase="webapps">
- </Host>
- </Engine>
當在server.xml文件中配置了一個定制化valve時,會調用pipeline對象的addValve方法,將valve以鏈表方式組織起來,看一下代碼;
- public class StandardPipeline implements Pipeline, Contained, Lifecycle{
- protected Valve first = null;
- public void addValve(Valve valve) {
- // Validate that we can add this Valve
- if (valve instanceof Contained)
- ((Contained) valve).setContainer(this.container);
- // Start the new component if necessary
- if (started) {
- if (valve instanceof Lifecycle) {
- try {
- ((Lifecycle) valve).start();
- } catch (LifecycleException e) {
- log.error("StandardPipeline.addValve: start: ", e);
- }
- }
- // Register the newly added valve
- registerValve(valve);
- }
- // 將配置的valve添加到鏈表中,並且每個容器的標准valve在鏈表的尾端
- if (first == null) {
- first = valve;
- valve.setNext(basic);
- } else {
- Valve current = first;
- while (current != null) {
- if (current.getNext() == basic) {
- current.setNext(valve);
- valve.setNext(basic);
- break;
- }
- current = current.getNext();
- }
- }
- }
- }
從上面可以清楚的看出,valve按照容器作用域的配置順序來組織valve,每個valve都設置了指向下一個valve的next引用。同 時,每個容器缺省的標准valve都存在於valve鏈表尾端,這就意味着,在每個pipeline中,缺省的標准valve都是按順序,最后被調用。
消息流
先看一下四大容器的標准valve的調用邏輯圖。從中可以梳理出標准valve的邏輯。注意此圖只是在缺省配置下的狀態,也就是說每個pipeline只包含一個標准valve的情況。

圖中顯示的是各個容器默認的valve之間的實際調用情況。從StandardEngineValve開始,一直到 StandardWrapperValve,完成整個消息處理過程。注意每一個上層的valve都是在調用下一層的valve返回后再返回的,這樣每個上 層valve不僅具有request對象,同時還能拿到response對象,想象一下,這樣是不是可以批量的做很多東西?
有人問? : valve消息流中說的是每層只有一個valve的調用情況。如果每層有多個valve的情況下,消息流又是怎樣的呢?
回答 : 每一層有多個valve,以Engine層為例, 都是以這個順序 valve0,valve1,...StandardEngineValve進行調用,典型的責任鏈模式,各個valve之間根據一定的邏輯通過 getNext().invoke(request, response);調用下一個valve