Tomcat    核心组件 Connector_linux

Connector是Tomcat的连接器,其主要任务是负责处理浏览器发送过来的请求,并创建一个Request和Response的对象用于和浏览器交换数据,然后产生一个线程用于处理请求,Connector会把Request和Response对象传递给该线程,该线程的具体的处理过程是Container容器的事了。执行过程分为以下几个步骤:

  1. 实例化Connector,构造一个Connector对象。
  2. 调用Connector的initIntenal方法,初始化Connetor。
  3. 调用ProtocolHanlder的init方法,完成ProtocolHanlder的初始化。这个过程包括了创建线程池并创建一个线程处理浏览器请求。
  4. 调用Connector的startIntenal方法,启动Connector。
  5. 调用ProtocolHandler的start方法,启动Protocolhanlder。
  6. 调用MapperListener的start方法,启动监听器程序。

Connect执行过程的时序图:

Tomcat    核心组件 Connector_tomcat_02

注意:由于Tomcat还支持AJP协议,但为了简化,我画的这个序列图是基于Http协议的,这也是我们在Web开发中接触最多的协议了。

在深入Connector之前我们先看看Connector类的结构:

Tomcat    核心组件 Connector_linux教程_03

Tomcat    核心组件 Connector_linux教程_04

既然是处理浏览器请求,那么需要支持http协议,在Tomcat中有两种协议处理器:HTTP/1.1与AJP/1.3协议处理器。在server.xml中已经指明tomcat所支持的两种协议:

    <Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

在tomcat中是怎么样分别处理这两种协议的呢,我们可以ProtocolHanlder类中找到答案:

Tomcat    核心组件 Connector_linux教程_05

图中被选中的就是Tomcat默认使用协议处理器,其实现过程与Java标准Socket编程是一样的,在tomcat中可以使用Connetor类的setProtocol方法,看看源码就知道了:

    public void setProtocol(String protocol) {        if (AprLifecycleListener.isAprAvailable()) {            if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else { if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11Protocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}

从第2个if子句的最后一个else可以知道tomcat默认使用的是http1.1协议。

我们再看看Connector的初始化过程:

    @Override
protected void initInternal() throws LifecycleException { super.initInternal(); // Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter); // Make sure parseBodyMethodsSet has a default
if( null == parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
} if (protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) { throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
} try {
protocolHandler.init();
} catch (Exception e) { throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
} // Initialize mapper listener
mapperListener.init();
}

从这段代码中可以看到:首先调用父类org.apache.catalina.util.LifecycleMBeanBase的初始化方法,然后创建一个Adapter,然后设置protocolHanlder(协议处理器)的Adapter,同时判断传过来的请求的请求方法(比如get或者post),如果没有指明请求方法,默认使用post处理,然后调用protocolHanlder的初始化方法,最后调用mapperListener的初始化方法,而mapperListener的初始化方法调用的是org.apache.catalina.util.LifecycleBase的init方法,我们重点关注protocolHanlder的初始化方法,具体是实现在AbstractProtocol抽象类中,其直接子类有AbstractAjpProtocol和AbstractHttp11Protocol,分别对应的是两种不同的处理协议,所以协议处理器的初始化方法是在其子抽象类(实现ProtocolHanlder接口的抽象类)来实现的,这里看看AbstractHttp11Protocol的初始化方法:

    @Override
public void init() throws Exception { if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.init",
getName())); if (oname == null) { // Component not pre-registered so register it
oname = createObjectName(); if (oname != null) {
Registry.getRegistry(null, null).registerComponent(this, oname, null);
}
} if (this.domain != null) { try {
tpOname = new ObjectName(domain + ":" + "type=ThreadPool,name=" + getName());
Registry.getRegistry(null, null).registerComponent(endpoint,
tpOname, null);
} catch (Exception e) {
getLog().error(sm.getString( "abstractProtocolHandler.mbeanRegistrationFailed",
tpOname, getName()), e);
}
rgOname=new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null );
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1)); try {
endpoint.init();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.initError",
getName()), ex); throw ex;
}
}

