tomcat 8 源码分析 ,本文主要讲解tomcat拥有哪些组件,容器,又是如何启动的

推荐访问我的个人网站,排版更好看呦: https://chenmingyu.top/tomcat-source-code/

tomcat

简介

​ Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,Tomcat服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器。

整体架构

首先我们先看一张图



看上图总结一下tomcat的组件主要包括:

  • server:整个servlet容器,一个tomcat对应一个server,一个server包含多个service

    server在tomcat中的实现类是:StandardServer

  • service: 一个service包含多个connector(接受请求的协议),和一个container(容器)

    多个connector共享一个container容器,

    service在tomcat中的实现类是:StandardService

  • connector:链接器,负责处理客户端请求,解析不同协议及io方式

  • executor:线程池

  • container:包含engine,host,context,wrapper等组件

  • engine:servlet引擎,container容器中顶层的容器对象,一个engine可以包含多个host主机

    engine在tomcat中的实现类是:StandardEngine

  • host:engine容器的子容器,一个host对应一个网络域名,一个host包含多个context

    host在tomcat中的实现类是:StandardHost

  • context:host容器的子容器,表示一个web应用

    context在tomcat中的实现类是:StandardContext

  • wrapper:tomcat中最小的容器单元,表示web应用中的servlet

    wrapper在tomcat中的实现类是:StandardWrapper

所以tomcat的组件结构大概是这个样子的:

生命周期:Lifecycle

tomcat的启动过程非常规范,使用Lifecycle接口统一管理各组件的生命周期,根据各个组件之间的父子级关系,首先调用init()方法逐级初始化各组件,然后在调用start()的方法进行启动;

Lifecycle接口提供的方法如下,提供了init,start,destory等方法:

tomcat中的组件基本都继承了LifecycleMBeanBase类,LifecycleMBeanBase集成LifecycleBase,LifecycleBase实现Lifecycle接口:

LifecycleBase重写Lifecycle接口,比如init()方法,在init()方法中调用initInternal()方法,initInternal()方法是抽象方法,具体实现交由各个子类(组件)去实现。如果没有实现initInternal()方法,则调用默认的LifecycleMBeanBase的initInternal方法。

启动过程

接下来从源码看一下tomcat的启动流程:

bootstrap

tomcat的入口类为BootStrap的main方法

Bootstrap中main()方法如下,不重要的代码省略了

/**
* Main method and entry point when starting Tomcat via the provided
* scripts.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
.....
//初始化
bootstrap.init();
.....
if (command.equals("startd")) {
args[args.length - 1] = "start";
//实例化各组件 调用Catalina类的load方法
daemon.load(args);
//启动各组件 调用Catalina类的start方法
daemon.start();
}
.....
}

bootstrap.init()的工作是初始化Bootstrap类,包含初始化类加载器

/**
* Initialize daemon.
* @throws Exception Fatal initialization error
*/
public void init() throws Exception {
//初始化类加载
initClassLoaders();
......
//实例化Catalina类
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
......
catalinaDaemon = startupInstance; }
Catalina

接着调用刚初始化的Catalina类的实例catalinaDaemon的load()方法,重要的就两点

/**
* Start a new server instance.
*/
public void load() {
.....
// Digester... 实例化组件
Digester digester = createStartDigester();
.....加载server.xml......
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
......
// 初始化sever
getServer().init();
}
Digester

Digester是一种将xml转化为java对象的事件驱动型工具,通过读取xml文件,当识别到特定的节点的时候会执行特定的动作,创建java对象或者执行对象的某个方法

通过Digester去创建了Catania中的大量初始化工作,具体详见源码:

// 创建server实例
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//创建Executor
digester.addObjectCreate("Server/Service/Executor",
"org.apache.catalina.core.StandardThreadExecutor",
"className");
...等等大量初始化工作...

接着讲,getServer().init()方法的作用是初始化Sever,调用LifecycleBase的init()方法,在init方法中调用的是StandardServer类initInternal()方法

StandardServer

StandardServer类图如下:

StandardServer类initInternal()方法:

/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException { super.initInternal();
...省略很多,但是主要的在下面...
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
//调用services的init
services[i].init();
}
}

前面的时候讲过一个server初始化多个services;

StandardService

services[i].init();初始化的是StandardService类,类图如下

StandardService的initInternal() 方法的工作是初始化engine组件,初始化线程池,初始化mapperListener,初始化connector

/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException { super.initInternal();
//初始化engine
engine.init();
//初始化线程池
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
//初始化mapperListener
// Initialize mapper listener
mapperListener.init();
//初始化connector
connector.init(); }

初始化executor,mapperListener,connector后面再讲其作用,先接初始化engine

StandardEngine

StandardEngine的类图如下:

在StandardEngine的初始化中并没有直接调用host的初始化,而是调用的父类containerBase的initInternal的方法:

//StandardEngine
@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
//containerBase
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}

host的init是在start阶段去做的,所以后面再说

executor

executor.init();默认调用LifecycleMBeanBase的initInternal方法

mapperListener

mapperListener.init();也默认调用LifecycleMBeanBase的initInternal方法

