• 【tomcat】tomcat系统架构以及核心启动流程


    对于web后端开发工程师来说,tomcat作为一个应用服务器框架本质上就是一个HTTP服务+Servlet容器。研究过spring、spring mvc源码的同学应该了解,spring mvc其实就是基于Servlet规范实现的请求的转发路由、转发处理。而Spring和SpringMVC就是通过web.xml文件中的事件监听器ContextListener加载Spring容器,然后在加载SpringMVC子容器。

    所以带着几个问题,去学习tomcat的原理,本篇先聊聊基础架构,以及核心的启动流程。

    • 基本架构如何,以及各个组件如何功能
    • 启动的核心流程
    • 处理请求的核心流程
    • tomcat的是如何打破双亲委派模型,进行的类加载
    • tomcat的线程模型
    • tomcat的性能调优

    在这里插入图片描述

    tomcat系统架构

    在这里插入图片描述
    对于tomcat来说,比较重要的就是server.xml文件

    1 <Server> //顶层组件,可以包括多个Service
    2 <Service> //顶层组件,可包含一个Engine,多个连接器
    3 <Connector/> //连接器组件,代表通信接口
    4 <Engine> //容器组件,一个Engine组件处理Service中的所有请求,包含多个Host
    5 <Host> //容器组件,处理特定的Host下客户请求,可包含多个Context
    6 <Context/> //容器组件,为特定的Web应用处理所有的客户请求
    7 Host>
    8 Engine>
    9 Service>
    10 Server>
    

    在这里插入图片描述
    Connector 连接器:对外交流
    Container 容器:内部处理
    Server:负责和管理启动多个service,加痛8005端口的shutdown命令。关闭整个容器

    启动流程

    启动类就是BootStrap的main方法。

    init

    在这里插入图片描述
    初始化反射生成一个Catalina对象。这里有类加载器的操作,先不介绍,后面在进行单独讲解。

    daemon.load(args);
    

    在这里插入图片描述
    反射调用catalina.load()方法

     digester.parse(inputSource); // 解析server.xml文件 构建对象
    
    getServer().init(); // 初始化 可以看到这里是StandardServer
    

    在这里插入图片描述

    public final class StandardServer extends LifecycleMBeanBase implements Server 
    // 因为StandardServer继承了LifeCycleMBeanBase 先调用父类的init()
    
    
    public final synchronized void init() throws LifecycleException {
        // 不是new 不能调用init
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
    
        try {
            // 初始化之前 状态变更为init
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            // 模版方法 拓展使用
            initInternal();
            // 初始化完成
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            // 初始化异常 修改成 公共逻辑
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }
    
    
    for (Service service : services) {
        // Service组件初始化
        service.init();
    }
    
    


    这里只有一个对象,所以对StandardService进行初始化,在初始化Service的时候,发现需要先初始化engine

    在这里插入图片描述
    在这里插入图片描述

    if (engine != null) {
         // 1.Engine组件, 即servlet容器 初始化
         // 创建了一个线程池用于后续start流程中的 Host 的启动
         engine.init();
     }
    
    
    // Initialize our defined Connectors
    synchronized (connectorsLock) { // 加锁操作
        for (Connector connector : connectors) {
    
            // 2. Connector组件 初始化
            // endpoint 绑定端口
            connector.init();
        }
    }
    
    
     // TODO 重点关注 Endpoint 的端口绑定与 NIO的监听
    protocolHandler.init();
    
    
     // Endpoint 组件的初始化
    endpoint.init();
    
    // 三种实现, 默认 NioEndpoint
     // BioEndpoint
     // NioEndpoint
     // Nio2Endpoint
     bind();
    
    
    // NIO之 获取通道 SocketChannel
    serverSock = ServerSocketChannel.open();
    
    socketProperties.setProperties(serverSock.socket());
    InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
    
    // NIO之 绑定端口( IP:PORT )
    serverSock.socket().bind(addr,getAcceptCount());
    

    在这里插入图片描述

    总结下,其实初始化的过程就是把一些基础的东西进行加载。主要是绑定8090端口。

    start

    在这里插入图片描述
    这里执行start方法,会去反射调用catalina.start()方法

     // TODO 3.真正启动
     daemon.start();
     
      Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
     method.invoke(catalinaDaemon, (Object [])null);
    
    // 执行启动
    getServer().start();
    
    // 循环遍历service
    for (Service service : services) {
        // 启动所有的 service
        service.start();
    }
    
    
    synchronized (engine) {
          // 启动 Engine 子容器
          engine.start();
    }
    
    // 可以看到这里通过线程池进行异步处理
    for (Container child : children) {
        // 这就是 Engine init 过程中构建好的线程池
        // 这个线程池是在实例化 Engine 时给 Host 用的
        // 处理逻辑在 StandardHost 中的 startInternal
        results.add(startStopExecutor.submit(new StartChild(child)));
    }
    
    open();
    
     // 启动 Connector 组件
    connector.start();
    protocolHandler.start();
    endpoint.start();
    
    // 创建线程池
    public void createExecutor() {
           internalExecutor = true;
           TaskQueue taskqueue = new TaskQueue();
           TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
           executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
           taskqueue.setParent( (ThreadPoolExecutor) executor);
       }
    
    
    // 等待
    public void await() {
        getServer().await();
    }
    

    在这里插入图片描述

    小结

    其实就是三个流程,初始化、解析文件、启动。
    而最终的处理请求由acceptor\poller\worker 来处理。具体的流程在流程图中。
    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    使用Python探索四大名著【红楼梦】人物之间的关系,简直帅呆了
    CentOS Linux下CMake二进制文件安装并使用Visual Studio调试
    go分布式锁的一个简单实现
    CMake教程系列-04-编译相关函数
    flutter 随机数的生成 四舍五入
    三个方面浅析数据对大语言模型的影响
    golang设计模式——行为模式
    猿创征文|JDBC操作数据库
    Celery结合flask完成异步任务与定时任务
    i.MX rt 系列微控制器的学习记录
  • 原文地址:https://blog.csdn.net/jia970426/article/details/139883898