打断点调试可以知道oname的值是Tomcat:type=ProtocolHandler,port=auto-1,address=”127.0.0.1”,tpOname是Tomcat:type=ProtocolHandler,port=auto-1,address=”127.0.0.1”,rOname是Tomcat:type=GlobalRequestProcessor,name=”http-bio-127.0.0.1-auto-1”,我们重点关注endpoint的init方法,主要完成以下几个过程:

  1. 设置线程接收数和最大连接数
  2. 创建线程池,启动监听的线程监听用户请求
  3. 启动一个线程处理请求

初始化完成Connector就可以启动了,启动阶段调用startInternal方法:

    @Override
protected void startInternal() throws LifecycleException { // Validate settings before starting
if (getPort() < 0) { throw new LifecycleException(sm.getString( "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING); try {
protocolHandler.start();
} catch (Exception e) {
String errPrefix = ""; if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
} throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed"), e);
}
mapperListener.start();
}

可以看出Connector调用protocolHandler.start()方法,继续看看这个方法的源码:

    @Override
public void start() throws Exception { if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName())); try {
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex); throw ex;
}
}

这个方法又调用了endpoint.start()方法:

    public final void start() throws Exception {        if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}

然后又调用了org.apache.tomcat.util.net.AbstractEndpoint.startInternal()方法:

    @Override
