Tomcat是常用的Java Web服务器,HTTP请求在经过服务器之后,服务器会解析HTTP报文,将请求封装成Request对象,同时生成一个Response对象作为响应数据的载体。在平时的应用中,我们一般处理的是Request和Request对象,如何产生这两个对象对我们而言是透明的。本文旨在从整体上探究这一过程,浅析Tomcat架构与相关的技术细节。
下图是tomcat整体架构图:
Tomcat中最顶层的容器是Server,代表着整个服务器,一个tomcat对应一个server,整个 Tomcat 的生命周期由 Server 控制。
一个Server中可以有多个Service,Service具体对外提供服务。
Service主要由Connector和Container组成,一个Service中包含一个Container和多个Connector。Connector用于接受请求并将请求封装成Request和Response来具体处理,Container用于封装和管理Servlet以及具体处理request请求。
下图是容器架构图,包含4种子容器:
Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine。
Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点,Tomcat目录下webapps就是一个Host。
Context:代表一个应用程序,对应着平时开发的一套程序,webapps下每一个目录代表一个Context。
Wrapper:每一Wrapper封装着一个Servlet。
在以上的基础上,对照着Tomcat配置文件server.xml可以看一下容器之间的关系。
# Server配置,最顶层标签
# Service配置
# 线程池配置
# 容器配置,最上面的是引擎,一个Service只有一个
# 引擎下包含多个多个虚拟主机Host
# 主机下包含多个项目Context
Tomcat的组件是以嵌套的形式存在的,使用Tomcat实例时,启动脚本会执行引导类Bootstrap的main方法。所有组件实现了Lifecycle接口,该接口包含组件生命周期相关的方法,因此,当Tomcat启动时,会从最外层组件开始,嵌套调用内部组件的启动方法,以此启动所有组件。
Tomcat启动时序图如下图所示。
Tomcat启动之后,服务器等待请求进来处理请求。下图是Service处理请求流程图:
Connetor底层使用Socket接受TCP连接,将请求封装为Request和Response,再交由Container进行处理。Container处理完请求之后再返回给Connector,最后由Connector通过Socket将处理的结果返回给客户端。
Connector处理请求主要是通过ProtocolHandler进行的,不同的ProtocolHandler代表不同的连接类型。比如:Http11Protocol使用的是普通Socket来连接的,Http11NioProtocol使用的是NioSocket来连接的。
Connector.start()实际上会调用AbstractProtocol.start() 方法,最后调用endpoint.start() 的方法。AbstractProtocol是ProtocolHandler的抽象子类,AbstractProtocol包含了三个部件:Endpoint、Processor、Adapter。
1、以NioEndpoint为例,其启动逻辑源码分析如下:
public void startInternal() throws Exception {
...
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
// 开始Poller线程
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
startAcceptorThread();//开始Accepter线程
}
2、Accepter用来处理底层Socket的网络连接,其run()方法摘取部分如下:
public void run() {
...
try {
//如果到达最大连接数,等待
endpoint.countUpOrAwaitConnection();
// pause状态暂停接受连接
if (endpoint.isPaused()) {
continue;
}
U socket = null;
try {
// 接受socket连接
socket = endpoint.serverSocketAccept();
} catch (Exception ioe) {
...
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (endpoint.isRunning() && !endpoint.isPaused()) {
// setSocketOptions() 方法会试图调用Processor处理socket
if (!endpoint.setSocketOptions(socket)) {
endpoint.closeSocket(socket);
}
} else {
endpoint.destroySocket(socket);
}
} (Throwable t) {
...
}
...
}
3、跟踪setSocketOptions,会将socket包装为SocketWrapper,注册到第一步创建的Poller对象中。
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
// Disable blocking, polling will be used
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
NioChannel channel = null;
if (nioChannels != null) {
channel = nioChannels.pop();
}
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(bufhandler, selectorPool, this);
} else {
channel = new NioChannel(bufhandler);
}
} else {
}
NioSocketWrapper socketWrapper = new NioSocketWrapper(channel, this);
channel.reset(socket, socketWrapper);
socketWrapper.setReadTimeout(getConnectionTimeout());
socketWrapper.setWriteTimeout(getConnectionTimeout());
socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
socketWrapper.setSecure(isSSLEnabled());
poller.register(channel, socketWrapper);
return true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error(sm.getString("endpoint.socketOptionsError"), t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
}
// Tell to close the socket
return false;
}
4、Poller是一个Runnable对象,最后调用processKey方法处理请求,其run()摘取方法如下:
public void run() {
// 循环直到destroy()被调用
while (true) {
...
Iterator iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// 遍历已经就绪的事件,分发
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
if (socketWrapper == null) {
iterator.remove();
} else {
iterator.remove();
processKey(sk, socketWrapper);//处理连接
}
}
...
}
5、processKey方法调用processSocket方法处理,部分源码摘取如下:
public boolean processSocket(SocketWrapperBase socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase sc = null;
if (processorCache != null) {
sc = processorCache.pop();
}
if (sc == null) {
//创建Socket处理器,也是一个Runnable对象
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
}
...
}
6、在SocketProcessor中,最后会调用getHandler().process(args)。其中Handler是AbstractEndpoint.Handler接口,其实现类是AbstractProtocol的内部类ConnectionHandler。
ConnectionHandler维护了Map的键值对,如果对应的Socket找不到处理器,则会创建处理器,保存起来。
最终调用的是 processor.process(wrapper, status);
7、接下来的方法实际调用了AbstractProcessorLight.service(),以其子类方法Http11Processor.service()为例,最后调用getAdapter().service(request, response) 处理,即使用适配器处理封装后的请求。
在Connector最后封装的对象是org.apache.coyote.Request和org.apache.coyote.Response。
8、在适配器的service方法中,请求从Connector被转到Container。部分源码片段如下:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
// 将org.apache.coyote.Request转化为org.apache.catalina.connector.Request,Response同理
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().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 {
// Parse and set Catalina and configuration specific
// request parameters
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
//调用容器管道链发送请求,最终会被Servlet处理
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
...
}
}
Container是容器的父接口,所有子容器都必须实现这个接口,其设计用的是典型的责任链的的设计模式。
每一个容器内部都有一个对应的管道Pipeline,管道管理对应的阀门Valve。每一种容器都对应一种Valve,StandardEngine、StandardHost、StandardContext、StandardWrapper分别对应StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve。在容器初始化时,每一个容器都会初始化对应的Pipeline和Valve。
Connector在接收到请求后会首先调用最顶层容器的Pipeline来处理,处理方法为StandardEngineValve.invoke(request, response)。
Engine->Host->Context->Wrapper对应的Valve依次调用,最后由StandardWrapperValve获取对应的StandardWrapper,最终拿到对应的Servlet。
创建FilterChain,调用Servlet的service()方法。
当所有的Pipeline-Value都执行完之后,将结果交给Connector,通过Socket的方式将结果返回给客户端。
参考博文:https://blog.csdn.net/qq_38245537/article/details/79009448