本文是在阅读博客以后整理的一些知识笔记,方便自己再次回顾,也提供给需要的伙伴。
-
tomcat 架构图

LiftCycle接口是一个公用的顶层接口,定义了组件生命周期的一些方法,用于启动、停止Catalina组件。组件的生命周期包括:init、start、stop、destory,以及定义了各种事件的常量,操作LifecycleListener的API,是一个典型的观察者模式。各大组件均实现了Lifecycle接口:
LifecycleBase:它实现了Lifecycle的init、start、stop等主要逻辑,向注册在LifecycleBase内部的LifecycleListener发出对应的事件,并预留了initInternal、startInternal、stopInternal等模板方法,便于子类完成自己的逻辑
- MBeanRegistration:JmxEnabled的父类,jmx框架提供的注册MBean的接口,引入此接口是为了便于使用JMX提供的管理
- LifecycleMbeanBase:JmxEnable的子类,通过重写initInternal、destroyInternal方法,统一向jmx中注册、取消注册当前实例,方便利用jmx对实例对象进行管理,代码上特别强调要求子类先行调用super.initInternal
- ContainerBase、standardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都继承了LifecycleMBeanBase,因此这些容器都具有了同样的生命周期并可以通过JMX进行管理。
依赖关系图:
具体生命周期的处理如下:
-
初始化流程