connector

connector的初始化调用Connector类的initInternal方法,主要是new了一个CoyoteAdapter,初始化protocolHandler

@Override
protected void initInternal() throws LifecycleException { super.initInternal(); // 实例化 CoyoteAdapter 适配器
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
......
try {
//初始化 protocolHandler
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}

ProtocolHandler.init();的实现:

AbstractProtocol是调用endpoint的init方法,这个方法中调用bind()

@Override
public void init() throws Exception {
//初始化endpoint
endpoint.init();
}

bind()针对不同的io类型提供了三种的默认实现

进入NioEndpoint类的bind()

/**
* Initialize the endpoint.
*/
@Override
public void bind() throws Exception {
//使用nio方式监听端口
if (!getUseInheritedChannel()) {
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
......
//开启selectorPool
selectorPool.open();
}
start过程

tomcat的start阶段与init阶段相似,都是逐层调用,稍有不同的是在于engine,host,context,wrapper的启动方式;

首先回到Bootstrap的main方法中,继续执行Catalina类的start(),在start()方法中调getServer().start();

调用LifecycleBase类的start()方法,在这个方法中调动StandardServer类实现的startInternal(),在这个类中继续调用service的star()方法,以此类推逐层start调用,直到调用engine的start(),我们看下engine的start()方法,在看下StandardEngine的类图:

StandardEngine的startInternal()调用ContainerBase的startInternal()

/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException { ......
// Start our child containers, if any
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])));
}
......
}

findChildren()的方法找到的是engine容器的子容器然后在new StartChild(children[i])中调用子类容器的start();使用这种方式依次启动子容器

tomcat8 源码分析 | 组件及启动过程的更多相关文章

  1. Android源码——Activity组件的启动过程

    根Activity启动过程 Launcher启动MainActivity的过程主要分为6个步骤: 一.Launcher向ActivityManagerService发送一个启动MainActivity ...

  2. Netty源码分析之客户端启动过程

    一.先来看一下客户端示例代码. public class NettyClientTest { public void connect(int port, String host) throws Exc ...

  3. wxWidgets源码分析(1) - App启动过程

    目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wx ...

  4. Hive源码分析(1)——HiveServer2启动过程

    1.想了解HiveServer2的启动过程,则需要找到启动HiveServer2的入口,hive服务的启动命令为hive --service HiveServer2,通过分析$HIVE_HOME/bi ...

  5. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  6. JVM源码分析之JVM启动流程

      原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十四篇. 今天呢!灯塔君跟大家讲: JVM源码分析之JVM启动流程 前言: 执行Java类的main方法,程序就能运 ...

  7. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  8. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  9. Spring Ioc源码分析系列--Bean实例化过程(二)

    Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...

随机推荐

  1. (办公)SpringBoot与mybatisGenerator自动生成.

    20181206-自动生成,少写一点代码. (以下的内容主要参考csdn上的<[完美]SpringBoot+Mybatis-Generator自动生成>这篇文章,还有简书上的mbatis- ...

  2. 【学习笔记】【Design idea】一、Java异常的设计思想、性能相关、笔记

    1.前言: 异常.本该是多么优雅的东西,然而,得全靠自己在零散的信息中汇集. 学习笔记保持更新. 2.教材(参考资料) 其他 ①受检异常与非受检异常:https://www.cnblogs.com/j ...

  3. Docker创建JIRA 7.2.4中文破解版

    目录 目录 1.介绍 1.1.什么是JIRA? 2.JIRA的官网在哪里? 3.如何下载安装? 4.对JIRA进行配置 4.1.打开浏览器:http://localhost:20012 4.2.JIR ...

  4. MySQL 处理海量数据时的一些优化查询速度方法

    查询速度慢的原因 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O 吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 5.网络速度慢 6 ...

  5. Ehcache入门经典:第一篇

    ehcache主要是轻量级的缓存实现 ehcache.xml <?xml version="1.0" encoding="UTF-8"?> < ...

  6. 前后端分离djangorestframework—— 接入支付宝支付平台

    支付宝 简介 支付宝是什么不用多说了,本次教程适合初学者 前提准备 话不多说,干就完了 1.注册开发者账号,设置公钥私钥 首先进入支付宝开发者平台:传送门 ,有账号直接登录,没账号用你平时用来付款收钱 ...

  7. Win10 - MySQL 5.7 密码重置

    Win10 - MySQL 5.7 密码重置 所有行为均发生在系统管理员权限的 Cmd 或 Powershell 下 注意! 本行为会导致数据库重置 # 重新安装 mysql 服务 mysqld -- ...

  8. Vue学习之路1-集成环境安装

    1.前言 Vue 是一款友好的.多用途且高性能的javascript框架,与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用,它能够帮你创建可维护性和可测试性更强的代码库,Vue是渐进式的j ...

  9. .NET CORE学习笔记系列(2)——依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在上篇中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的 ...

  10. Python开发【第五篇】内置函数

    abs() 函数返回数字的绝对值 __author__ = "Tang" a = -30 all() 函数用于判断给定的可迭代参数iterable中的所有元素是否都为True,如果 ...