源码地址:https://gitee.com/yangaizy/my-tomcat8.5
学习tomcat这类有配置文件的源码时,可以从配置文件上看整体架构然后进去学习源码。
一 tomcat架构概览
1.架构图:
二、tomcat生命周期
1.LifeCycle类
* start()
* -----------------------------
* | |
* | init() |
* NEW -»-- INITIALIZING |
* | | | | ------------------«-----------------------
* | | |auto | | |
* | | \|/ start() \|/ \|/ auto auto stop() |
* | | INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»--- |
* | | | | |
* | |destroy()| | |
* | --»-----«-- ------------------------«-------------------------------- ^
* | | | |
* | | \|/ auto auto start() |
* | | STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»-----
* | \|/ ^ | ^
* | | stop() | | |
* | | -------------------------- | |
* | | | | |
* | | | destroy() destroy() | |
* | | FAILED ----»------ DESTROYING ---«----------------- |
* | | ^ | |
* | | destroy() | |auto |
* | --------»----------------- \|/ |
* | DESTROYED |
* | |
* | stop() |
* ----»-----------------------------»------------------------------
*
2.LifecycleBase
运用模版方法设计模式定义了上图的start,stop,init,destroy等方法
3.LifecycleMBeanBase
定义了将tomcat组件注册到jmx中的方法。
4.ContainerBase
添加子容器方法:addChild
该方法被digester的createStartDigester方法解析配置文件时调用。
org.apache.catalina.startup.HostRuleSet#addRuleInstances
作为LifeCycleBase的子类,会实现xxInterval(xx为start,init等)方法用于生命周期处理。
threadStart(启动后台线程)
org.apache.catalina.core.ContainerBase#threadStart
// -------------------- Background Thread --------------------
/**
* Start the background thread that will periodically check for session timeouts.
*/
protected void threadStart() {
if (thread != null) {
return;
}
//只有设置了后台线程间隔才会启动后台线程
if (backgroundProcessorDelay <= 0) {
return;
}
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
}
只有设置了后台线程间隔才会启动后台线程处理任务
org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor#processChildren
子容器如果没有设置后台线程间隔,父容器会帮忙解决
org.apache.catalina.Container#backgroundProcess
给容器自己实现的,用于处理周期性任务。
5.LifeCycleListener
生命周期事件监听器
lifecycleEvent
org.apache.catalina.LifecycleListener#lifecycleEvent
用于处理特定的生命周期事件方法
三、Tomcat应用启动
Bootstrap启动
BootStrap的main方法是整个tomcat的入口。其init方法创建了真正干活的是由单独的类加载器反射而来的Catalina类:
public void init() throws Exception {
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled()) {
log.debug("Loading startup class");
}
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled()) {
log.debug("Setting startup class properties");
}
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
Catalina重点是load和start方法:
3.catalina不一定需要存在,因为catalina的主要作用还是就是解析server.xml初始化组件(包含容器)树,然后调用start方法启动server,
四:类加载器相关:
BootStap初始化得到的三个类加载器都是一样的:
Catalina的父加载器是图上的sharedLoader
五:Server组件:
org.apache.catalina.core.StandardServer
核心方法:
addService
await:
org.apache.catalina.core.StandardServer#await
设置一个socket连接等待远程关闭指令。
initInternal
org.apache.catalina.core.StandardServer#initInternal
六:connector组件
org.apache.catalina.connector.Connector
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
//重点
ProtocolHandler p = null;
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"), e);
} finally {
this.protocolHandler = p;
}
if (Globals.STRICT_SERVLET_COMPLIANCE) {
uriCharset = StandardCharsets.ISO_8859_1;
} else {
uriCharset = StandardCharsets.UTF_8;
}
// Default for Connector depends on this (deprecated) system property
if (Boolean
.parseBoolean(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false"))) {
encodedSolidusHandling = EncodedSolidusHandling.DECODE;
}
}
Connector的startInterval中启动了ProtocalHandler:
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPortWithOffset() < 0) {
throw new LifecycleException(
sm.getString("coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
ProtocalHandler的重点方法:
子类:AbstractProtocol
构造函数
public AbstractProtocol(AbstractEndpoint<S, ?> endpoint) {
this.endpoint = endpoint;
ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
setHandler(cHandler);
getEndpoint().setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
start方法:
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
logPortOffset();
}
endpoint.start();
// Start timeout thread
asyncTimeout = new AsyncTimeout();
Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
int priority = endpoint.getThreadPriority();
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
priority = Thread.NORM_PRIORITY;
}
timeoutThread.setPriority(priority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
子类:AbstractHttp11Protocol
构造函数
public AbstractHttp11Protocol(AbstractEndpoint<S,?> endpoint) {
super(endpoint);
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
}
由此可见tomcat超时时间设置为60s.
createProcessor 创建关于Http协议处理的处理器
Override
protected Processor createProcessor() {
Http11Processor processor = new Http11Processor(this, getEndpoint());
processor.setAdapter(getAdapter());
processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
processor.setDisableUploadTimeout(getDisableUploadTimeout());
processor.setRestrictedUserAgents(getRestrictedUserAgents());
processor.setMaxSavePostSize(getMaxSavePostSize());
return processor;
}
子类Http11NioProtocol
仅仅添加了(“http-nio”)名字前缀。
成员AbstractEndpoint
setMaxConnections
private int maxConnections = 8*1024;
public void setMaxConnections(int maxCon) {
this.maxConnections = maxCon;
LimitLatch latch = this.connectionLimitLatch;
if (latch != null) {
// Update the latch that enforces this
if (maxCon == -1) {
releaseConnectionLatch();
} else {
latch.setLimit(maxCon);
}
} else if (maxCon > 0) {
initializeConnectionLatch();
}
}
用于处理的maxConnection的限流工具LimitLatch :
/**
* counter for nr of connections handled by an endpoint
*/
private volatile LimitLatch connectionLimitLatch = null;
成员NioEndpoint
startInternal
启动方法,创建线程池,启动监听客户端线程Acceptor
/**
* Start the NIO endpoint, creating acceptor, poller threads.
*/
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
if (socketProperties.getProcessorCache() != 0) {
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
}
if (socketProperties.getEventCache() != 0) {
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
}
if (socketProperties.getBufferPool() != 0) {
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
}
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
// Start poller thread
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-Poller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
startAcceptorThread();
}
}
org.apache.tomcat.util.net.Acceptor#run
该方法负责处理客户端的链接请求
setSocketOptions
设置和客户端相连socket信息以及将已经建立连接的socket包装成pollerEvent给Poller线程处理
protected boolean setSocketOptions(SocketChannel socket) {
NioSocketWrapper socketWrapper = null;
try {
// Allocate channel and wrapper
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, this);
} else {
channel = new NioChannel(bufhandler);
}
}
NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
channel.reset(socket, newWrapper);
connections.put(socket, newWrapper);
socketWrapper = newWrapper;
// Set socket properties
// Disable blocking, polling will be used
socket.configureBlocking(false);
socketProperties.setProperties(socket.socket());
socketWrapper.setReadTimeout(getConnectionTimeout());
socketWrapper.setWriteTimeout(getConnectionTimeout());
socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
poller.register(socketWrapper);
return true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error(sm.getString("endpoint.socketOptionsError"), t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
if (socketWrapper == null) {
destroySocket(socket);
}
}
// Tell to close the socket if needed
return false;
}
给和客户端连接设置tcp参数的方法是:
org.apache.tomcat.util.net.SocketProperties#setProperties(java.net.Socket)
七、Tomcat NIo 操作
Tomcat Nio核心组件:
Poller
用来处理accept后的socket相关事件
其包含了PollerEvent的events看似是无界队列,实则由LimitLatch的大小限制。
run方法
org.apache.tomcat.util.net.NioEndpoint.Poller#run
处理PollerEvent事件以及发现io事件
processSocket
org.apache.tomcat.util.net.AbstractEndpoint#
将待读写的socket事件包装成SocketProcessor丢给线程池
SocketProcessor
用于真正处理io事件,io事件被poller线程丢给SocketProcessor,然后被线程池执行。
doRun()
org.apache.tomcat.util.net.NioEndpoint.SocketProcessor#doRun
处理基本信息然后把数据交给ConnectionHandler处理
ConnectionHandler
process
org.apache.coyote.AbstractProtocol.ConnectionHandler#process
处理基本信息然后把数据交给AbstractProcessorLight处理
AbstractProcessorLight
process
org.apache.coyote.AbstractProcessorLight#process
定义了处理http请求的基本操作,调用子类的service方法
Http11Processor
HttpParser类
用于处理http协议解析
Request、Response类
原生请求、响应
service()
org.apache.coyote.http11.Http11Processor#service
用于处理http header然后调用CoyoteAdapter适配成容器需要的请求头和响应头
CoyoteAdapter
service()
org.apache.catalina.connector.CoyoteAdapter#service
用于将原生request适配成引擎层需要的httprequest,然后传输到engine层
八、Engine(StandardEngine类)
继承自ContainerBase的生命周期方法(start,init,stop...)
构造函数:
org.apache.catalina.core.StandardEngine#StandardEngine
初始化了Pipeline的basic。
EnginConfig
engine生命周期监听器
九、Pipeline(StandardPipeline)
Pipeline概述:
主要负责了valve的增删改查
basic是最重要的valve
每个容器默认就带有了Pipeline:
pipeline和容器互相持有引用。
Valve
invoke是Valve子类执行相关任务的关键。
ErrorReportValve
org.apache.catalina.valves.ErrorReportValve#invoke
这个valve用来记录错误信息并输出。
十、Tomcat重要方法类
SystemLogHandler
public SystemLogHandler(PrintStream wrapped) {
super(wrapped);
out = wrapped;
}
protected void initStreams() {
// Replace System.out and System.err with a custom PrintStream
System.setOut(new SystemLogHandler(System.out));
System.setErr(new SystemLogHandler(System.err));
}
可用于重定向输出流,也可以说是捕获了输出,实现原理是重写PrintStream的方法。
/**
* Find PrintStream to which the output must be written to.
* @return the print stream
*/
protected PrintStream findStream() {
Queue<CaptureLog> stack = logs.get();
if (stack != null && !stack.isEmpty()) {
CaptureLog log = stack.peek();
if (log != null) {
PrintStream ps = log.getStream();
if (ps != null) {
return ps;
}
}
}
return out;
}
WebappLoader
十一、Host
继承自ContainerBase的生命周期方法(start,init,stop...)
HostConfig
lifecycleEvent
org.apache.catalina.startup.HostConfig#lifecycleEvent
deployApps
org.apache.catalina.startup.HostConfig#deployApps()
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
*/
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
部署context应用以及热部署
org.apache.catalina.startup.HostConfig#deployDescriptor