tomcat4.0版本。
连接器是tomcat组件中的一员,连同engine组件一起为service组件服务。一个service组件可以包含若干个连接器组件和一个engine组件。如果包含了多个连接器,那么应该是对应了多种传输协议,比如:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
下面看下连接器的相关实现。
首先是connector接口的重要方法:
* Create (or allocate) and return a Request object suitable for
* specifying the contents of a Request to the responsible Container.
*/
public Request createRequest();
/**
* Create (or allocate) and return a Response object suitable for
* receiving the contents of a Response from the responsible Container.
*/
public Response createResponse();
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*
* @exception LifecycleException If this server was already initialized.
*/
public void initialize()
throws LifecycleException;
/**
* Return the Container used for processing requests received by this
* Connector.
*/
public Container getContainer();
/**
* Set the Container used for processing requests received by this
* Connector.
*
* @param container The new Container to use
*/
public void setContainer(Container container);
连接器的主要任务是处理底层socket相关的操作,比如开启一个serviceSocket监听端口,如果有请求了转交给相应的processor处理,当然还需要把请求封装在request里,同时给后续的processor提供一个response实例来返回结果。processor的作用是调用相应的containter也即engine组件来处理请求,后面会说。
tomcat4中的默认connector实现是httpConnector。
因为一个service可能会有多个connector的实现,而socket的相关操作时阻塞式的,所以这里connector要以异步形式启动,实现了runnable接口:
public final class HttpConnector
implements Connector, Lifecycle, Runnable {
Lifecycle接口在另一篇中说到过,tomcat中的所有组件都会实现该接口,这样父组件就可以管理其生命周期。
所以,service会调用connector的start方法来启动一个httpConnector。但是start之前需要调用initialize方法初始化,该方法在connector接口内定义。
先看父组件service启动connector:
先是:
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
connectors[i].initialize();
}
}
然后:
// Start our defined Connectors second
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
if (connectors[i] instanceof Lifecycle)
((Lifecycle) connectors[i]).start();
}
}
下面看httpConnector中的相应实现:
/**
* Initialize this connector (create ServerSocket here!)
*/
public void initialize()
throws LifecycleException {
if (initialized)
throw new LifecycleException (
sm.getString("httpConnector.alreadyInitialized"));
this.initialized=true;
Exception eRethrow = null;
// Establish a server socket on the specified port
try {
serverSocket = open();
} catch (IOException ioe) {
log("httpConnector, io problem: ", ioe);
eRethrow = ioe;
} catch (KeyStoreException kse) {
log("httpConnector, keystore problem: ", kse);
eRethrow = kse;
} catch (NoSuchAlgorithmException nsae) {
log("httpConnector, keystore algorithm problem: ", nsae);
eRethrow = nsae;
} catch (CertificateException ce) {
log("httpConnector, certificate problem: ", ce);
eRethrow = ce;
} catch (UnrecoverableKeyException uke) {
log("httpConnector, unrecoverable key: ", uke);
eRethrow = uke;
} catch (KeyManagementException kme) {
log("httpConnector, key management problem: ", kme);
eRethrow = kme;
}
if ( eRethrow != null )
throw new LifecycleException(threadName + ".open", eRethrow);
}
正如注释所示,该方法所要做的事情很简单,就是开启一个serverSocket来监听端口。具体内容在open方法中,很简单就不看了。
接着就是start方法:
/**
* Begin processing requests via this Connector.
*
* @exception LifecycleException if a fatal startup error occurs
*/
public void start() throws LifecycleException {
// Validate and update our current state
if (started)
throw new LifecycleException
(sm.getString("httpConnector.alreadyStarted"));
threadName = "HttpConnector[" + port + "]";
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our background thread
threadStart();
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
recycle(processor);
}
}
start里首先开启了connector的异步处理线程,然后创建了若干processor实例并池化在一个stack中。池化的数目由minProcessors定义。
接着看connector的线程run方法:
/**
* 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 (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
// if (debug >= 3)
// log("run: Waiting on serverSocket.accept()");
socket = serverSocket.accept();
// if (debug >= 3)
// log("run: Returned from serverSocket.accept()");
if (connectionTimeout > 0)
socket.setSoTimeout(connectionTimeout);
socket.setTcpNoDelay(tcpNoDelay);
} catch (AccessControlException ace) {
log("socket accept security exception", ace);
continue;
} catch (IOException e) {
// if (debug >= 3)
// log("run: Accept returned IOException", e);
try {
// If reopening fails, exit
synchronized (threadSync) {
if (started && !stopped)
log("accept error: ", e);
if (!stopped) {
// if (debug >= 3)
// log("run: Closing server socket");
serverSocket.close();
// if (debug >= 3)
// log("run: Reopening server socket");
serverSocket = open();
}
}
// if (debug >= 3)
// log("run: IOException processing completed");
} catch (IOException ioe) {
log("socket reopen, io problem: ", ioe);
break;
} catch (KeyStoreException kse) {
log("socket reopen, keystore problem: ", kse);
break;
} catch (NoSuchAlgorithmException nsae) {
log("socket reopen, keystore algorithm problem: ", nsae);
break;
} catch (CertificateException ce) {
log("socket reopen, certificate problem: ", ce);
break;
} catch (UnrecoverableKeyException uke) {
log("socket reopen, unrecoverable key: ", uke);
break;
} catch (KeyManagementException kme) {
log("socket reopen, key management problem: ", kme);
break;
}
continue;
}
// Hand this socket off to an appropriate processor
HttpProcessor processor = createProcessor();
if (processor == null) {
try {
log(sm.getString("httpConnector.noProcessor"));
socket.close();
} catch (IOException e) {
;
}
continue;
}
// if (debug >= 3)
// log("run: Assigning socket to processor " + processor);
processor.assign(socket);
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
// if (debug >= 3)
// log("run: Notifying threadStop() that we have shut down");
synchronized (threadSync) {
threadSync.notifyAll();
}
}
这里,会拿到一个连接的socket,然后取出一个processor来处理这个socket的请求。取出processor的时候,会先看池中有没有,如果没有就会在没有达到创建上限的情况下新建一个processor来处理,最后也会池化这个新建的processor。
processor的设计:
processor的实现类也很重,这就是之前在connector中池化processor的原因。所以池化的processor会处理不同的socket请求,在connector把一个连接上来的socket交给一个processor处理的时候,需要重设processor的socket变量,也就是之前看到的调用processor的assign方法。
processor被设计为一个异步处理方式:
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Process requests until we receive a shutdown signal
while (!stopped) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
try {
process(socket);
} catch (Throwable t) {
log("process.invoke", t);
}
// Finish up this request
connector.recycle(this);
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
会run方法检查当前的processor是否设置了socket,如果设置了就process,否则等待。这个逻辑是在await内:
/**
* 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();
if ((debug >= 1) && (socket != null))
log(" The incoming request has been awaited");
return (socket);
}
接着是process方法:
/**
* Process an incoming HTTP request on the Socket that has been assigned
* to this Processor. Any exceptions that occur during processing must be
* swallowed and dealt with.
*
* @param socket The socket on which we are connected to the client
*/
private void process(Socket socket) {
boolean ok = true;
boolean finishResponse = true;
SocketInputStream input = null;
OutputStream output = null;
// Construct and initialize the objects we will need
try {
input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
} catch (Exception e) {
log("process.create", e);
ok = false;
}
keepAlive = true;
while (!stopped && ok && keepAlive) {
finishResponse = true;
try {
request.setStream(input);
request.setResponse(response);
output = socket.getOutputStream();
response.setStream(output);
response.setRequest(request);
((HttpServletResponse) response.getResponse()).setHeader
("Server", SERVER_INFO);
} catch (Exception e) {
log("process.create", e);
ok = false;
}
// Parse the incoming request
try {
if (ok) {
parseConnection(socket);
parseRequest(input, output);
if (!request.getRequest().getProtocol()
.startsWith("HTTP/0"))
parseHeaders(input);
if (http11) {
// Sending a request acknowledge back to the client if
// requested.
ackRequest(output);
// If the protocol is HTTP/1.1, chunking is allowed.
if (connector.isChunkingAllowed())
response.setAllowChunking(true);
}
}
} catch (EOFException e) {
// It's very likely to be a socket disconnect on either the
// client or the server
ok = false;
finishResponse = false;
} catch (ServletException e) {
ok = false;
try {
((HttpServletResponse) response.getResponse())
.sendError(HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception f) {
;
}
} catch (InterruptedIOException e) {
if (debug > 1) {
try {
log("process.parse", e);
((HttpServletResponse) response.getResponse())
.sendError(HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception f) {
;
}
}
ok = false;
} catch (Exception e) {
try {
log("process.parse", e);
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception f) {
;
}
ok = false;
}
// Ask our Container to process this request
try {
((HttpServletResponse) response).setHeader
("Date", FastHttpDateFormat.getCurrentDate());
if (ok) {
connector.getContainer().invoke(request, response);
}
} catch (ServletException e) {
log("process.invoke", e);
try {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (Exception f) {
;
}
ok = false;
} catch (InterruptedIOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
try {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (Exception f) {
;
}
ok = false;
}
// Finish up the handling of the request
if (finishResponse) {
try {
response.finishResponse();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
request.finishRequest();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
if (output != null)
output.flush();
} catch (IOException e) {
ok = false;
}
}
// We have to check if the connection closure has been requested
// by the application or the response stream (in case of HTTP/1.0
// and keep-alive).
if ( "close".equals(response.getHeader("Connection")) ) {
keepAlive = false;
}
// End of request processing
status = Constants.PROCESSOR_IDLE;
// Recycling the request and the response objects
request.recycle();
response.recycle();
}
try {
shutdownInput(input);
socket.close();
} catch (IOException e) {
;
} catch (Throwable e) {
log("process.invoke", e);
}
socket = null;
}
该方法比较长,主要是解析request的请求,然后调用engine容器的invoke方法处理请求。
所以,tomcat4中的connector组件主要包含了processor和connector。connector负责创建监听服务器端口,然后把连接上来的socket交给processor处理。processor负责解析然后调用engine的invoke方法处理此次请求。
设计上,由于connector和processor都是很重的实现,所以这里采用了池化复用processor的设计。二者最终都是以异步线程的方式来服务的。
connector的异步是因为可能有多种connector,如果不异步就会被第一个connector独占。
processor异步是为了提高吞吐率,在处理上一个socket时还可以接受新的socket。这里processor是池化的,所以就需要在其process方法里检测当前的processor有没有指派要处理的socket,如果没有那么就continue,否则就去处理。