为什么我的oppo r7s plusr7s到现在都没有升级,是咋回事

javaweb(2)
org.apache.coyote.Request和org.apache.coyote.Response
在Acceptor接收到一个socket之后,在JIoEndpoint的processSocket方法中这个socket被包装成SocketWrapper
然后从线程池中分配一个线程来处理这次请求,会调用Http11ConnectionHandler.process方法来处理,来看看这个process方法
在这个方法里面如果处理器为空,就调用createProcessor方法创建一个,创建processor时会产生两个对象,分别是request,response,也就是说在接收到socket之后用户的请求作了如下转化:
处理用户请求会调用Adapter.service方法,Adapter就是一个适配器,来看看service方法的签名
service方法规定了用户请求必须被转化为org.apache.coyote.Request才能被容器处理,由于用户请求多种多样,将这些多种多样的请求接口转换为一个统一的接口正是适配器模式所长,这以这里采用了适配器模式。
org.apache.catalina.connector.Request和org.apache.catalina.connector.Response
我们知道servlet的service方法需要传入ServletRequest和ServletResponse类型的参数,tomcat是不是直接将org.apache.coyote.Request直接转换为ServletRequest了呢?从org.apache.coyote.Request没有实现javax.servlet.http.HttpServletRequest接口可知不是这样的。事实上也不能这样做,因为org.apache.coyote.Request里面封闭了很多底层处理方法,不想暴露给web开发人员使用。所以org.apache.coyote.Request与ServletRequest之前还有一个中间层,这就是tomcat容器独有的请求响应接口:org.apache.catalina.connector.Request与org.apache.catalina.connector.Response
正如所料,在Adapter的service方法中将org.apache.coyote.Request转化为了org.apache.catalina.connector.Request,如下代码所示:
org.apache.catalina.connector.Request实现了HttpServletRequest接口,重写了父类的一些方法,还加了一些与容器相关的方法,另外还代理了org.apache.coyote.Request里面的一些方法,既然org.apache.catalina.connector.Request里面有一些容器特有的方法,这些方法也是不能给web开发人员使用的,那么org.apache.catalina.connector.Request又是怎么转换为ServletRequest的呢?这里采用了外观模式,将与容器相关的方法封装起来。
这些接口和类的关系图如下:
本节只分析了request和response的类层次结构,具体请求的处理在后面分析!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:4660次
排名:千里之外
(2)(2)(1)(6)(2)(1)(1)java(31)
1. Request和Response&
当处理请求的时候,Tomcat使用org.apache.coyote.Request和org.apache.coyote.Response对象来封装对Socket的数据处理,就好像它们是输入流和输出流一样。&
Request和Response是low-level的,并不是用于在开发中使用的,所以,Tomcat使用了适配器模式定义了两个类org.apache.catalina.connector.Request和org.apache.catalina.connector.Response来对前面的low-level对象进行封装,并且扩展了HttpServletRequest和HttpServletResponse接口,实现了Servelt规范。&
2. Pipeline和Valve(不是half-life那个)&
Tomcat有四个级别的容器,在server.xml中出现了出现过3个,Engine,Host,Context。另外一个是Wrapper,是对Servlet,JSP或者静态资源调用的一种封装,是最后一层容器了。Tomcat使用Pipeline模式在各层容器间传递请求,将请求通过管道依次通过Engine,Host,Context和Wrapper。另外,每一个容器都可以设置一系列的Valve去对请求进行拦截,就像管道中的阀一样对请求的行为进行一些干涉。&
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:108750次
积分:1429
积分:1429
排名:千里之外
原创:10篇
转载:169篇
(1)(1)(1)(1)(2)(4)(3)(1)(2)(1)(2)(7)(1)(7)(4)(2)(1)(3)(4)(1)(1)(5)(1)(2)(2)(13)(2)(21)(11)(74)tomcat HTTP处理—— Request的生命历程和相应Connector配置解析 - 平衡
trade-off - ITeye博客
博客分类:
Request的生命历程,可以参见常量类org.apache.coyote.Constants.java
// Request states
public static final int STAGE_NEW = 0;
public static final int STAGE_PARSE = 1;
public static final int STAGE_PREPARE = 2;
public static final int STAGE_SERVICE = 3;
public static final int STAGE_ENDINPUT = 4;
public static final int STAGE_ENDOUTPUT = 5;
public static final int STAGE_KEEPALIVE = 6;
public static final int STAGE_ENDED = 7;
和用到这些常量的地方 Http11Processor.java (代码贴的比较多,可以用浏览器的 查找功能来查找上面的常量)
while (started && !error && keepAlive && !endpoint.isPaused()) {
// Parsing the request header
if (keptAlive) {
if (keepAliveTimeout & 0) {
socket.setSoTimeout(keepAliveTimeout);
else if (soTimeout & 0) {
socket.setSoTimeout(soTimeout);
inputBuffer.parseRequestLine();
request.setStartTime(System.currentTimeMillis());
keptAlive =
if (disableUploadTimeout) {
socket.setSoTimeout(soTimeout);
socket.setSoTimeout(timeout);
inputBuffer.parseHeaders();
} catch (IOException e) {
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.header.parse"), t);
// 400 - Bad Request
response.setStatus(400);
adapter.log(request, response, 0);
if (!error) {
// Setting up filters, and parse some request headers
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
prepareRequest();
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.request.prepare"), t);
// 400 - Internal Server Error
response.setStatus(400);
adapter.log(request, response, 0);
if (maxKeepAliveRequests & 0 && --keepAliveLeft == 0)
keepAlive =
// Process the request in the adapter
if (!error) {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
adapter.service(request, response);
// Handle when the response was committed before a serious
// error occurred.
Throwing a ServletException should both
// set the status to 500 and set the errorException.
// If we fail here, then the response is likely already
// committed, so we can't try and set headers.
if(keepAlive && !error) { // Avoid checking twice.
error = response.getErrorException() != null ||
statusDropsConnection(response.getStatus());
} catch (InterruptedIOException e) {
} catch (Throwable t) {
log.error(sm.getString("http11processor.request.process"), t);
// 500 - Internal Server Error
response.setStatus(500);
adapter.log(request, response, 0);
// Finish the handling of the request
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
// If we know we are closing the connection, don't drain input.
// This way uploading a 100GB file doesn't tie up the thread
// if the servlet has rejected it.
inputBuffer.setSwallowInput(false);
inputBuffer.endRequest();
} catch (IOException e) {
} catch (Throwable t) {
log.error(sm.getString("http11processor.request.finish"), t);
// 500 - Internal Server Error
response.setStatus(500);
adapter.log(request, response, 0);
rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
outputBuffer.endRequest();
} catch (IOException e) {
} catch (Throwable t) {
log.error(sm.getString("http11processor.response.finish"), t);
// If there was an error, make sure the request is counted as
// and error, and update the statistics counter
if (error) {
response.setStatus(500);
request.updateCounters();
rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
// Don't reset the param - we'll see it as ended. Next request
// will reset it
// thrA.setParam(null);
// Next request
inputBuffer.nextRequest();
outputBuffer.nextRequest();
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
// Recycle
inputBuffer.recycle();
outputBuffer.recycle();
this.socket =
// Recycle ssl info
sslSupport =
Connector配置解析:
官方文档:
文档中讲了比较多的选项的用法,其中,我把比较重要的几个列举下:
maxKeepAliveRequests
在服务器关闭socket连接之前,能保持的最大请求数。
maxThreads
服务器的最大线程数。讲白了就是Worker的最大数目,当然如果配置开启了Executor的话,这个配置项便是没有用的。
这里你会不会有疑问,线程数和请求数有什么关系。根据我上一篇文章 的分析,一个Worker线程对应一个请求。
那么你会不会有更多的疑问,maxThreads如何设置得比 maxKeepAliveRequests 小会怎么办?
请看下面这段代码
Http11Processor.java
int keepAliveLeft = maxKeepAliveR
int soTimeout = endpoint.getSoTimeout();
// When using an executor, these values may return non-positive values
int curThreads = endpoint.getCurrentThreadsBusy();
int maxThreads = endpoint.getMaxThreads();
if (curThreads & 0 && maxThreads & 0) {
// Only auto-disable keep-alive if the current thread usage % can be
// calculated correctly
if ((curThreads*100)/maxThreads & 75) {
keepAliveLeft = 1; // 当前使用线程是最大线程数的75%的时候,会自动禁用keepAlive
while (started && !error && keepAlive) {
if (maxKeepAliveRequests & 0 && --keepAliveLeft == 0)
keepAlive =
当前使用线程是最大线程的75% 的时候,会自动禁用keepAlive
所以maxThreads设置得比maxKeepAliveRequests 小,则这个maxKeepAliveRequests 设置时没有效果的。
当maxThreads设置肯定要比 maxKeepAliveRequests 来的大,而且 maxKeepAliveRequests 不会超过maxThreads的75%!!
默认:maxThreads 为 200
maxKeepAliveRequests
keepAliveTimeout
服务器Socket读取HTTP请求行到来的限制时间。 以millionseconds为单位。默认和connectionTimeout 的值一样。
connectionTimeout
单位是毫秒,Connector从接受连接到提交URI请求数据的等待的时间。
开始这个我没搞懂,不过找到了这篇文章:
上面的解释和测试。
也可以看下面的代码:Http11Processor.java
while (started && !error && keepAlive) {
// Parsing the request header
if (keptAlive) {
if (keepAliveTimeout & 0) {
socket.setSoTimeout(keepAliveTimeout);
else if (soTimeout & 0) {
socket.setSoTimeout(soTimeout);
// 服务器获取客户端请求行,这里会调用inputBuffer的read 方法,引起socket阻塞,阻塞时间超过soTimeout的话就会SocketTimeOutException
inputBuffer.parseRequestLine();
disableUploadTimeout
是否允许在上传操作的时候给与更长的socketTimeOut时间。默认false不允许。
如果设置为true,则默认为5分钟限制,超过5分钟则会报SocketTimeOut异常。
如果要改这个时间,需要用这个Attribute: timeout 这个官方文档没有写,不知道为什么,可能不建议修改吧,因为5分钟是和apache 的httpd的时间保持一致的。
&Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" disableUploadTimeout="false" timeout="600000"/&
其中HTTP 协议,最好参考下RFC的文档 结合Http11Protocal 和 Http11Processor来看比较好
&当前使用线程是最大线程的75% 的时候,会自动禁用keepAlive &所以maxThreads设置得比maxKeepAliveRequests 小,则这个maxKeepAliveRequests 设置时没有效果的。 &当maxThreads设置肯定要比 maxKeepAliveRequests 来的大,而且 maxKeepAliveRequests 不会超过maxThreads的75%!! &默认:maxThreads 为 200 &maxKeepAliveRequests& 为100 楼主,文中这一段是不是有误呀?当前线程大于最大线程75%时候,自动禁用keepAlive这个没错。但是下面的推论, maxThreads跟maxKeepAliveRequests的关系我认为不太对。maxThreads 跟maxKeepAliveRequests的设置应该没有直接的关系。那个75%的关系是当前线程跟最大线程之间的关系。我的理解是,当当前线程超过最大线程75%时,说明系统并发有点高了,就暂时放弃对keepAlive的支持(keepAlive=1),是为了支持更多的用户。因为keepAlive会让连接被同一个用户占用比较长时间。而当当前线程数小于最大线程数的75%时,说明系统并发不算太高,就使用keepAlive来提高Socket连接的利用效率,同一个用户的多次请求可以重用同一条连接(因为如果每个请求都要打开关闭Socket是比较低效的)。所以我觉得, maxThreads 设置大于实际平均并发量的4/3会比较合理。(即实际平均并发量小于maxThreads的75%) 这样可以让keepAlive尽量工作。至于maxKeepAliveRequests 设置多大,哪个值最优,这个不好说。一般默认值应该就差不多了。以上只是我的个人理解,不一定正确,欢迎一起讨论。感谢您的回复,我认为你说的比较正确。我当时认为maxKeepAliveRequests 表示 最多能有多少个线程在忙 ,这个肯定是错误的其实maxKeepAliveRequests &= 最多能有几个线程在忙。但是:maxKeepAliveRequests& maxThreads 我想应该是无可厚非的具体我可能需要再深入研究下。另外,不同的应用场景可能不一定需要keepAlive。
浏览: 246492 次
来自: 火星
这个ibatis 还真是不好用啊。楼主解决了我看文档也没解决的 ...
SqlMapClientTemplate sqlMa ...
2楼肯定是people.xsd格式错了
遇到同样的问题,请教如何解决的java.io.NotSeria ...Tomcat源码解读:我们发起的HTTP请求如何到达Servlet的 - WEB服务器 - 次元立方网 - 电脑知识与技术互动交流平台
Tomcat源码解读:我们发起的HTTP请求如何到达Servlet的
在上一节中,了解了Tomcat服务器启动的整个过程,现在来了解一下Tomcat如何接收到HTTP请求,并将请求送达至Servlet,Servlet处理后,响应信息又是如何返回给浏览器的呢?这两个问题是接下来要研究的课题,本节先研究第一个问题。
&&& 了解一点点网络知识的人,都会知道TCP连接通信是基于Socket的,上一节也有提到这点。通过上一节的说明,可以了解到Tomcat服务器在内部已经使用Endpoint类封装了Socket。
&本篇会包含大量的源码解读说,由于篇幅原因,就将源码折叠起来,如果想了解其过程,简易打开折叠的源码看看。
在查看源码之前,建议大家自己想一想,如果是你来设计,会怎么处理请求。我先自己想了一下请求处理过程:
1) ServerSocket accept客户端的请求得到Socket对象,这一部分肯定要与Connector关联起来,因为只有Connector上配置了与TCP相关的东西,例如:port, protocol等。
2) Tomcat中的某个组件(在Connector范围内)解析Socket对象,封装成一个Request对象
3) Tomcat中某个组件(在Connector范围内)根据Request对象在服务器上找出于这个连接器关联的Container,也就是Engine。因为Connector和Engine都是Service范围内的,并且一个Service内可以有多个Connector,只能有一个Engine,所以在Connector确定的情况下,Engine就是确定的。接下来只需要找到所在的虚拟主机Host就行了。
4) 找到请求所属的主机Host,根据HTTP请求的URL就可以了。这是因为URL由遵循下面的结构:Protocol://host:port/context/path
5)找到请求所属的context,也就是说找到请求那个web app的。
6)根据请求的path部分找到所在的Web app的资源处理Servlet
因为在web.xml中配置了Servlet的url-pattern,也就是那个Servlet处理哪些路径下的资源请求。
7)如果有filter,先filter处理。
8)调用Servlet#doService()方法。这是在做Java Web开发时了解到的。然后根据请求的method(GET、POST、PUT等),自动的解析为doXxx(doGet, doPost)
在了解这一部分前,我简单的看了一下源码,做了一个简单的类图:
接下来,就通过调试说明这个处理过程:
调试前,先看看系统中已有的线程:
从这个线程列表里可以看出来,这些线程都是各个Endpoint内部的属性(Acceptor、CometPoller、Poller)的线程。所以我们发送的请求肯定是这几类线程中的一个来处理的。
真实流程解读
1)使用AprEndpoint$Acceptor#run()来接收TCP连接
通过类图知道,在AprEndpoint内部有很多的Acceptor,应该是用于接收不同端口的TCP连接的吧。
猜想不能解决问题,经过调试,确实发现这里开始接收TCP连接请求了:
public void run() {
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
Thread.sleep(1000);
} catch (InterruptedException e) {
if (!running) {
// Accept the next incoming connection from the server socket
long socket = Socket.accept(serverSock);
* In the case of a deferred accept unlockAccept needs to
* send data. This data will be rubbish, so destroy the
* socket and don't process it.
if (deferAccept && (paused || !running)) {
destroySocket(socket);
// Hand this socket off to an appropriate processor
if (!processSocketWithOptions(socket)) {
// Close socket and pool right away
destroySocket(socket);
} catch (Throwable t) {
if (running) {
String msg = sm.getString('endpoint.accept.fail');
if (t instanceof Error) {
Error e = (Error)
if (e.getError() == 233) {
// Not an error on HP-UX so log as a warning
// so it can be filtered out on that platform
// See bug 50273
log.warn(msg, t);
log.error(msg, t);
log.error(msg, t);
// The processor will recycle itself when it finishes
这里附加一些Java的基本知识:它只用接收请求就行了,别的什么也不用做。
它并没有拿到一个Socket,也没有将socket传递给其他的类,但是却能够完成请求的处理,这是为什么呢?
上面的代码中Socket.acccpt(serverSocket),就是接收一个Socket请求,标示是serverSocket 。
&&& 因为使用了内部类,这样的好处是内部类方法和属性对于外部类是可见的,外部类的方法属性对内部类也是可见的。其实内部类的方法可以认为是对于外部类的扩充,只是在其他的类中不能使用这些方法而已,只能在外部类,内部类本身里使用而已(当然了,如果内部类是public,方法也是public情况下,第三方类还是可以使用的)。
2)使用AprEndpoint$Worker#run()来做启动TCP连接处理流程
public void run() {
// Process requests until we receive a shutdown signal
while (running) {
// Wait for the next socket to be assigned
// 拿到Acceptor接收到的请求
long socket = await();
if (socket == 0)
if (!deferAccept && options) {
if (setSocketOptions(socket)) {
getPoller().add(socket);
// Close socket and pool
destroySocket(socket);
socket = 0;
// Process the request from this socket
if ((status != null) && (handler.event(socket, status) == Handler.SocketState.CLOSED)) {
// Close socket and pool
destroySocket(socket);
socket = 0;
// 调用Handler.process处理Socket
// 在Handler对象内部,会找到一个HTTP11AprProcessor处理器,用于处理Socket请求
// 然后在HTTP11AprProcessor处理过程中,又会转给
} else if ((status == null) && ((options && !setSocketOptions(socket))
|| handler.process(socket) == Handler.SocketState.CLOSED)) {
// Close socket and pool
destroySocket(socket);
socket = 0;
// Finish up this request
recycleWorkerThread(this);
3)根据Socket解析HTTP Header封装成Request
&由于方法比较长,就不粘了,只粘出主要代码:
inputBuffer.setSocket(socket);
inputBuffer.parseHeaders();
而inputBuffer#parseHeader():&
* Parse the HTTP headers.
public void parseHeaders()
throws IOException {
// 每一次调用parseHeader(),就是解析HTTP Header中的一条。 这个是基于HTTP Header的格式来解析的,这个不明白,可以先了解一下HTTP协议
while (parseHeader()) {
parsingHeader =
InputBuffer# parseHeader(),这个过程比较复杂,就不贴了。下面贴出来解析后的消息头:&
4)根据消息头解析出Container相关信息
准备Request,其实就是做一些额外的处理,例如根据消息头解析host, port,context,path等等,将其封装为StandardContext对象,然后放在Request对象里。
5)调用adapter#service(),请求转给Container(Engine)
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = (Request) connector.createRequest();
request.setCoyoteRequest(req);
response = (Response) connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
if (connector.getXpoweredBy()) {
response.addHeader('X-Powered-By', POWERED_BY);
boolean comet =
// Parse and set Catalina and configuration specific
// request parameters
req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
if (postParseRequest(req, request, res, response)) {
// Calling the container
// 通过这一步,获取到与Connector关联的Container【也就是Engine】,如此就将流程转给了Tomcat容器处理了。
//这也是这个CoyoteApapter的作用
// 这里connector.getContainer()得到的是一个Engine
// engine.getPipeline().getFirst()得到的其实是Engine的pipeliene中的一个StandardEngineValue。
Tomcat中的Value是用于执行一些任务的。至于为什么起名为Value,就不太清楚了。只需要知道这段代码执行的是StandardEngineValue#invoke()就可以了。
connector.getContainer().getPipeline().getFirst().invoke(request, response);
if (request.isComet()) {
if (!response.isClosed() && !response.isError()) {
if (request.getAvailable() || (request.getContentLength() & 0 && (!request.isParametersParsed()))) {
// Invoke a read event right away if there are available bytes
if (event(req, res, SocketStatus.OPEN)) {
res.action(ActionCode.ACTION_COMET_BEGIN, null);
res.action(ActionCode.ACTION_COMET_BEGIN, null);
// Clear the filter chain, as otherwise it will not be reset elsewhere
// since this is a Comet request
request.setFilterChain(null);
if (!comet) {
response.finishResponse();
req.action(ActionCode.ACTION_POST_REQUEST , null);
} catch (IOException e) {
} finally {
req.getRequestProcessor().setWorkerThreadName(null);
// Recycle the wrapper request and response
if (!comet) {
request.recycle();
response.recycle();
// Clear converters so that the minimum amount of memory
// is used by this processor
request.clearEncoders();
response.clearEncoders();
6)在Engine》Host》Context之间通过Pipeline传递请求
6.1)StandarEngineValue#invoke()从将Request转给Host处理
下面是StandardEngineValue#invoke()的源码:
* Select the appropriate child Host to process this request,
* based on the requested server name.
If no matching Host can
* be found, return an appropriate HTTP error.
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()));
// Ask this Host to process this request
// 这个设计与之前的设计思路是一致的,调用的是StandardHostValue#invoke()
host.getPipeline().getFirst().invoke(request, response);
从invoke方法的注释就可以知道,是要从engine范围内,根据第3)步找到的HOST信息,将请求交给host处理。
6.2)StandardHostValue#invoke()将Request转给Context处理
这一步与上面是类似的,将请求交给了StandardContextValue处理。
* Select the appropriate child Context to process this request,
* based on the specified request URI.
If no matching Context can
* be found, return an appropriate HTTP error.
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
Context context = request.getContext();
if (context == null) {
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString('standardHost.noContext'));
// Bind the context CL to the current thread
if( context.getLoader() != null ) {
// Not started - it should check for availability first
// This should eventually move to Engine, it's generic.
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
// Ask this Context to process this request
context.getPipeline().getFirst().invoke(request, response);
// Access a session (if present) to update last accessed time, based on a
// strict interpretation of the specification
if (Globals.STRICT_SERVLET_COMPLIANCE) {
request.getSession(false);
// Error page processing
response.setSuspended(false);
Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
if (t != null) {
throwable(request, response, t);
status(request, response);
// Restore the context classloader
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
至此,请求终于到达处理它的WEB应用程序了。&
7)启动监听器处理,接着在Context找到Wrapper,让Wrapper处理
* Select the appropriate child Wrapper to process this request,
* based on the specified request URI.
If no matching Wrapper can
* be found, return an appropriate HTTP error.
* @param request Request to be processed
* @param response Response to be produced
* @param valveContext Valve context used to forward to the next Valve
* @exception IOException if an input/output error occurred
* @exception ServletException if a servlet error occurred
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Disallow any direct access to resources under WEB-INF or META-INF
// META-INF 和WEB-INF目录是应用程序内部的专属空间,是不允许直接这两个目录下的内容的。所以如果你的请求URL上包含着两个目录,都不会被处理。
// 在应用程序内部,例如Servlet里,可以处理这两个目录下的文件。譬如JSP文件放在WEB-INF目录下,直接访问是不可见的,但是通过Servlet进行forward就可以。但是这都是请求到达Servlet之后的事了,现在请求还没到Servlet呢,别急。
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase('/META-INF/', 0))
|| (requestPathMB.equalsIgnoreCase('/META-INF'))
|| (requestPathMB.startsWithIgnoreCase('/WEB-INF/', 0))
|| (requestPathMB.equalsIgnoreCase('/WEB-INF'))) {
notFound(response);
// Wait if we are reloading
// 软重启应用程序,前提是Web应用程序下有资源改变,一般情况下,不会重启的。
boolean reloaded =
while (context.getPaused()) {
reloaded =
Thread.sleep(1000);
} catch (InterruptedException e) {
// Reloading will have stopped the old webappclassloader and
// created a new one
// 要是重启过,就得重新设置类加载器
if (reloaded &&
context.getLoader() != null &&
context.getLoader().getClassLoader() != null) {
Thread.currentThread().setContextClassLoader(
context.getLoader().getClassLoader());
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null) {
notFound(response);
} else if (wrapper.isUnavailable()) {
// May be as a result of a reload, try and find the new wrapper
wrapper = (Wrapper) container.findChild(wrapper.getName());
if (wrapper == null) {
notFound(response);
// Normal request processing
// 取得所有的监听器,这些监听器都是我们在web.xml中配置的(有ServletContext(application)、Session、Request 级别的监听器)
不管属于哪个级别的,全部查出来。
Object instances[] = context.getApplicationEventListeners();
ServletRequestEvent event =
if ((instances != null)
&& (instances.length & 0)) {
// 封装一个request级别的Event
event = new ServletRequestEvent
(((StandardContext) container).getServletContext(),
request.getRequest());
// create pre-service event
// 轮询前面取到所有的listener,处理RequestListener
for (int i = 0; i & instances. i++) {
if (instances[i] == null)
if (!(instances[i] instanceof ServletRequestListener))
ServletRequestListener listener =
(ServletRequestListener) instances[i];
// listener处理
listener.requestInitialized(event);
} catch (Throwable t) {
container.getLogger().error(sm.getString('standardContext.requestListener.requestInit',
instances[i].getClass().getName()), t);
ServletRequest sreq = request.getRequest();
sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
// 同之前的设计一样,找到StandardWrapperValue,处理Request
wrapper.getPipeline().getFirst().invoke(request, response);
// 监听器结束生命周期
if ((instances !=null ) &&
(instances.length & 0)) {
// create post-service event
for (int i = 0; i & instances. i++) {
if (instances[i] == null)
if (!(instances[i] instanceof ServletRequestListener))
ServletRequestListener listener =
(ServletRequestListener) instances[i];
listener.requestDestroyed(event);
} catch (Throwable t) {
container.getLogger().error(sm.getString('standardContext.requestListener.requestDestroy',
instances[i].getClass().getName()), t);
ServletRequest sreq = request.getRequest();
sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
8)Servlet处理请求
请求并不是直接就让Servlet处理的,这点做过Web开发的人都知道,至少中间还有个Filter要处理吧。
下面是StandardWrapperValue#invoke()的源码,就来了解一下它是咋处理的。
这点代码包含了很多内容,解析来会一一说明:
* Invoke the servlet we are managing, respecting the rules regarding
* servlet lifecycle and SingleThreadModel support.
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Initialize local variables we may need
boolean unavailable =
Throwable throwable =
// This should be a Request attribute...
long t1=System.currentTimeMillis();
requestCount++;
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet =
Context context = (Context) wrapper.getParent();
// 检查context是否可用,就是检查web应用程序是否可用,因为可能出现应用程序挂了,或者软重启了
if (!context.getAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString('standardContext.isUnavailable'));
unavailable =
// 检查要处理请求的Servlet是否可用,如果Servlet被删除,然后也重启Context了,servlet就没有了,所以又必要检查一下。
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString('standardWrapper.isUnavailable',
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available & 0L) && (available & Long.MAX_VALUE)) {
response.setDateHeader('Retry-After', available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString('standardWrapper.isUnavailable',
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString('standardWrapper.notFound',
wrapper.getName()));
unavailable =
// 分配一个Servlet对象来处理请求,下面8.1会详细说明如何分配的
if (!unavailable) {
servlet = wrapper.allocate();
} catch (UnavailableException e) {
container.getLogger().error(
sm.getString('standardWrapper.allocateException',
wrapper.getName()), e);
long available = wrapper.getAvailable();
if ((available & 0L) && (available & Long.MAX_VALUE)) {
response.setDateHeader('Retry-After', available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString('standardWrapper.isUnavailable',
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString('standardWrapper.notFound',
wrapper.getName()));
} catch (ServletException e) {
container.getLogger().error(sm.getString('standardWrapper.allocateException',
wrapper.getName()), StandardWrapper.getRootCause(e));
throwable =
exception(request, response, e);
} catch (Throwable e) {
container.getLogger().error(sm.getString('standardWrapper.allocateException',
wrapper.getName()), e);
throwable =
exception(request, response, e);
// Identify if the request is Comet related now that the servlet has been allocated
boolean comet =
if (servlet instanceof CometProcessor
&& request.getAttribute('org.et.support') == Boolean.TRUE) {
request.setComet(true);
// 告诉Connector:已经拿到处理的Servlet了。
response.sendAcknowledgement();
} catch (IOException e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
container.getLogger().warn(sm.getString('standardWrapper.acknowledgeException',
wrapper.getName()), e);
throwable =
exception(request, response, e);
} catch (Throwable e) {
container.getLogger().error(sm.getString('standardWrapper.acknowledgeException',
wrapper.getName()), e);
throwable =
exception(request, response, e);
MessageBytes requestPathMB =
if (request != null) {
requestPathMB = request.getRequestPathMB();
request.setAttribute
(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
ApplicationFilterFactory.REQUEST_INTEGER);
request.setAttribute
(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// 为request创建Filter链,是创建,是为每一个请求创建过滤器链,不是获取已有的。
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
// Reset comet flag value after creating the filter chain
request.setComet(false);
//调用filter chain处理请求,这个过程我还有一篇文章专门讲述
String File = wrapper.getJspFile();
if (File != null)
request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
request.removeAttribute(Globals.JSP_FILE_ATTR);
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
SystemLogHandler.startCapture();
if (comet) {
filterChain.doFilterEvent(request.getEvent());
request.setComet(true);
filterChain.doFilter(request.getRequest(),
response.getResponse());
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() & 0) {
context.getLogger().info(log);
if (comet) {
request.setComet(true);
filterChain.doFilterEvent(request.getEvent());
filterChain.doFilter
(request.getRequest(), response.getResponse());
request.removeAttribute(Globals.JSP_FILE_ATTR);
} catch (ClientAbortException e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
throwable =
exception(request, response, e);
} catch (IOException e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
container.getLogger().error(sm.getString('standardWrapper.serviceException',
wrapper.getName()), e);
throwable =
exception(request, response, e);
} catch (UnavailableException e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
container.getLogger().error(sm.getString('standardWrapper.serviceException',
wrapper.getName()), e);
throwable =
exception(request, response, e);
wrapper.unavailable(e);
long available = wrapper.getAvailable();
if ((available & 0L) && (available & Long.MAX_VALUE)) {
response.setDateHeader('Retry-After', available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString('standardWrapper.isUnavailable',
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString('standardWrapper.notFound',
wrapper.getName()));
// Do not save exception in 'throwable', because we
// do not want to do exception(request, response, e) processing
} catch (ServletException e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
container.getLogger().error(sm.getString('standardWrapper.serviceException',
wrapper.getName()), rootCause);
throwable =
exception(request, response, e);
} catch (Throwable e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
container.getLogger().error(sm.getString('standardWrapper.serviceException',
wrapper.getName()), e);
throwable =
exception(request, response, e);
// 释放过滤器链,此时Servlet已经执行完毕。
if (filterChain != null) {
if (request.isComet()) {
// If this is a Comet request, then the same chain will be used for the
// processing of all subsequent events.
filterChain.reuse();
filterChain.release();
// 回收servlet
if (servlet != null) {
wrapper.deallocate(servlet);
} catch (Throwable e) {
container.getLogger().error(sm.getString('standardWrapper.deallocateException',
wrapper.getName()), e);
if (throwable == null) {
throwable =
exception(request, response, e);
// If this servlet has been marked permanently unavailable,
// unload it and release this instance
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
} catch (Throwable e) {
container.getLogger().error(sm.getString('standardWrapper.unloadException',
wrapper.getName()), e);
if (throwable == null) {
throwable =
exception(request, response, e);
long t2=System.currentTimeMillis();
long time=t2-t1;
processingTime +=
if( time & maxTime) maxTime=
if( time & minTime) minTime=
8.1)分配Servlet
要让Servlet处理请求,得先分配Servlet,分配Servlet对象也是有讲究的,因为Servlet有两种运行模式,单线程运行模式和多线程运行模式。
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading)
throw new ServletException
(sm.getString('standardWrapper.unloading', getName()));
boolean newInstance =
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// 如果是第一次请求这个Servlet,Servlet肯定还没有创建,这时就要创建一个Servlet实例,并初始化。这一点,我想有的面试官会问到的。
if (instance == null) {
synchronized (this) {
if (instance == null) {
if (log.isDebugEnabled())
log.debug('Allocating non-STM instance');
// loadServlet()过程也做了很多事:
// 如果是jsp请求:解析JSP成一个Servlet类,编译,加载(解析,编译过程只在第一次请求该JSP文件时进行)
// 如果是html,img,css,js等就返回DefaultServlet
instance = loadServlet();
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case #3
if (!singleThreadModel) {
newInstance =
countAllocated.incrementAndGet();
} catch (ServletException e) {
} catch (Throwable e) {
throw new ServletException
(sm.getString('standardWrapper.allocate'), e);
// 返回Servlet实例。
if (!singleThreadModel) {
if (log.isTraceEnabled())
log.trace('
Returning non-STM instance');
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
return (instance);
// 要是单线程模式下运行的Sevlet,就得等Servlet执行完毕,被回收后,再分配给你这个请求
synchronized (instancePool) {
while (countAllocated.get() &= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances & maxInstances) {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
} catch (Throwable e) {
throw new ServletException
(sm.getString('standardWrapper.allocate'), e);
instancePool.wait();
} catch (InterruptedException e) {
if (log.isTraceEnabled())
log.trace('
Returning allocated STM instance');
countAllocated.incrementAndGet();
return (Servlet) instancePool.pop();
8.2)过滤器链处理
参考博客:Filter
8.3)Servlet.service() ,Servlet执行
过滤器对象里有个属性就是servelt,在过滤器链处理完毕,就直接调用了Servlet了。
官方的说法
官方说法中前三步中涉及到的类,可能有我调试时不同,这是因为采用的协议不同,协议不同,与protocol相关的processor也就不同。但是整个流程就是这个样子的。
延伸阅读:
  Django的部署可以有很多方式,采用nginx+uwsgi的...
本教程为 李华明 编著的iOS-Cocos2d游戏开发系列教程:教程涵盖关于i......
专题主要学习DirectX的初级编程入门学习,对Directx11的入门及初学者有......
&面向对象的JavaScript&这一说法多少有些冗余,因为JavaScript 语言本......
Windows7系统专题 无论是升级操作系统、资料备份、加强资料的安全及管......

我要回帖

更多关于 oppor7s 的文章

 

随机推荐