《How Tomcat Works》读书笔记(四)

来源:互联网 发布:淘宝双十一销售额直播 编辑:程序博客网 时间:2024/04/28 07:46

第四章:容器初探

接触JAVA EE以来,最初对“容器”一词满头雾水、无比崇拜,后来听到耳朵长茧,一直觉得这个词的定义有点太广了,很多情况下不管沾没沾点关系的都往上靠,力图通过此术语使自己显得“专业”一些(老实说我写文档也这么做过)。但不论如何,发明这个计算机术语的人还是相当牛的,充分体现了JAVA EE“分层”的思想。

唯一不爽的是,一直以来都处于“容器”的黑盒之外,更加上那些大厂商对自己的JAVA EE“容器”产品的神乎其神的吹嘘宣传,一直没法想象外国那些鬼佬怎么就这么牛能做出这么厉害的东西,我们只有乖乖使用的份?还好有开源,还好有这本《How Tomcat Works》,可以满足我的好奇心,一窥“容器”的奥秘

Tomcat的容器架构

我们一般都把tomcat、weblogic、websphere app server和JBOSS AS称为“J2EE容器”,是一种广义的说法;而这里的“容器”,指的是tomcat中的两大组件之一的“容器”,属于狭义的说法(另一种组件当然就是Connector了)。

tomcat的容器架构,一直都没有太大变化,基本元素都是四个接口:

  • engine:表示一整个Catalina Servlet引擎
  • host:表示一个虚拟主机。什么是虚拟主机可以百度一下“tomcat 虚拟主机配置”
  • Context:表示一个web app应用,比如你做的一个网站
  • Wrapper:表示单个Servlet

以上四个基本元素由上至下逐渐细分,成树状结构,构成了tomcat容器结构的主体,它们都位于org.apache.catalina包

值得一提的是,这四个接口并不是同时必须的,例如,你完全可以做一个只有Wrapper的“迷你版”tomcat,这在一些资源受限的环境中,比如嵌入式系统很有用(说不定将来能放到手机里面跑,O(∩_∩)O哈哈~)

类图如下:

image

 

一般来说,容器里头都还有session管理、日志等功能,不过这一章暂时还不作讨论。

PipeLine & Valve

熟悉Servlet的人一定接触过Servlet filter,在Servlet处理请求之前,先会由filter“过滤”一下。tomcat内部同样也有类似的东西,那就是Valve——阀门。而所有的Valve都是装在一个pipeline(管道)里头的,tomcat的开发者估计对水管工之类的活比较感兴趣。这些Valve的功能各异,你也可以自己开发然后放到tomcat的配置文件里面。

那具体是如何工作的呢?首先,Connector调用容器的invoke方法,把Request给容器,容器再把Request对象给自身的pipeline:

public void invoke(Request request, Response response) 
   throws IOException, ServletException { 
   pipeline.invoke(request, response);

   ... 
}

然后,在pipeline内部有个内部类ValveContext,它来管理所有的Valve。pipeline一般会调用ValveContext的invokeNext

public void invokeNext(Request request, Response response)

ValveContext又调用第N个Valve的invoke方法(N是一个计数器,记录调用到第几个Valve了),不过参数稍微有点不同

public void invoke(Request request, Response response, 
   ValveContext ValveContext) throws IOException, ServletException

它把自己作为参数传了进去给Valve,有什么用呢?其实很简单,看看Valve的invoke是怎么实现的

public void invoke(Request request, Response response, 
   ValveContext valveContext) throws IOException, ServletException { 
   // Pass the request and response on to the next valve in our pipeline 
   valveContext.invokeNext(request, response); 
   // now perform what this valve is supposed to do 
     ... 
}

秘密就在这里!Valve又回调了ValveContext的invokeNext,这样就相当于递归一样,把全部Valve都调用一遍。

仔细推敲会发现,每个Valve都是先调用ValveContext的invokeNext,然后才做自己的工作,所以“第一个”被pipeline调用的Valve,实际却是最后一个完成自己工作的,有点类似“压栈”操作,第一个Valve最先被压进去,却是最后一个从堆栈中弹出来的。如果不信,可以做个试验,眼见为实。

修改BasicValve

每个pipeline默认会有一个basicvalve,做一些基本工作,比如把Request传递给下一级子容器,或者把Request交给Servlet(Wrapper的basicvalve就是做这个的)。从pipeline的源码来看(书中的源码,未必是tomcat的源码),basicvalve是最后一个被调用的。

if (subscript < valves.length) {        valves[subscript].invoke(request, response, this); 
     } 
     else if ((subscript == valves.length) && (basic != null)) { 
       basic.invoke(request, response, this); 
     }

以上是ValveContext.invokeNext方法的一部分,basic就是指BasicValve,很明显是最后一个被加进去的。

我们在basicvalve的invoke方法第一行增加一个简单的输出,运行之后就会发现,basicvalve的输出在其他Valve的前面,可见上面的推断是正确的!

关于Wrapper的疑问

这一章最后是两个简单的程序:第一个只有Wrapper容器,另一个则由两个Wrapper包含在一个Context容器里组成。Wrapper和Context接口就不啰嗦了,在后续章节有专门的详细解说。但在这里,每个Wrapper对应一个Servlet,如果是个大项目,那里面的Servlet起码有几十个,很难想像有那么多的Wrapper在同时跑,会不会导致性能低下呢?也许要看完这本书才能找到答案了

keep moving,坚持每周看一章~!

0 0