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(); 使用这种方式依次启动子容器
来源: https://www.cnblogs.com/cmyxn/p/10311349.html