/**
*作者:annegu
*日期:2009-06-24
*/
现在我们从connector.getContainer().getPipeline().getFirst().invoke(request, response)开始进入容器...
前面说到容器的时候,anne一直都只有说三个容器,engine, host, context。其实在context之下,还有一个容器,叫做wrapper,每个wapper包含了一个servlet,因此前文没有接触到servlet的时候,就暂时省略了。好了,现在我们知道了有这四个级别的容器。这四个容器素由上至下逐渐细分,形成树状结构,构成了tomcat容器结构的主体,它们都位于org.apache.catalina包内。
之前在第二部分tomcat的启动的时候,我们就看到了pipeline,pipeline是一种上级容器和下级容器进行沟通的管道,当上级容器种的request和response要被传递到下一个容器中去时,就必须通过这个管道,而value就像管道中的一个个阀门一样,给传递的request和response把把关,只有过了所有的阀门,才能被正确的传递到下一级容器中去。Tomcat中的pipeline/valve是标准的责任链模式,每个级别的容器中pipeline下都有配置value,每种类型的value专门负责做一项工作,比如验证Request的有效性、写日志等等。请求先到达第一个value,value会对其做一些工作,当工作做完后,将请求传递给下一个value。每个pipeline的最后都会有一个BasicValue(比如Engine的StandardEngineValue、Host的StanadrdHostValue),它负责寻找下一级容器的pipeline,并且将请求传递给下一级容器的pipeline中的value,这样一直传递下去直到真正的servlet。
在tomcat中,这个责任链真是最最标准,最最基础的责任链了,我们来看一下StandardPipeline中是怎样为pipeline添加value的:
public void addValve(Valve valve) {
if (valve instanceof Contained)
((Contained) valve).setContainer(this.container);
...// Start the new component if necessary
// Add this Valve to the set associated with this Pipeline
① 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();
}
}
}
First(代码①处)记录了这个pipeline关联着的第一个value,basic则是pipeline关联的最后一个value,执行完basic,就进入到下一级的容器中去了。
从上面的代码,我们可以看到,pipeline用了一种最基本的方法来维持这个value的链条,每个value都保持了一个下个value的引用。于是,我们就看到connector.getContainer().getPipeline().getFirst()就得到了engine中的第一个value。为什么呢?因为connector.getContainer()得到的是connector父节点(也就是service)中的engine容器,进而getPipeline()得到engine的pipeline,getFirst()得到engine的pipeline中的第一个value。
好了,下面我们来看看具体进入到了value,都做了些什么,我们来看一个AccessLogValue,很明显,是一个用来写日志的value。
public void invoke(Request request, Response response) throws IOException, ServletException {
if (started && getEnabled()) {
long t1 = System.currentTimeMillis();
② getNext().invoke(request, response);
long t2 = System.currentTimeMillis();
long time = t2 - t1;
StringBuffer result = new StringBuffer();
... //add log info into result
log(result.toString());
} else
getNext().invoke(request, response);
}
OK,很明显,value是通过getNext()方法来得到下一个责任链上的value的(代码②处)。那么当这个责任链到头了,进入到了最后一个value的话是怎么处理的呢?前面说了,每个容器的pipeline的责任链的末端都会有一个特殊的value,Engine的StandardEngineValue、Host的StanadrdHostValue,Context的StanadrdContextValue,Wrapper的StanadrdWrapperValue,这些叫做basicValue,对于容器来说,这些basicValue是一定会有的。我们就看一个StandardEngineValue:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
// Ask this Host to process this request
③ host.getPipeline().getFirst().invoke(request, response);
}
我们可以看到在basicValue中得到了下一级容器,并且调用了下级容器的pipeline中的first value(代码③处)。对于StanadrdHostValue,StanadrdContextValue和StanadrdWrapperValue来说,也都是类似的。BasicValue放在org.apache.catalina.core下,而其他的value都放在org.apache.catalina.value下面。
Ok,大家继续脑补一下,现在已经进入到StanadrdWrapperValue了。那根据我们对tomcat的了解下面应该做什么了呢?对了,接下来我们就要穿越过层层filter,进入servlet了。
我们看一下StanadrdWrapperValuede的invoke方法,为了看起来方便一点,anne就大刀阔斧的只截取了一点点我们需要关注的内容。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
...
④ servlet = wrapper.allocate();
// 下面开始创建filter链啦
⑤ ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
...
// 调用filter链
filterChain.doFilter(request.getRequest(),response.getResponse());
if (servlet != null) {
wrapper.deallocate(servlet);
}
}
因为一个wrapper是对应与一个servlet的,所以wrapper.allocate()就是得到它负责封装的那个servlet(代码④处)。
下面在代码⑤处,我们就要来创建filter chain了,anne照样把createFilterChain中的方法稍微提取了一下:
public ApplicationFilterChain createFilterChain
(ServletRequest request, Wrapper wrapper, Servlet servlet) {
ApplicationFilterChain filterChain = new ApplicationFilterChain();
filterChain.setServlet(servlet);
// 得到context的filter mapping
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// 遍历filterMaps,如果有符合这个servlet的filter就把它加到filter chain中
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
}
return (filterChain);
}
我们都知道filter chain也是采用的责任链模式,前面我们说到pipeline中的value也是采用的责任链模式,每个value都持有了下一个value的引用。我们可以看看ahuaxuan的《请问责任链真的是一种设计模式吗》这篇文章(http://ahuaxuan.iteye.com/blog/105825),这里面谈到了三种责任链的实现方式,filter chain就是这第三种潇洒版责任链。
前面pipeline的value链是通过引用的方式来形成一条隐形的链条,而这里,filterChain是真是存在的。我们只需要把这个链条上面的一个一个关节通过filterChain.addFilter()装上即可。这个filterChain中的每个关节都是一个FilterConfig对象,这个对象中包含了filter,context,initParameter等等。
链条组装完毕!
启动filterChain.doFilter()!
doFilter方法主要调用了internalDoFilter()。
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
...
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
⑥ if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
filter = filterConfig.getFilter();
...
filter.doFilter(request, response, this);
}
⑦ if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
servlet.service((HttpServletRequest) request,
(HttpServletResponse) response);
}
} else {
servlet.service(request, response);
}
}
代码⑥处的pos是filter chain上的一个标识位,表示现在执行的是哪一个filter。然后调用了filter的doFilter方法。我们来看一个例子,一个用来记录执行时间的filter。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (attribute != null)
request.setAttribute(attribute, this);
long startTime = System.currentTimeMillis();
⑧ chain.doFilter(request, response);
long stopTime = System.currentTimeMillis();
filterConfig.getServletContext().log
(this.toString() + ": " + (stopTime - startTime) +
" milliseconds");
}
首先把当前正在执行的这个filter作为一个attribute放到request中去,接下来我们可以看到在两个time之间,调用了chain.doFilter()(代码⑧处),Chain就是filter chain。这下又要回到internalDoFilter,pos又加了1,就变成执行filterChain的下一个filter了。
如果这个filter chain已经到头了(pos=n),那就进入代码⑦处,就表示request和response已经突破filter的重重阻拦,可以进入servlet了。因此,我们就可以调用wrapper内的servlet的service()方法了。自此进入servlet。这下我们知道了filter原来是这样执行的,它是一层包着一层,一直不断的向内层进发,当进入到最内层,就是servlet了。
好了,我们现在终于进入servlet的service方法了。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
...
doGet(req, resp);
} else if (method.equals(METHOD_HEAD)) {
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
...// NO servlet supports
}
}
Service方法里面其实很简单,就是根据不同的请求,调用不同的do***()方法。Http的请求类型一共有如上七种。
一个HttpServlet的子类必须至少覆写以下方法中的一个。
1) doGet()方法,适用于HTTP GET请求。自动支持一个HTTP HEAD请求。当覆写doGet()时,首先读取请求数据,写入响应的head,然后获得响应的writer或输出流对象,最后写入响应数据。
2) doPost()方法,适用于HTTP POST请求。覆写该方法与doGet()类似。
3) doPut()方法,适用于HTTP PUT请求。PUT操作允许客户好像使用FTP一样把文件放置到服务器。
4) doDelete()方法,适用于HTTP DELETE请求。DELETE操作允许客户从服务器中删除一个文档或网页。
5) init()和destroy()方法,管理Servlet生命周期中的资源。
6) getServletInfo()方法,提供Servlet本身的信息。
另外还有不需要覆写的方法:
7) doHead()方法,适用于HTTP HEAD请求。当客户端只需要知道响应头,比如Content-Type或者Content-Length,客户端只需要发送一个HEAD请求。HTTP HEAD会准确地计算输出的字节数来设定Content-Length。如果覆写该方法,可以避免计算响应的BODY,而只需设置响应头以改善性能。
8) doOptions()方法,适用于OPTIONS请求。OPTIONS操作决定服务器支持哪种HTTP方法,并返回一个适当的头信息。例如,如果一个servlet覆写了doGet()方法,doOptions()方法将会返回如下头信息:Allow: GET, HEAD, TRACE, OPTIONS。
9) doTrace()方法,适用于TRACE请求。该方法用于程序调试,无需被覆写。
尽管说有这么多do***()方法,可是我们常用的就只有doGet()和doPost()。
Tomcat6讲到这里就大概差不多了,它的组成,启动,消息处理都过了一遍。不过消息处理我只写了nio的处理方式,下篇看看要不写下传统的bio方式。
That’s all!
- 大小: 41.2 KB
- 大小: 11.3 KB
- 大小: 2.8 KB
分享到:
相关推荐
`org.apache.tomcat.ajp`包提供了处理AJP请求的相关类,使得非Java应用服务器可以与Tomcat共享Java应用。 4. **Tomcat Lang API**: 这可能是Tomcat中特定的实用工具类库,比如在处理国际化、字符串操作、异常处理等...
Tomcat的设计允许开发者通过编写自定义Valve(请求处理管道中的组件)来扩展其功能。在TOMCAT_6_0_26中,Valve的使用十分灵活,可以实现诸如日志记录、会话管理、限流等功能。 通过研究TOMCAT_6_0_26的源代码,...
在"jsp+Tomcat+SQL server网上书店的开发"项目中,JSP主要负责展示页面内容,处理用户请求,并与后台数据库进行交互。 【Tomcat】 Tomcat是一款开源的Java Servlet容器,它是Apache软件基金会的Jakarta项目的一部分...
3. Tomcat处理请求并返回响应给ISAPI Redirector。 4. 最后,ISAPI Redirector将Tomcat的响应转发回IIS,再由IIS将其发送给客户端。 标签“tomcat连接器”强调了这是用于连接IIS和Tomcat的组件,确保两个独立的Web...
tomcat中server配置文件的结构,以及处理一个http请求的全过程
这些监控项可能包括Tomcat的进程状态、CPU和内存使用率、线程数、请求处理时间等。这些指标可以通过JMX(Java Management Extensions)接口获取,因为Tomcat本身支持JMX。 对于Tomcat的运行监控,我们可以通过...
6. **性能优化**:通过改进线程池管理、内存分配策略以及垃圾回收机制,Tomcat 8.5在处理大量并发请求时表现更优。 7. **管理工具**:Tomcat管理员可以使用内置的管理工具,如Manager App和Host Manager,来监控和...
3. **Web应用程序开发**:这个停车场程序展示了如何将前后端技术结合,实现一个完整的Web应用,包括用户交互、数据处理和业务逻辑。 4. **Java后端开发**:使用Java编写服务器端逻辑,处理HTTP请求,与数据库交互,...
jsp+mysql+tomcat+myeclipse+mysql_front留言板jsp+mysql+tomcat+myeclipse+mysql_front留言板jsp+mysql+tomcat+myeclipse+mysql_front留言板jsp+mysql+tomcat+myeclipse+mysql_front留言板
3. `apache-tomcat-5.5.16-src.tar.gz`:包含了Tomcat源代码,开发者可以查看和修改源码,理解其工作原理或进行定制化开发。 4. `apache-tomcat-5.5.16.zip`:这是未压缩的Windows版本,可以用于非Windows系统,也...
作为一个Servlet容器,Tomcat负责接收HTTP请求,并调用Java Servlet来处理这些请求。Servlet是Java编程语言中的一个接口,用于扩展服务器的功能。同时,Tomcat也支持JSP,这是一种用于创建动态网页的服务器端技术。...
AJP连接器(Coyote Connector)负责处理AJP请求。 3. **Local File Inclusion(LFI)漏洞**:LFI漏洞通常出现在Web应用中,允许攻击者通过用户输入来控制服务器加载本地文件。如果成功利用,攻击者可获取服务器敏感...
【标题】:“解析Tomcat处理请求的类Connector<三>” 在Java的Web服务器领域,Tomcat无疑是最为广泛使用的轻量级应用服务器之一。它以其开源、免费、高效的特点深受开发者喜爱。在这个系列的第三部分,我们将深入...
标题“Tomcat分配请求之——Socket获取请求”主要涉及的是Tomcat服务器在处理HTTP请求时的核心机制。在Web服务器中,Tomcat是一个广泛使用的开源应用服务器,它负责解析并响应来自客户端(如浏览器)的HTTP请求。这...
3. 编写Servlet,实现接收HTTP请求,与数据库交互,处理业务逻辑,并将结果存储到Session中。 4. 设计JSP页面,从Session中取出数据并显示给用户。 5. 实现用户登录功能,创建Session并保存用户信息,以便在后续请求...
6. **性能优化**:与前一版本相比,Tomcat 7.0在性能上有所提升,能够处理更高的并发请求,同时保持较低的内存占用。 7. **多线程处理**:Tomcat 7.0通过改进的线程池模型,更好地利用多核处理器,提高了服务器的...
7. **Context**、**Host**和**Engine**:这三者构成了Tomcat的容器层级结构,Engine处理所有请求,Host是特定域名的容器,而Context则对应一个Web应用。 在深入到源码层面,我们可以通过以下关键点来理解Tomcat的...
Tomcat 是一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应...
1. 安装并配置Nginx,设置反向代理规则,将用户请求路由到后端的Tomcat服务器,并配置session stickiness,确保用户在相同会话中始终连接到同一台服务器。 2. 安装并配置Tomcat,添加Redis Session Manager到web应用...
7. **集群与负载均衡**: 对于高并发访问需求,可以通过配置Tomcat集群和负载均衡来分散请求,提高系统的可用性和响应速度。这通常需要结合其他软件如Nginx或Apache HTTP Server进行反向代理。 8. **部署与更新**: ...