Tomcat对HTTP请求的处理(一)
摘要:本文主要介绍了tomcat在启动过程中为了处理HTTP请求之前所做的准备工作
在之前的文章中介绍了tomcat的启动,关闭过程,后续又陆陆续续介绍了tomcat内部很多机制以及机制的源码。大家都知道在tomcat启动完毕以后就可以对外提供服务了,如果访问 http://localhost:8080
就可以访问tomcat的主页,那么我们今天就来查看下tomcat是如何处理这个请求的,也就是如何处理http请求的。
之前我们查看了tomcat启动的时候的部分源码(http://www.cnblogs.com/coldridgeValley/p/5631610.html),有的地方没有深入去看。现在我们来重点看下tomcat在启动的过程中如何初始化接收请求的各种组件。
Connector构造函数初始化
从tomcat的架构中我们知道,Connector
组件是用来接收外部请求的,那么我们就从Connector
的初始化来说起。
我们之前的文章说过在启动的过程中会调用Catalina
类的load()
方法,在load()
方法中有解析server.xml
的代码如下:
//代码段1
// Create and execute our Digester
Digester digester = createStartDigester();
在createStartDigester()
方法中关于Connector
的具体代码如下:
//代码段2
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
具体的规则就在ConnectorCreateRule
类中:
//代码段3
@Override
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Service svc = (Service)digester.peek();
Executor ex = null;
if ( attributes.getValue("executor")!=null ) {
ex = svc.getExecutor(attributes.getValue("executor"));
}
//11111
Connector con = new Connector(attributes.getValue("protocol"));
if ( ex != null ) _setExecutor(con,ex);
digester.push(con);
}
在标注1的地方attributes.getValue("protocol")
指的就是server.xml
中<Connector>
标签配置的属性:
//代码段4
<Connector minSpareThreads="10" maxThreads="200" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
所以其实这里传递的是HTTP/1.1
这个字符串。我们继续查看new Connector()
源码。
//代码段5
public Connector(String protocol) {
//1111
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
//222 初始化protocolHanler变量
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
查看标注1的 setProtocol()
方法源码:
//代码段6
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 {
//1111111111
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);
}
}
}
public void setProtocolHandlerClassName(String protocolHandlerClassName) {
this.protocolHandlerClassName = protocolHandlerClassName;
}
默认走的是标注1 的地方,所以最后就是把Connector
类的protocolHandlerClassName
成员变量赋值为org.apache.coyote.http11.Http11Protocol
。
继续查看Connector
构造方法标注2的地方
//代码段7
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
也就是说Connector
的初始化构造函数中把protocolHandler
这个变量初始化为Http11Protocol
的一个实例。(仅针对http请求)
Connector的init()方法初始化
我们在tomcat生命周期管理的文章中部分讲解过关于Connector
的init()
方法,今天我们重新完整查看下:
//代码段8
@Override
protected void initInternal() throws LifecycleException {
//111 调用父类 注册到jmx
super.initInternal();
// Initialize adapter
//222 新建 CoyoteAdapter对象 将新建对象设置到protocolHandler对象中
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 {
//333 调用protocalHander的 init()方法
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
// Initialize mapper listener
//4444 调用mapperListener的init() 方法
mapperListener.init();
}
代码逻辑很简单,稍微梳理下。
- 注册到jmx
- 新建
CoyoteAdapter
对象,设置到protocalHandler
中。(重要 以后会提到) - 调用
protocalHandler
对象的init()
方法 - 调用
mapperListener
的init()
方法 (没啥好看的,就是注册到jmx,至于这个对象是干嘛的,后面会看到)
protocalHandler
是 Http11Protocol
的实例,查看其init()
方法之前先看下Http11Protocol
的继承体系方便我们查看:
最先在AbstractHttp11JsseProtocol
找到了init
方法:
//代码段9
@Override
public void init() throws Exception {
// SSL implementation needs to be in place before end point is
// initialized
sslImplementation = SSLImplementation.getInstance(sslImplementationName);
super.init();
}
调用了super.init()
,所以继续往上找,在AbstractProtocol
中找到init()
方法:
//代码段10
@Override
public void init() throws Exception {
//略代码
try {
//调用endpoint.init()方法
endpoint.init();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.initError",
getName()), ex);
throw ex;
}
}
可以看到调用了endpoint
变量的init()
方法,而endpoint
变量的初始化是在代码段5 初始化变量protocolHandler
的时候进行的。
//代码段11
//调用newInstance方法等于调用该类的无参构造函数
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
所以我们继续查看JIoEndpoint
类的init()
方法,最后在其父类AbstractEndpoint
中找到init()
方法:
//代码段12
public final void init() throws Exception {
testServerCipherSuitesOrderSupport();
if (bindOnInit) {
//调用bind()方法,但是因为AbstractEndpoint类的`bind()`方法为抽象方法,所以这个留给子类实现,继续去类JIoEndpoint中查找 (模版设计模式)
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
我们继续在JIOEndpoint
类中查找bind()
方法:
//代码段13
@Override
public void bind() throws Exception {
// Initialize thread count defaults for acceptor
//初始化 connector 默认起始链接数
if (acceptorThreadCount == 0) {
//如果不配置,默认是0 初始化为1
acceptorThreadCount = 1;
}
// Initialize maxConnections
// 初始化Connector的最大链接数
if (getMaxConnections() == 0) {
// User hasn't set a value - use the default
setMaxConnections(getMaxThreadsExecutor(true));
}
// 初始化 serverSocketFactory 用来处理 socket
if (serverSocketFactory == null) {
if (isSSLEnabled()) {
serverSocketFactory =
handler.getSslImplementation().getServerSocketFactory(this);
} else {
serverSocketFactory = new DefaultServerSocketFactory(this);
}
}
if (serverSocket == null) {
try {
if (getAddress() == null) {
//初始化一个默认跟这个JIOEndpoint管理的socket (工厂设计模式)
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog());
} else {
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog(), getAddress());
}
} catch (BindException orig) {
//异常处理 略
}
}
}
到此就是代码段8protocolHandler
的init()
方法调用完毕,Connector
类的init()
方法调用完毕。
Connector的start()方法初始化
tomcat启动的时候在调用init()
方法以后,会继续调用start()
方法,继续查看,Connector
的start()
方法。
//代码段14
@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 {
//11111
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);
}
//22222
mapperListener.start();
}
查看Http11Protocol
类的start()
方法:
//代码段15
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
//调用 endpoint的start()方法
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
查看JIOEndpoint
类的start()
方法:
//代码段16
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
因为在init()
方法中,bindState
已经初始化,所以直接调用startInternal()
方法,查看JIOEndPoint()
类的startInternal()
方法:
//代码段17
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// Create worker collection
// 创建内部线程池 executor
if (getExecutor() == null) {
//1111
createExecutor();
}
// 初始化 connectionLimitLatch 组件(一个同步组件,给停止的时候释放链接用的)
initializeConnectionLatch();
//2222
startAcceptorThreads();
// Start async timeout thread
Thread timeoutThread = new Thread(new AsyncTimeout(),
getName() + "-AsyncTimeout");
timeoutThread.setPriority(threadPriority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
查看createExecutor()
方法:
//代码段18
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
//自定义的ThreadFactory 主要目的是为了能自定义线程池名称
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
//新建一个线程池,最小线程数量是 getMinSpareThreads() ,最大是 getMaxThreads(),缓存时间60秒,使用 LinkedBlockingQueue 双端无界队列
//如果在server.xml中没有配置的话的,默认情况 getMinSpareThreads()返回10,getMaxThreads()返回200,也就是 tomcat内部线程池默认最小线程数量10,最大200
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
public class TaskQueue extends LinkedBlockingQueue<Runnable>
继续查看 startAcceptorThreads()
方法:
//代码段19
protected final void startAcceptorThreads() {
//在代码段13中 acceptorThreadCount 变量被初始化为1
int count = getAcceptorThreadCount();
//新建Acceptor 数组
acceptors = new Acceptor[count];
//for 循环赋值
for (int i = 0; i < count; i++) {
//赋值11111
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
//传递任务 启动
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
public int getAcceptorThreadCount() { return acceptorThreadCount; }
可以看到 代码段19 主要做的就是新建了一个Acceptor
数组并且赋值启动,而从代码中可以知道 createAcceptor()
方法返回的是一个Runnable
的子类,我们查看createAcceptor()
方法:
//代码段20 在JIOEndpoint中
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
JIOEndpoint的内部类
*/
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
//各种条件判断略
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
countUpOrAwaitConnection();
Socket socket = null;
try {
// Accept the next incoming connection from the server
// socket
//1111111111
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
//异常处理 略
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused && setSocketOptions(socket)) {
// Hand this socket off to an appropriate processor
//222222222
if (!processSocket(socket)) {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} else {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} catch (IOException x) {
//异常处理 略
} catch (NullPointerException npe) {
//异常处理 略
} catch (Throwable t) {
//异常处理 略
}
}
state = AcceptorState.ENDED;
}
}
原本的源码比较长,稍微删减了一些并且加上了注释。
在标注1的代码中,serverSocketFactory
和serverSocket
变量都在代码段13中进行了初始化,所以我们查看下acceptSocket()
方法。
//代码段21
@Override
public Socket acceptSocket(ServerSocket socket) throws IOException {
return socket.accept();
}
很简单就是调用了socket
的accept()
方法,但是众所周知accept()
是阻塞的方法,所以代码段19中新建的线程就会阻塞的这里,那这个阻塞是干什么的呢? 其实这个阻塞就是在接收外部的请求,紧接着就是标注2的地方开始处理请求了,具体的处理请求流程我们在下一篇文章中继续。
到此就是代码段14的Connector
的start()
方法中protocolHandler.start()
方法的源码我们都看完了,我们继续查看start()
方法中的mapperListener.start()
的源码。
//代码段22
/**
* Mapper.
*/
protected Mapper mapper = new Mapper();
/**
* Mapper listener.
*/
protected MapperListener mapperListener = new MapperListener(mapper, this);
@Override
public void startInternal() throws LifecycleException {
//设置生命周期状态
setState(LifecycleState.STARTING);
// Find any components that have already been initialized since the
// MBean listener won't be notified as those components will have
// already regis tered their MBeans
// 设置 defaultHostName 变量
findDefaultHost();
//将engine 以及engine以下所有的自容器全部添加监听器 mapperListener
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
//11111111
registerHost(host);
}
}
}
/**
* 获取默认的 host
*/
private void findDefaultHost() {
//先获取Engine
Engine engine = (Engine) connector.getService().getContainer();
//获取Engine对象中配置的 defaultHost 变量 ( server.xml 中配置的)
String defaultHost = engine.getDefaultHost();
boolean found = false;
if (defaultHost != null && defaultHost.length() >0) {
//获取engine的所有 子容器(Host)
Container[] containers = engine.findChildren();
for (Container container : containers) {
Host host = (Host) container;
//遍历所有Host,如果Engine中默认的host名称和 当前host名称相同,设置found为true
if (defaultHost.equalsIgnoreCase(host.getName())) {
found = true;
break;
}
String[] aliases = host.findAliases();
//同上 只是使用host的别名查找
for (String alias : aliases) {
if (defaultHost.equalsIgnoreCase(alias)) {
found = true;
break;
}
}
}
}
if(found) {
//如果 存在就 设置 defaultHostName 变量
mapper.setDefaultHostName(defaultHost);
} else {
log.warn(sm.getString("mapperListener.unknownDefaultHost",
defaultHost, connector));
}
}
/**
* Add this mapper to the container and all child containers
*
* @param container
*/
private void addListeners(Container container) {
//把mapperListener添加到该容器的监听器中
container.addContainerListener(this);
container.addLifecycleListener(this);
//遍历子容器
for (Container child : container.findChildren()) {
//递归调用该方法,也就是把mapperListener设置到所有的容器中
addListeners(child);
}
}
可以看到mapperListener
的start()
方法中简单的部分都直接添加了注释,还有标注1的地方没有说明。标注1的地方registerHost()
就不深入查看了,主要原因是内部代码太过繁琐,但是逻辑很清楚,所以这里就直接讲解原理,不查看源码分析了。registerHost()
主要作用是把Host
,Context
,Wrapper
容器全部注册到mapperListener
中,以供后续方法使用!
我们查看下MapperListener
类的构造:
//代码段23
/**
* Associated mapper.
*/
private Mapper mapper = null;
/**
* Associated connector
*/
private Connector connector = null;
可以看出mapperListener
和Connector
互相持有对方的引用。继续看Mapper
类:
/**
* Array containing the virtual hosts definitions.
*/
Host[] hosts = new Host[0];
Mapper
内部持有Host
数组,而这个Host
并非我们常说的 那个容器类Host
:
protected static final class Host extends MapElement {
public volatile ContextList contextList;
}
这个Host
是Mapper
的一个内部类,持有了一个ContextList
对象,继续查看:
protected static final class ContextList {
public final Context[] contexts;
}
ContexetList
依旧是Mapper
的一个内部类,持有了一个Context
数组,继续查看Context
类的定义:
protected static final class Context extends MapElement {
public volatile ContextVersion[] versions;
public Context(String name, ContextVersion firstVersion) {
super(name, null);
versions = new ContextVersion[] { firstVersion };
}
}
Context
是Mapper
的一个内部类,持有了ContextVersion
的数组,查看ContextVersion
:
protected static final class ContextVersion extends MapElement {
public String path = null;
public int slashCount;
public String[] welcomeResources = new String[0];
public javax.naming.Context resources = null;
public Wrapper defaultWrapper = null;
public Wrapper[] exactWrappers = new Wrapper[0];
public Wrapper[] wildcardWrappers = new Wrapper[0];
public Wrapper[] extensionWrappers = new Wrapper[0];
public int nesting = 0;
public boolean mapperContextRootRedirectEnabled = false;
public boolean mapperDirectoryRedirectEnabled = false;
private volatile boolean paused;
}
可以看到,ContextVersion
中持有了 3个Wrapper
的数组。具体说到这里可以看出 MapperListener
内部包含了所有的Host
以及Host
的所有自容器,一直到Wrapper
,也许看起来不是很直观,我们debug下看下最终MapperListener
的结构:
到此为止,整个Connector
从构造函数初始化,到启动的时候调用init()
方法一直到初始化完毕准备接收外部请求的准备工作的源码已经查看完毕,tomcat处理请求的源码我们下篇文章继续查看。