public void startInternal() throws Exception { if (!running) {
running = true;
paused = false; // Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
startAcceptorThreads(); // Start async timeout thread
Thread timeoutThread = new Thread(new AsyncTimeout(),
getName() + "-AsyncTimeout");
timeoutThread.setPriority(threadPriority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
  1. 设置线程接收数和最大连接数
  2. 创建线程池,启动监听的线程监听用户请求
  3. 启动一个线程处理异步请求

这个异步线程是如何执行的呢?

    /**
* Async timeout thread
*/
protected class AsyncTimeout implements Runnable {
/**
* The background thread that checks async requests and fires the
* timeout if there has been no activity.
*/
@Override
public void run() { // Loop until we receive a shutdown command
while (running) { try {
Thread.sleep(1000);
} catch (InterruptedException e) { // Ignore
} long now = System.currentTimeMillis();
Iterator<SocketWrapper> sockets =
waitingRequests.iterator(); while (sockets.hasNext()) {
SocketWrappersocket = sockets.next(); long access = socket.getLastAccess(); if (socket.getTimeout() > 0 &&
(now-access)>socket.getTimeout()) {
processSocketAsync(socket,SocketStatus.TIMEOUT);
}
} // Loop if endpoint is paused
while (paused && running) { try {
Thread.sleep(1000);
} catch (InterruptedException e) { // Ignore
}
}
}
}
} //processSocket
public boolean processSocketAsync(SocketWrappersocket,
SocketStatus status) { try { synchronized (socket) { if (waitingRequests.remove(socket)) {
SocketProcessor proc = new SocketProcessor(socket,status);
ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { //threads should not be created by the webapp classloader
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedActionpa = new PrivilegedSetTccl(
getClass().getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader(
getClass().getClassLoader());
} // During shutdown, executor may be null - avoid NPE
if (!running) { return false;
}
getExecutor().execute(proc); //TODO gotta catch RejectedExecutionException and properly handle it
} finally { if (Constants.IS_SECURITY_ENABLED) {
PrivilegedActionpa = new PrivilegedSetTccl(loader);
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader(loader);
}
}
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(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;
}

org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor的职责是把具体的请求处理过程委派给org.apache.tomcat.util.net.JIoEndpoint.Handler,然后根据handler返回的不同SocketState,来决定是否关闭连接或者进行下一轮处理。

public void run() {
boolean launch = false;
synchronized (socket) {
try {
SocketState state = SocketState.OPEN;
try { // SSL handshake
serverSocketFactory.handshake(socket.getSocket());
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t); if (log.isDebugEnabled()) { log.debug(sm.getString("endpoint.err.handshake"), t);
}
// Tell to close the socket
state = SocketState.CLOSED;
} if ((state != SocketState.CLOSED)) { if (status == null) { state = handler.process(socket, SocketStatus.OPEN_READ);
} else { state = handler.process(socket,status);
}
} if (state == SocketState.CLOSED) { // Close socket
if (log.isTraceEnabled()) { log.trace("Closing socket:"+socket);
}
countDownConnection();
try { socket.getSocket().close();
} catch (IOException e) { // Ignore
}
} else if (state == SocketState.OPEN || state == SocketState.UPGRADING || state == SocketState.UPGRADING_TOMCAT || state == SocketState.UPGRADED){ socket.setKeptAlive(true); socket.access();
launch = true;
} else if (state == SocketState.LONG) { socket.access();
waitingRequests.add(socket);
}
} finally { if (launch) {
try {
getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));
} catch (RejectedExecutionException x) { log.warn("Socket reprocessing request was rejected for:"+socket,x);
try { //unable to handle connection at this time
handler.process(socket, SocketStatus.DISCONNECT);
} finally {
countDownConnection();
}
} catch (NullPointerException npe) { if (running) { log.error(sm.getString("endpoint.launch.fail"),
npe);
}
}
}
}
} socket = null; // Finish up this request
}
}

其中的process方法主要完成对request的解析,包括请求头、请求行和请求体 :

    //process method of org.apache.coyote.http11.AbstractHttp11Processor.HttpProcessor extends org.apache.coyote.http11.AbstractHttp11Processor
@Override
public SocketState process(SocketWrapper socketWrapper) throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE); // Setting up the I/O
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint);
getOutputBuffer().init(socketWrapper, endpoint); // Flags
error = false;
keepAlive = true;
comet = false;
openSocket = false;
sendfileInProgress = false;
readComplete = true; if (endpoint.getUsePolling()) {
keptAlive = false;
} else {
keptAlive = socketWrapper.isKeptAlive();
} if (disableKeepAlive()) {
socketWrapper.setKeepAliveLeft(0);
} while (!error && keepAlive && !comet && !isAsync() &&
upgradeInbound == null &&
httpUpgradeHandler == null && !endpoint.isPaused()) { // Parsing the request header
try {
setRequestLineReadTimeout(); if (!getInputBuffer().parseRequestLine(keptAlive)) { if (handleIncompleteRequestLineRead()) { break;
}
} if (endpoint.isPaused()) { // 503 - Service unavailable
response.setStatus(503);
error = true;
} else { // Make sure that connectors that are non-blocking during
// header processing (NIO) only set the start time the first
// time a request is processed.
if (request.getStartTime() < 0) {
request.setStartTime(System.currentTimeMillis());
}
keptAlive = true; // Set this every time in case limit has been changed via JMX
request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount()); // Currently only NIO will ever return false here
if (!getInputBuffer().parseHeaders()) { // We've read part of the request, don't recycle it
// instead associate it with the socket
openSocket = true;
readComplete = false; break;
} if (!disableUploadTimeout) {
setSocketTimeout(connectionUploadTimeout);
}
}
} catch (IOException e) { if (getLog().isDebugEnabled()) {
getLog().debug(
sm.getString("http11processor.header.parse"), e);
}
error = true; break;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
UserDataHelper.Mode logMode = userDataHelper.getNextMode(); if (logMode != null) {
String message = sm.getString( "http11processor.header.parse"); switch (logMode) { case INFO_THEN_DEBUG:
message += sm.getString( "http11processor.fallToDebug"); //$FALL-THROUGH$
case INFO:
getLog().info(message); break; case DEBUG:
getLog().debug(message);
}
} // 400 - Bad Request
response.setStatus(400);
adapter.log(request, response, 0);
error = true;
} if (!error) { // Setting up filters, and parse some request headers
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE); try {
prepareRequest();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t); if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString( "http11processor.request.prepare"), t);
} // 400 - Internal Server Error
response.setStatus(400);
adapter.log(request, response, 0);
error = true;
}
} if (maxKeepAliveRequests == 1) {
keepAlive = false;
} else if (maxKeepAliveRequests > 0 &&
socketWrapper.decrementKeepAlive() <= 0) {
keepAlive = false;
} // Process the request in the adapter
if (!error) { try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
adapter.service(request, response); if(keepAlive && !error) { // Avoid checking twice.
error = response.getErrorException() != null ||
(!isAsync() &&
statusDropsConnection(response.getStatus()));
}
setCometTimeouts(socketWrapper);
} catch (InterruptedIOException e) {
error = true;
} catch (HeadersTooLargeException e) {
error = true; // The response should not have been committed but check it
// anyway to be safe
if (!response.isCommitted()) {
response.reset();
response.setStatus(500);
response.setHeader("Connection", "close");
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
getLog().error(sm.getString( "http11processor.request.process"), t); // 500 - Internal Server Error
response.setStatus(500);
adapter.log(request, response, 0);
error = true;
}
} // Finish the handling of the request
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT); if (!isAsync() && !comet) { if (error) {

getInputBuffer().setSwallowInput(false);
} if (response.getStatus() < 200 || response.getStatus() > 299) { if (expectation) { // Client sent Expect: 100-continue but received a
// non-2xx response. Disable keep-alive (if enabled) to
// ensure the connection is closed. Some clients may
// still send the body, some may send the next request.
// No way to differentiate, so close the connection to
// force the client to send the next request.
getInputBuffer().setSwallowInput(false);
keepAlive = false;
}
}
endRequest();
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT); // 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(); if (!isAsync() && !comet || error) {
getInputBuffer().nextRequest();
getOutputBuffer().nextRequest();
} if (!disableUploadTimeout) { if(endpoint.getSoTimeout() > 0) {
setSocketTimeout(endpoint.getSoTimeout());
} else {
setSocketTimeout(0);
}
}
rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE); if (breakKeepAliveLoop(socketWrapper)) { break;
}
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
}
}

