第四章:容器初探
接触JAVAEE以来,最初对“容器”一词满头雾水、无比崇拜,后来听到耳朵长茧,一直觉得这个词的定义有点太广了,很多情况下不管沾没沾点关系的都往上靠,力图通过此术语使自己显得“专业”一些(老实说我写文档也这么做过)。但不论如何,发明这个计算机术语的人还是相当牛的,充分体现了JAVAEE“分层”的思想。
唯一不爽的是,一直以来都处于“容器”的黑盒之外,更加上那些大厂商对自己的JAVAEE“容器”产品的神乎其神的吹嘘宣传,一直没法想象外国那些鬼佬怎么就这么牛能做出这么厉害的东西,我们只有乖乖使用的份?还好有开源,还好有这本《How Tomcat Works》,可以满足我的好奇心,一窥“容器”的奥秘
Tomcat的容器架构
我们一般都把tomcat、weblogic、websphere app server和JBOSSAS称为“J2EE容器”,是一种广义的说法;而这里的“容器”,指的是tomcat中的两大组件之一的“容器”,属于狭义的说法(另一种组件当然就是Connector了)。
tomcat的容器架构,一直都没有太大变化,基本元素都是四个接口:
engine:表示一整个Catalina Servlet引擎
host:表示一个虚拟主机。什么是虚拟主机可以百度一下“tomcat 虚拟主机配置”
Context:表示一个web app应用,比如你做的一个网站
Wrapper:表示单个Servlet
以上四个基本元素由上至下逐渐细分,成树状结构,构成了tomcat容器结构的主体,它们都位于org.apache.catalina包
值得一提的是,这四个接口并不是同时必须的,例如,你完全可以做一个只有Wrapper的“迷你版”tomcat,这在一些资源受限的环境中,比如嵌入式系统很有用(说不定将来能放到手机里面跑,O(∩_∩)O哈哈~)
类图如下:
一般来说,容器里头都还有session管理、日志等功能,不过这一章暂时还不作讨论。
PipeLine & Valve
熟悉Servlet的人一定接触过Servletfilter,在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,坚持每周看一章~!
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com