-
启动流程
-
处理请求流程
http 请求是基于socket 技术实现,在tomcat中请求由connetor容器处理,其中最核心的逻辑在Jioendpoint类中; 在启动流程阶段已经完成对连接处理器的初始化工作,此处详细描述一下
public void init() throws Exception {
............
// Initialize thread count defaults for acceptor
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1;
}
..............
}
public void start() throws Exception {
.............
// Start acceptor threads
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
..............
}
适配层类实现了runable 接口
/**
* Server socket acceptor thread.
*/
protected class Acceptor implements Runnable {
/**
* 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 (running) {
// Loop if endpoint is paused
while (paused) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
// Accept the next incoming connection from the server socket
try {
Socket socket = serverSocketFactory.acceptSocket(serverSocket);
serverSocketFactory.initSocket(socket);
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) {
// Close socket right away
try {
socket.close();
} catch (IOException e) {
// Ignore
}
}
}catch ( IOException x ) {
if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
} catch (Throwable t) {
log.error(sm.getString("endpoint.accept.fail"), t);
}
// The processor will recycle itself when it finishes
}
}
}
大体流程是serverSocketFactory.acceptSocket 方法中通过工厂类得到socket 对象,然后把socket对象交给processSocket 方法处理,每当接收线程接收到客户端链接时,就从线程池中取出一个工作线程(Work)来处理socket通信;
/**
* processSocket
*/
protected boolean processSocket(Socket socket) {
try {
if (executor == null) {
getWorkerThread().assign(socket);
} else {
executor.execute(new SocketProcessor(socket));
}
} catch (Throwable 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;
}
/**
* 当没有空闲的工作线程时,当前线程阻塞,直到线程池//workers.size()>0,
*/
protected Worker getWorkerThread() {
// Allocate a new worker thread
synchronized (workers) {
Worker workerThread;
while ((workerThread = createWorkerThread()) == null) {
try {
workers.wait();
} catch (InterruptedException e) {
// Ignore
}
}
return workerThread;
}
}
protected Worker createWorkerThread() {
synchronized (workers) {
if (workers.size() > 0) {
curThreadsBusy++;
return workers.pop();
}
if ((maxThreads > 0) && (curThreads < maxThreads)) {
curThreadsBusy++;
if (curThreadsBusy == maxThreads) {
log.info(sm.getString("endpoint.info.maxThreads",
Integer.toString(maxThreads), address,
Integer.toString(port)));
}
return (newWorkerThread());
} else {
//从此处代码可以看出,如何我们修改server.xml文件connector的maxThreads属性为负数,则线程数可能会一直增加。
if (maxThreads < 0) {
curThreadsBusy++;
return (newWorkerThread());
} else {
return (null);
}
}
}
}
这段代码中涉及worksocket 的创建,以及http协议的解析,最终会把资源封装到httpsevletRequest 对象中,主要是通过CoyoteAdapter 类来实现的把资源请求交给containor容器处理的,CoyoteAdapter类的service方法
/**
* Service method.
*/
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);//判断是否绑定了HttpServletRequest
Response response = (Response) res.getNote(ADAPTER_NOTES);//判断是否绑定了HttpServletResponse
if (request == null) {
// Create objects
request = connector.createRequest();//连接器创建一个http请求对象request
request.setCoyoteRequest(req);//设置该http请求对象的真正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());
if (postParseRequest(req, request, res, response)) {//将当前http请求对象request与相关的host,Context、Wrapper对象引用相绑定。
//check valves if we support async
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);//将真正的请求处理交给Engine的Pipeline去处理
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)) {
comet = true;
res.action(ActionCode.ACTION_COMET_BEGIN, null);
}
} else {
comet = true;
res.action(ActionCode.ACTION_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 && asyncConImpl.getState()==AsyncContextImpl.AsyncState.STARTED) {
res.action(ActionCode.ACTION_ASYNC_START, request.getAsyncContext());
async = true;
} else if (request.isAsyncDispatching()) {
asyncDispatch(req, res, SocketStatus.OPEN);
if (request.isAsyncStarted()) {
async = true;
res.action(ActionCode.ACTION_ASYNC_START, request.getAsyncContext());
}
} else if (!comet) {
response.finishResponse();
req.action(ActionCode.ACTION_POST_REQUEST , null);
}
} catch (IOException e) {
// Ignore
} catch (Throwable t) {
log.error(sm.getString("coyoteAdapter.service"), t);
} finally {
req.getRequestProcessor().setWorkerThreadName(null);
// Recycle the wrapper request and response
if (!comet && !async) {
request.recycle();
response.recycle();
} else {
// Clear converters so that the minimum amount of memory
// is used by this processor
request.clearEncoders();
response.clearEncoders();
}
}
}
-
if (postParseRequest(req, request, res, response)) {//将当前http请求对象request与相关的host,Context、Wrapper对象引用相绑定。
- 2.connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);//将真正的请求处理交给Engine的Pipeline去处理:
connector.getService() :方法拿到的是connector相关联的service容器对象默认的都是StandardService,
getContainer():方法拿到的是StandardEngine对象。这些对象是在初始化阶段完成容器之间的关系绑定。StandardEngine对象调用getPipeline()方法,因为StandardEngine类继承了ContainerBase类所以getPipeline()方法执行的是父类的方法。
小结:tomcat 中的请求经过连接器的处理转换以后最终是要通过通道的方式经过container host context wapper 链式处理,返回也是同样
管道与阀门
Pipeline是管道组件,用于封装一组有序Valve,便于Valve顺序地传递或者处理请求;
Valve是阀门组件,穿插在Container容器中,可以把它理解成请求拦截器,在tomcat接收网络请求与触发Servlet之间执行;
ContainerBase中包含了一个Pipeline实例,默认实现StandardPipeline; 查看父类的代码org.apache.catalina.core.ContainerBase的getPipeline方法如下:
getPipeline()返回的StandardPipeline对象。从这里可以看出StandardEngine,StandardHost,StandardContext,StandardWrapper标准容器类都继承了Containerbase类,每个容器对象都有一个管道对象,每个管道对象必然会有一个或多个的阀门。也就是说容器包含管道,管道包含阀门。
tomcat为我们提供了一系列的Valve阀门:
- AccessLogValve,记录请求日志,默认会开启
- RemoteAddrValve,可以做访问控制,比如限制IP黑白名单
- RemoteIpValve,主要用于处理 X-Forwarded-For 请求头,用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段
前面我们分析了 StandardEngine 的启动逻辑,它会启动其子容器 StandardHost,接下来我们看下 StandardHost 的 start 逻辑。其实, StandardHost 重写的 startInternal 方法主要是为了查找报告错误的 Valve 阀门
-
停止流程
org.apache.catalina.startup.Catalina类中stop方法
public void stop() { try { // Remove the ShutdownHook first so that server.stop() // doesn't get invoked twice if (useShutdownHook) { Runtime.getRuntime().removeShutdownHook(shutdownHook); // If JULI is being used, re-enable JULI's shutdown to ensure // log messages are not lost LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( true); } } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } // Shut down the server try { Server s = getServer(); LifecycleState state = s.getState(); if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 && LifecycleState.DESTROYED.compareTo(state) >= 0) { // Nothing to do. stop() was already called } else { s.stop(); s.destroy(); } } catch (LifecycleException e) { log.error("Catalina.stop", e); } }
移除完关闭钩子后,守护线程
1.调用stop()方法,和启动过程类似,会先后调用StandardServer和StandardService类的stopInternal()方法、加锁暂停HTTP Connector和AJP Connector的连接、加锁停止StandardEngine、再加锁停止HTTP Connector和AJP Connector的连接、调用NamingResources的stopInternal()方法等。
2.调用destroy()方法,会先后调用StandardServer和StandardService类的destroyInternal()方法、加锁销毁Connector、加锁销毁StandardEngine、销毁NamingResources等。
-
销毁流程
各组件的destroy()方法执行完后要调用父类org.apache.catalina.util.LifecycleMBeanBase中的destroyInternal(),这意味着生命周期的结束