首先在Http11Processor的process方法里,会先从socket里读取http请求数据,并解析请求头,构造Request对象和Response对象,然后调用Adapter.service()方法。Adapter.service()完成请求行以及请求体的解析,并把解析出来的信息封装到Request和Response对象中,Adapter(确切说是org.apache.catalina.connector.CoyoteAdapter)是connector和container的桥梁,经过这一步,请求就从connector传递到container里了,Adapter.service()方法之后便将封装了Request以及Response对象的Socket传给Container容器了。

要注意的是:最先处理请求的Request是org.apache.coyote.Request类型,这是一个Tomcat中一个轻量级对象,完成基本的请求处理后很容易被JVM回收,那为什么不直接交给Connector.Request对象处理呢?由于后者是Servlet容器真正传递的对象其完成的职责比前者复杂,这里使用org.apache.coyote.Request主要减轻后者的任务负担,出于性能考虑才这么设计。

具体service方法清单如下:

@Override
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 = connector.createRequest();
request.setCoyoteRequest(req);
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 = false;
boolean async = false;
try {
// Parse and set Catalina and configuration //specific
// request parameters
req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
//postParseRequest方法把CoyoteRequest转换为Connector.Request对象
//后一类型的对象才是在Tomcat容器流转时真正传递的对象
boolean postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
// 调用Container容器的invoke方法,把请求交给Container容器
connector.getService().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_READ)) {
comet = true;
res.action(ActionCode.COMET_BEGIN, null);
}
} else {
comet = true;
res.action(ActionCode.COMET_BEGIN, null);
}
} else {
// Clear the filter chain, as otherwise it will not be reset elsewhere
// since this is a Comet request
request.setFilterChain(null);
}
}
}
AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
if (asyncConImpl != null) {
async = true;
} else if (!comet) {
request.finishRequest();
response.finishResponse();
if (postParseSuccess &&
request.getMappingData().context != null) {
((Context) request.getMappingData().context).logAccess(
request, response,
System.currentTimeMillis() - req.getStartTime(),
false);
}
req.action(ActionCode.POST_REQUEST , null);
}
} catch (IOException e) {
// Ignore
} finally {
//ignore
}
}

从connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);这句代码中可以知道下一步的处理需要交给Container容器了。

经过上面一系列复杂的操作流程,Tomcat的Connector已经完成了protocol.start()方法,返回Connector的startIntenal方法,还有一个步骤要完成就是mapperListener.start()的方法了,整个执行过程比较简单,有两步:

  1. 执行Connector的startIntenal方法
  2. 执行MapperListener的startIntenal方法
   @Override
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
findDefaultHost();
Engine engine = (Engine) connector.getService().getContainer();
addListeners(engine);
Container[] conHosts = engine.findChildren(); for (Container conHost : conHosts) {
Host host = (Host) conHost; if (!LifecycleState.NEW.equals(host.getState())) { // Registering the host will register the context and wrappers
registerHost(host);
}
}
}

首先注册已初始化的组件,然后为这些组件添加监听器,最后添加容器之间的映射关系。

​https://blog.51cto.com/lovebetterworld​