一:初始化init
1.在Bootstrap里执行主函数main,初始化commonLoader,catalinaLoader,sharedLoader为URLClassLoader,他的父类加载器是系统类加载器AppClassLoader,设置当前线程类加载器为catalinaLoader,表示下面那些类的加载用我们创建的类加载器加载,而不是使用默认是系统加载器加载,这种方式经常用于开源框架里。初始化Catalina类并且反射设置他的parentClassLoader属性为当前类加载器,保存Catalina实例catalinaDaemon,bootstrap实例daemon
2.调用catalina的load方法,创建新的Digester主要解析xml文件并且进行类实例化,规则的主要抽象类Rule,他的begin,body,end,finish主要对应xml标签解析的过程。
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer","className");
digester.addSetProperties("Server");
digester.addSetNext("Server","setServer","org.apache.catalina.Server");
开始创建Digester只设置了\conf\server.xml文件所需要的属性,上面代码对应于<Server port="8005" shutdown="SHUTDOWN">,后面会大量使用这种方式进行操作。digester.parse(inputSource);解析这个文件,初始化Server为StandardServer,设置监听器Listener,他们都有同一个接口LifecycleListener,但又不同的事件发生可以处理不同情况,这个在tomcat中很重要,因为很多很多操作都需要需要依赖监听器来执行,设置Service为StandardService,它包含两个Connector和MapperListener,tomcat8版本默认使用NIO处理http链接Connector,另一个Connector负责和其他的HTTP服务器建立连接,Engine为StandardEngine也就是最初的容器,容器共有的父类ContainerBase,开始设置冠道处理pipeline,Host为StandardHost,Valve等元素。
3.启动StandardServer初始化init,自身有6个监听器VersionLoggerListener,AprLifecycleListener,JreMemoryLeakPreventionListener,GlobalResourcesLifecycleListener,ThreadLocalLeakPreventionListener,NamingContextListener,触发before_init前事件,注册JMX,通过jconsole工具可以修改查看。tomcat基本上重要的对象都注册在这里面。
4.启动StandardService初始化init,启动StandardEngine初始化init,监听器EngineConfig,创建容器开始结束线程池,启动mapperListener初始化init,启动Connector初始化init,创建CoyoteAdapter并设置协议处理器Http11NioProtocol,启动Http11NioProtocol初始化init,启动NioEndpoint初始化init,这个endpoint是协议链接Http11NioProtocol初始化时设置的,绑定网络连接服务端,设置SHARED_SELECTOR = Selector.open();启动BlockPoller线程,触发after_init事件。
public void bind() throws Exception {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
serverSock.configureBlocking(true); //mimic APR behavior
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {
//minimum one poller thread
pollerThreadCount = 1;
}
setStopLatch(new CountDownLatch(pollerThreadCount));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open();
}
二:
启动start
5.Bootstrap.start-》Catalina.start-》StandardServer.start()每一次状态的变动都会触发那六个监听器的处理-》StandardService.start();先启动StandardEngine.start-》Cluster.start-》Realm.start,用线程池异步启动StandardHost,这里的方法都在容器共同的父类ContainerBase里,可以递归调用
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
public Void call() throws LifecycleException {
child.start();
return null;
}
这个时候StandardHost的状态为new,状态监听器为HostConfig,创建开始结束线程池,给管道增加处理器ErrorReportValve,getPipeline().addValve(valve);
这时StandardHost没有子容器,开始启动管道StandardPipeline.start(),循环启动AccessLogValve,他就是这个文件localhost_access_log.xxxx.txt的来源,ErrorReportValve->StandardHostValve
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
6. StandardHost设置状态setState(LifecycleState.STARTING);-》
HostConfig接收事件触发start
if (host.getDeployOnStartup())
deployApps();
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);
}
单纯启动tomcat没有war包,异步deployDirectories五个目录webapps下docs,examples,host-manager,manager,ROOT,他们就是StandardContext,也就是host的子容器,es.submit(new DeployDirectory(this, cn, dir))
public void run() {
config.deployDirectory(cn, dir);
}
解析xml文件构建 context = (Context) digester.parse(xml);文件路径"META-INF/context.xml",这个digester是host容器的,给他添加ContextConfig监听,把自己加入host子容器里,给Context添加监听MemoryLeakTrackingListener,启动context .start();
,触发事件,创建线程池,创建自己Context的Digester contextDigester = createContextDigester();解析META-INF/context.xml,创建new ApplicationContext(this)设置ApplicationContextFacade,StandardRoot,启动StandardRoot.start();DirResourceSet.start();给Context设置类加载器WebappLoader并且添加监听NamingContextListener,启动WebappLoader.start(),
通过createClassLoader()创建WebappClassLoaderBase并启动ParallelWebappClassLoader.start(),启动配置事件"configure_start",创建专属解析WebXmlParser,这里会获取全局的web.xml和host本身默认的文件并解析,再获取context自身的"/WEB-INF/web.xml"解析,给StandardContext设置解析到的属性configureContext(WebXml webxml) ,给每个Servlet创建容器StandardWrapper,并把它添加到StandardContext的子容器中
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
context.addChild(wrapper);
}
7.和前面类似,子容器StandardWrapper.start(),创建线程池,启动管道
StandardWrapperValve.start()。处理一些重要的配置后启动
管道
StandardContextValve
.start(),启动StandardManager.start(),创建new StandardSessionIdGenerator()用来生成sessionID并启动,加载Servlet并且初始化wrapper.load();这里面设置了很多属性。最后返回HostConfig保存记录,各个文件的最后修改时间等等。
启动
StandardEngine的
管道
StandardEngineValve
.start(),监听
EngineConfig,具体应用容器启动完毕
8.mapperListener.start(),把他递归设置到所有的容器中(一个StandardEngine(一个Standardhost(五个StandardContext(ROOT(default,jsp),/examples(没有子容器)/host-manager(default,jsp,HostManager,HTMLHostManager),/manager(Status,JMXProxy,default,jsp,Manager,HTMLManager),/docs(default,jsp))))把容器信息存进Mapper对象中
private void addListeners(Container container) {
container.addContainerListener(this);
container.addLifecycleListener(this);
for (Container child : container.findChildren()) {
addListeners(child);
}
}
private void registerHost(Host host) {
String[] aliases = host.findAliases();
mapper.addHost(host.getName(), aliases, host);
for (Container container : host.findChildren()) {
if (container.getState().isAvailable()) {
registerContext((Context) container);
}
}
}
private void registerContext(Context context) {
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
Host host = (Host)context.getParent();
WebResourceRoot resources = context.getResources();
String[] welcomeFiles = context.findWelcomeFiles();
List<WrapperMappingInfo> wrappers = new ArrayList<>();
for (Container container : context.findChildren()) {
prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
}
mapper.addContextVersion(host.getName(), host, contextPath,
context.getWebappVersion(), context, welcomeFiles, resources,
wrappers);
}
9.启动链接connector.start(),首先启动的是用于http链接的Http11NioProtocol.start(),
public void start() throws Exception {
endpoint.start();
// Start async 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();
}
启动网络接收服务类NioEndpoint
.start(),自定的三个SynchronizedStack用于存储请求,作为一个缓冲,创建线程池,初始化最大连接阀门LimitLatch,它是根据AbstractQueuedSynchronizer扩展的,和CountDownLatch刚好相反,前者获取是递加,后者获取是递减,默认启动两个poller,一个对外接收请求的线程Acceptor,NioEndpoint启动完毕
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
// Create worker collection
if ( getExecutor() == null ) {
createExecutor();
}
initializeConnectionLatch();
// Start poller threads
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
startAcceptorThreads();
}
}
继续启动检查超时链接的线程AsyncTimeout,至此一个链接
connector启动完成,另一个用于和其他http链接的AJP协议的链接和上面类似。
StandardService启动完成,StandardServer发送最后启动事件"after_start"给那六个监听器进行处理,
10.添加关闭钩子Runtime.getRuntime().addShutdownHook(shutdownHook)做一些结尾工作,StandardServer监听端口获取"SHUTDOWN"命令。
if (await) {
await();
stop();
}
awaitSocket = new ServerSocket(port, 1,
InetAddress.getByName(address));