Tomcat以Servlet容器著称(处理Jsp和Servlet等动态资源的应用服务器)。由Tomcat的总体架构可知,Servlet容器由2个主要组件构成:Connector(连接器)和Container(容器)。Connector负责接收客户端的请求,而Container处理并响应该请求。
由JavaEE规范可知,Servlet容器内部只处理HTTP协议的请求,但是对于连接器的设计来说,它可以接收任何协议(如HTTP,AJP等)的请求,因此,在连接器接收到客户端的请求(Socket)后,需要将该请求包装成容器可以处理的对象,然后再传递给容器处理,同时,连接器也要创建一个响应对象一并传给容器,好让容器响应请求。如此一来,容器就与具体传输协议解耦了,而这正是Connector架构所要达到的目的。
以上是闲话,但是我觉得说的挺好的,摘自网上。
我们将Servlet容器分成两个部分:Connector和Container。
我们知道客户端和服务器之间要进行通信也是要建立连接的,也需要初始化套接字,通过套接字通信。在Servlet容器中就是通过Connector这个部分来实现。
在Connector进入start状态后,调用protocolHandler.start(),protocolHandler是根据你的协议类型来设置的,默认为org.apache.coyote.http11.Http11Protocol。在Http11Protocol中是通过JIoEndpoint来进行监听套接字,用Http11ConnectionHandler去处理这个套接字的通信。
在protocolHandler的start()中通过endPoint来建立连接,endpoint.start() //JIoEndpoint endpoint。
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
创建的Acceptor线程用来接受HTTP连接请求。
protected class Acceptor implements Runnable {
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
// Accept the next incoming connection from the server socket
try {
Socket socket = serverSocketFactory.acceptSocket(serverSocket);
serverSocketFactory.initSocket(socket);
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) {
// Close socket right away
try {
socket.close();
} catch (IOException e) {
// Ignore
}
}
}catch ( IOException x ) {
if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
} catch (Throwable t) {
log.error(sm.getString("endpoint.accept.fail"), t);
}
// The processor will recycle itself when it finishes
}
}
}
现在socket就可以用来和客户端进行通信。Acceptor只是用来接受连接,但是具体的处理并不是由它来解决的。而是交给Worker线程。
protected boolean processSocket(Socket socket) {
try {
if (executor == null) {
getWorkerThread().assign(socket);
} else {
executor.execute(new SocketProcessor(socket));
}
} catch (Throwable t) {
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
log.error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
当Workder线程启动时是处于阻塞状态的,它一直阻塞在await(),一直在等待有个socket来处理,当我们将socket通过assign(socket)交给Worker线程,那么就不再处于阻塞状态了。
public void run() {
// Process requests until we receive a shutdown signal
while (running) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
if (!setSocketOptions(socket) || !handler.process(socket)) {
// Close socket
try {
socket.close();
} catch (IOException e) {
}
}
// Finish up this request
socket = null;
recycleWorkerThread(this);
}
}
synchronized void assign(Socket socket) {
// Wait for the Processor to get the previous Socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
}
/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Notify the Connector that we have received this Socket
Socket socket = this.socket;
available = false;
notifyAll();
return (socket);
}
我们在之前就提过,
Http11Protocol中是通过
JIoEndpoint来进行监听套接字,用
Http11ConnectionHandler(Http11Protocol的内部类)去处理这个套接字的通信,所以Worker其实还是将socket交给Http11ConnectionHandler处理,handler.process(socket)。这个handler还是Http11Protocol在init()时交给JIoEndpoint。
public void init() throws Exception {
endpoint.setName(getName());
endpoint.setHandler(cHandler);
public boolean process(Socket socket) {
Http11Processor processor = recycledProcessors.poll();
try {
if (processor == null) {
processor = createProcessor();
}
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_START, null);
}
if (proto.isSSLEnabled() && (proto.sslImplementation != null)) {
processor.setSSLSupport
(proto.sslImplementation.getSSLSupport(socket));
} else {
processor.setSSLSupport(null);
}
processor.process(socket);
return false;
} catch(java.net.SocketException e) {
// SocketExceptions are normal
Http11Protocol.log.debug
(sm.getString
("http11protocol.proto.socketexception.debug"), e);
} catch (java.io.IOException e) {
// IOExceptions are normal
Http11Protocol.log.debug
(sm.getString
("http11protocol.proto.ioexception.debug"), e);
}
// Future developers: if you discover any other
// rare-but-nonfatal exceptions, catch them here, and log as
// above.
catch (Throwable e) {
// any other exception or error is odd. Here we log it
// with "ERROR" level, so it will show up even on
// less-than-verbose logs.
Http11Protocol.log.error
(sm.getString("http11protocol.proto.error"), e);
} finally {
// if(proto.adapter != null) proto.adapter.recycle();
// processor.recycle();
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
}
recycledProcessors.offer(processor);
}
return false;
}
从这里我们看出其实真正处理socket的是Http11Processor。
1:初始化流。
// Setting up the I/O
this.socket = theSocket;
inputBuffer.setInputStream(socket.getInputStream());
outputBuffer.setOutputStream(socket.getOutputStream());
2:解析头部。
// Parsing the request header
try {
if (keptAlive) {
if (keepAliveTimeout > 0) {
socket.setSoTimeout(keepAliveTimeout);
}
else if (soTimeout > 0) {
socket.setSoTimeout(soTimeout);
}
}
inputBuffer.parseRequestLine();
request.setStartTime(System.currentTimeMillis());
keptAlive = true;
if (disableUploadTimeout) {
socket.setSoTimeout(soTimeout);
} else {
socket.setSoTimeout(timeout);
}
// Set this every time in case limit has been changed via JMX
request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
inputBuffer.parseHeaders();
} catch (IOException e) {
error = true;
break;
} 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);
error = true;
}
3:对request的其他处理。
// Process the request in the adapter
if (!error) {
try {
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());
}
}
这里是调用adapter的service方法。
public interface Adapter {
public void service(Request req, Response res)
throws Exception;
public boolean event(Request req, Response res, SocketStatus status)
throws Exception;
public void log(Request req, Response res, long time);
}
adapter是一个适配器。他适配了org.apache.coyote.Request和org.apache.catalina.connector.Request,后者正是继承了HttpServletRequest。不知道你有没有跟我相同的疑问,为什么需要这样一个适配器,这不是多此一举吗?事实上,这里适配器起到了隔离内外的作用,对于coyote内部,用coyote.Request可以做一些request本身职责以外的事情(但是又不能不在request中实现,因为生命周期就是request,如设置开始结束时间,日志记录等等),但是这些事情不需要给开发者看到,有可能会被误用导致coyote出错,于是暴露给开发者的是connector.Request,中间使用adapter来适配。adapter的初始化还要追溯到Connector。
// Initializa adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
prorocolHandler是Http11ConnectionHandler。而
Http11ConnectionHandler又把adapter交给了Http11Processor
processor.setAdapter(proto.adapter);
而Adapter.service(...)就是在
Http11Processor中被调用的。
其实这么多只是为了一个目的:接受客户端的连接请求,对请求进行处理,再返回给客户端一个响应。只是过程有点复杂。
总结用到的类:Connector;Http11Protocol;Http11Processor;JIoEndpoint;Adapter(CoyoteAdapter)。还有很多的内部类,比如Worker,Acceptor,Http11ConnectionHandler等。
现在大概清楚了请求的处理过程了,但是细节还是不怎么清楚。看了一晚上,继续努力。