作为Java开发人员来说Tomcat应该在熟悉不过了,自己最近空闲时间比较多,前天突然很好奇到底用户的请求是怎么被转化成HttpServletRequest
,所以自己想着看看源码,但是因为考虑到一直以来自己对 Tomcat的架构并不熟悉,所以我决定在看源码前好好的熟悉下Tomcat的架构,看看Tomcat都有哪些组件以及它们的作用分别是什么,这个过程我主要是参考官方文档,另外就是Tomcat的源码。
一、Tomcat的组成
关于Tomcat的架构其实可以通过Tomcat下的server.xml
做一个基本了解。
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
然后通过网上的一张架构图来了解各个层级之间的关系
tomcat整体架构.jpg
二、核心组件
结合这server.xml
和上面这张图,来简单的了解一下各个组件。
Server
是Tomcat的顶层组件。在server.xml
中Server
元素表示整个Catalina Servlet容器。因此,它必须是server.xml
配置文件中的单个最外层元素,简单一点可以将Server
看做是Tomcat服务器本身,Server
管理着整个Tomcat生命周期。一个Server
内可以包了多个listener
,可以包含一个或多个Service
。
Service
是Tomcat的另一个顶层组件,Service
元素由一个或多个Connector
组件的组合,这些Connector
共享一个Engine
组件以处理传入的请求。Service
的功能就是对外提供服务。
接下来是Tomcat的几个比较重要的组件:
Connector
顾名思义就是连接器,它连接的是用户请求和容器,即用户的请求到达Connector
后,由它将用户请求进行包装,然后传递给具体的容器Engine
进行处理,之后再通过Connector
将结果进行包装转换传递给用户。Connector
功能就是处理用户的请求和响应。Service
中的Engine
对外是不可见的,所有与Engine
的交互必须先经过Connector
处理。目前Tomcat有三种Connector
,它们主要区别在于支持的协议不同。最常见的就是支持http/1.1
,另外还有支持http/2
以及ajp
(Apache Jserv Protocol,一种二进制协议)。关于这个三种连接器上相关的属性建议看下官方文档,因为内容还是比较多的。在对Tomcat进行优化事很多时候都会需要修改Connector
上的相关参数,比如最大连接数、最大线程数量等等。所以这部分内容是非常重要的,也是核心。
Engine
代表的处理请求的入口。它接受并处理来自Connector
的所有请求,并将完成的响应返回给连接器,最终传输回客户端。
Host
是Engine
的子容器,一个Engine
内可以有一个或多个Host
,它表示的一个虚拟主机,
Context
表示在特定虚拟主机中运行的Web应用程序,每一个Web应用程序都基于WAR文件或包含相应解压缩内容的相应目录。每一个虚拟主机中可以包含多个Context
。
在Service
中还有一个很重要的组件就是Executor
,Executor
表示是Tomcat中的组件之间共享的线程池。 Tomcat已经默认为每个连接器都创建了一个线程池。自定义线程池可以供Connector
共享,也可以和其他组件共享该线程池。
除了上面的核心组件之外,还有其他一些嵌套组件,比如server.xml
中定义的Listener
、Realm
、GlobalNamingResources
等。这里就不细述了。
下图是一个用户请求和响应的流程示意图,实际情况当然要复杂许多。
三、请求转换
下面主要看看Connector
是如何将用户请求一步一步进行转换的,我们先看下对应类的继承体系
Connector
封装了两个主要的成员变量,一个是ProtocolHandler
,一个是Adapter
,前者根据不同的协议有不同的实现类型,我们以Http11NioProtocol
为例,其类的继承体系如下Http11NioProtocol.jpg
ProtocolHandler
主要是处理网络连接,将字节流封装成 Request对象,再将Request 适配成 Servlet 处理ServletRequest
对象这几个动作,用组件封装起来了,ProtocolHandler
包括了三个组件:Endpoint
、Processor
、Adapter
。而
Endpoint
的创建则在其默认构造函数实现,以Http11NioProtocol
为例,其代码如下:
public Http11NioProtocol() {
super(new NioEndpoint());
}
Endpoint
主要用来处理底层的Socket
网络连接,在Endpoint
具体实现类中里面有个SocketProcessor
的内部类,它负责将Endpoint
接收到的Socket
请求转化成org.apache.coyote.Request
请求。
代码如下:
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
super(socketWrapper, event);
}
@Override
protected void doRun() {
NioChannel socket = socketWrapper.getSocket();
SelectionKey key = socket.getIOChannel().keyFor(socket.getSocketWrapper().getPoller().getSelector());
Poller poller = NioEndpoint.this.poller;
if (poller == null) {
socketWrapper.close();
return;
}
try {
int handshake = -1;
try {
if (key != null) {
if (socket.isHandshakeComplete()) {
handshake = 0;
} else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
event == SocketEvent.ERROR) {
handshake = -1;
} else {
handshake = socket.handshake(key.isReadable(), key.isWritable());
event = SocketEvent.OPEN_READ;
}
}
} catch (IOException x) {
handshake = -1;
if (log.isDebugEnabled()) log.debug("Error during SSL handshake",x);
} catch (CancelledKeyException ckx) {
handshake = -1;
}
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
poller.cancelledKey(key, socketWrapper);
}
} else if (handshake == -1 ) {
poller.cancelledKey(key, socketWrapper);
} else if (handshake == SelectionKey.OP_READ){
socketWrapper.registerReadInterest();
} else if (handshake == SelectionKey.OP_WRITE){
socketWrapper.registerWriteInterest();
}
} catch (CancelledKeyException cx) {
poller.cancelledKey(key, socketWrapper);
} catch (VirtualMachineError vme) {
ExceptionUtils.handleThrowable(vme);
} catch (Throwable t) {
log.error(sm.getString("endpoint.processing.fail"), t);
poller.cancelledKey(key, socketWrapper);
} finally {
socketWrapper = null;
event = null;
//return to cache
if (running && !paused && processorCache != null) {
processorCache.push(this);
}
}
}
}
代码中getHandler().process()
方法会创建一个Processor
对象,因为代码较多,我只粘贴一部分:
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
if (wrapper == null) {
return SocketState.CLOSED;
}
S socket = wrapper.getSocket();
Processor processor = connections.get(socket);
....
if (processor == null) {
String negotiatedProtocol = wrapper.getNegotiatedProtocol();
if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol);
if (upgradeProtocol != null) {
processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
} else if (negotiatedProtocol.equals("http/1.1")) {
} else {
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",negotiatedProtocol));
}
return SocketState.CLOSED;
}
}
}
if (processor == null) {
processor = recycledProcessors.pop();
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.processorPop",processor));
}
}
if (processor == null) {
processor = getProtocol().createProcessor();
register(processor);
}
// 省略代码
......
}
上面的代码在AbstractProtocol
的内部类ConnectionHandler
中,其中socket
变量其实是一个NioChannel
实例。这个方法代码中会根据不同情形创建相关的Processor
。比如我这里会获取相应的AbstractHttp11Protocol
然后调用其createProcessor()
方法创建Http11Processor
对象。
@Override
protected Processor createProcessor() {
Http11Processor processor = new Http11Processor(this, adapter);
return processor;
}
而adapter
其实在Connector
初始化的时候就创建完成了,并将其添加到protocolHandler
中,下面是Connector
的initInternal()
方法:
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
.......
// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
if (service != null) {
protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
}
......
另外需要关注的一点是创建Http11Processor
的时候,会调用其父类的构造函数:
public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
super(adapter);
this.protocol = protocol;
httpParser = new HttpParser(protocol.getRelaxedPathChars(),
protocol.getRelaxedQueryChars());
inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
protocol.getRejectIllegalHeaderName(), httpParser);
request.setInputBuffer(inputBuffer);
outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
response.setOutputBuffer(outputBuffer);
// 省略代码
.....
}
而调用父类构造函数的时候会创建两个对象,Request
和Response
,对应的具体类是org.apache.coyote.Request
和org.apache.coyote.Response
。这两个对象下面还会使用到,不要混淆了。
回归到正题,Processor
创建完成后会调用它的process
方法(AbstractProcessorLight.process
)其方法内部会调用具体的service
方法,这里调用的是Http11Processor.service
方法,这个方法内内容很多,我也并没有仔细的看,应该是对org.apache.coyote.Request
和org.apache.coyote.Response
进行了参数设置,核心的地方在于调用adapter
的service
方法,代码如下:
if (getErrorState().isIoAllowed()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
// 适配器转换,request和response
getAdapter().service(request, response);
if(keepAlive && !getErrorState().isError() && !isAsync() &&
statusDropsConnection(response.getStatus())) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
} catch (InterruptedIOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
} catch (HeadersTooLargeException e) {
log.error(sm.getString("http11processor.request.process"), e);
if (response.isCommitted()) {
setErrorState(ErrorState.CLOSE_NOW, e);
} else {
response.reset();
response.setStatus(500);
setErrorState(ErrorState.CLOSE_CLEAN, e);
response.setHeader("Connection", "close"); // TODO: Remove
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("http11processor.request.process"), t);
response.setStatus(500);
setErrorState(ErrorState.CLOSE_CLEAN, t);
getAdapter().log(request, response, 0);
}
}
这里会调用CoyoteAdapter
的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) {
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
request.setResponse(response);
response.setRequest(request);
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
}
boolean async = false;
boolean postParseSuccess = false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try {
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
if (request.isAsync()) {
async = true;
ReadListener readListener = req.getReadListener();
if (readListener != null && request.isFinished()) {
ClassLoader oldCL = null;
try {
oldCL = request.getContext().bind(false, null);
if (req.sendAllDataReadEvent()) {
req.getReadListener().onAllDataRead();
}
} finally {
request.getContext().unbind(false, oldCL);
}
}
Throwable throwable =(Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (!request.isAsyncCompleting() && throwable != null) {
request.getAsyncContextInternal().setErrorState(throwable, true);
}
} else {
request.finishRequest();
response.finishResponse();
}
} catch (IOException e) {
// Ignore
} finally {
AtomicBoolean error = new AtomicBoolean(false);
res.action(ActionCode.IS_ERROR, error);
if (request.isAsyncCompleting() && error.get()) {
res.action(ActionCode.ASYNC_POST_PROCESS, null);
async = false;
}
// Access log
if (!async && postParseSuccess) {
Context context = request.getContext();
Host host = request.getHost();
long time = System.currentTimeMillis() - req.getStartTime();
if (context != null) {
context.logAccess(request, response, time, false);
} else if (response.isError()) {
if (host != null) {
host.logAccess(request, response, time, false);
} else {
connector.getService().getContainer().logAccess(request, response, time, false);
}
}
}
req.getRequestProcessor().setWorkerThreadName(null);
if (!async) {
updateWrapperErrorCount(request, response);
request.recycle();
response.recycle();
}
}
}
这个方法的入参为org.apache.coyote.Request
和org.apache.coyote.Response
两个变量,方法内会根据入参创建出org.apache.catalina.connector.Request
和org.apache.catalina.connector.Response
,而这两个对象分别继承了HttpServletRequest
和HttpServletResponse
,也就是说实际上用户请求在这里完成了转换,变成了我们非常熟悉的HttpServletRequest
和HttpServletResponse
。
然后执行下面这段代码:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
即通过Connector
一步步将request
和response
丢给容器进行处理,而且从方法名称也可以看的出来,实际上是由Pipeline
进行处理的,这个就暂不深究了。也就是说到这里我们的HttpServletRequest
和HttpServletResponse
会被容器进行处理了,在Connector
的流程执行完成,之后就是有容器进行处理过程了,这里就不再继续往下看了。
整个Connector
执行流程如下图:
通过以上代码和流程图基本上就搞清楚了,用户请求是如何变成我们熟悉的
HttpServletRequest
和HttpServletResponse
的,当然实际过程要复杂很多,我只是简单的通过跟踪代码了解了大概的过程,具体代码内容并没有详细的去看,后期如果有需要的话自己会挑选一部分进行阅读,比如今天的Connector
这部分。
本次的学习收获主要有两点一是Tomcat的架构,即其组成及各组件的作用,当然这部分没有去深入学习,二是请求转换,即用户请求过来之后是如何转换成HttpServletRequest
和HttpServletResponse
的,这部分主要理清了大概的执行流程,并简单的跟踪了代码,如果后期有需要会在来具体的分析这部分代码。
网友评论