前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >框架源码私享笔记(01)Tomcat核心架构功能 | 配置详解

框架源码私享笔记(01)Tomcat核心架构功能 | 配置详解

原创
作者头像
拉丁解牛说技术
发布2025-03-03 16:56:10
发布2025-03-03 16:56:10
590
举报
文章被收录于专栏:框架源码私享笔记

读书笔记:对《活出意义来》这本书的序言特别有感触。其中:不要以成功为目标,你越是对它念念不忘,就越有可能错过它。因为成功如同幸福,不是追求就能得到;它必须因缘际会...它实际是一个人全心全意投入并把自己置之度外时,意外获得的副产品。

外界向你提供的目标,往往以某种奖励吸引着你追随。我们理应拒绝外在的奖励刺激-外驱力,需要自己建立’内奖‘-内驱力。即选定自己的目标,在追求目标的努力中,获得内心的秩序和成长的乐趣。


一、Tomcat概述

二、Tomcat核心功能

2.1 connector-网络连接器

2.1.1 三种IO模型

2.2 container-servlet 容器

2.2.1 Container的主要功能

2.2.2 Container的容器结构

三、Tomcat架构解析

四、tomcat server.xml配置说明


一、Tomcat概述

Tomcat是Apache基金会基于Java开发开源的web容器。Tomcat应用服务器,又称“汤姆猫”,它开源、轻量、易于集成、社区背书支持的特性,深受Java开发者偏爱。尤其是Tomcat专注于处理servlet和jsp的出色能力深得研发者信赖(当然目前jsp用的企业已经很少,瓶颈在于互联网高并发时代背景下,jsp的响应和吞吐量很难满足需求)。

web服务器除了Tomcat,还有我们熟悉的Nginx、Apache。Nginx主要用于静态资源、反向代理、负载均衡服务器。而Apache服务器主要用于静态页面和http请求,此外它有丰富的模块和插件,支持扩展实现负载均衡、虚拟主机等。

今天也是先从架构原理核心功能开头,有个大致了解,后面在对具体某个核心组件进行详细分享。

二、Tomcat核心功能

Tomcat核心功能有2个:网络连接器功能+servlet容器功能。

网络连接器功能,负责是接收客户端的http请求,并将http请求转给对应servlet进行处理,以及将servlet处理结果返回给客户端。具体是专注处理来自外部的socket连接请求,并将对应IO字节流转成对应Request和Response对象。可以说,网络连接器就是外交官,专注处理外部通讯事宜。

servlet容器功能,复杂加载和管理servlet,以及处理具体的Request请求。servlet容器就是真正的内务管家,处理内部具体业务逻辑事宜,也就是我们的接口逻辑。

这两个核心功能对应Tomcat两个核心源码组件,分别是连接器connector类,容器container接口。

2.1 connector-网络连接器

connector之所以可以负责接收和响应外部的请求,是因为里面有个协议处理器ProtocolHandler,专门负责处理socket通讯。Tomcat 8.0 支持的网络协议有http/1.1还有AJP/1.3 两个协议,而Tomcat8.5之后版本则新增支持下一代http/2.0协议。

2.1.1 三种IO模型

两种协议底层应用的网络IO模型,主要实现都是NIO,NIO2两个模型,此外还有一个APR模型。

三个IO模型核心特点和适用场景具体如下:

NIO ,全称Non-blocking I/O,是jdk1.4开始引入,并基于Java NIO类库的channel、buffer、selector去实现多路复用的NIO。具体是允许一个线程同时管理多个channel通道,当一个channel有读写IO事件,selector选择器再通知线程去处理,也就是允许每个线程管理多个IO操作,不像BIO是1V1那样干等,适合高并发连接数多的场景。

NIO2,全称Non-blocking I/O 2,此外也叫Asynchronous I/O,所以也称为AIO。在jdk7开始引入的异步非阻塞I/O,基于jdk 7的NIO2类库实现。具体是基于事件和回调机制,通过AsynchronousSocketChannel实现异步IO操作,解决数据复制阶段阻塞问题,允许应用程序直接返回不用阻塞。NIO2比NIO适合更高并发和复杂场景的应用,比如大数据处理、高性能网络服务器。

而APR模型(Apache Portable Runtime),是通过C/C++实现封装Unix的IO操作,可以跨平台使用,由于与操作系统底层直接交互,性能优于NIO和NIO2。适合性能要求极高的静态资源和大型web高并发请求场景。

这里附带说一下BIO,虽然BIO性能不佳,但是日常普通性能要求场景,我们还是经常使用和见到BIO。BIO是使用java.io里的FileInputStream和FileOutputStream进行读写操作,由于每次读写需要线程等待完成,导致效率低下性能不佳。

2.1.2 connector的核心组件

连接器里面有2个核心组件,一个是protocolHandler协议处理器,一个是Adapter适配器。源码filed视图:

ProtocolHandler接口,用来处理具体的tcp\http请求。

Adapter适配器,则将ProtocolHandler处理处理的http请求Request,转换成servlet容器能处理的servletRequest。

protocolHandler内部的核心功能,是通过Endpoint和Processor两个核心组件实现具体的协议处理:

其中Endpoint负责监听端口,实现了TCP/IP协议,专门处理socket数据流信息。

而Processor实现http协议,接收Endpoint发过来的socket字节流数据,通过http协议格式解析相关数据,转换成Tomcat的Request对象,最后通过Adapter提交到servlet容器处理。

对于协议处理器,Tomcat提供了以下7个实现类,包括我们常见的基于NIO、NIO2模型并实现http1.1协议、Ajp协议的几个处理器。

protocolHandler处理了网络连接并得到相关请求对象,为何不直接转给Container容器处理?

为什么需要Adapter适配器去中转?

由于每个协议格式数据不一样,Tomcat通过协议处理器,得到Tomcat的Request对象(org.apache.coyote.Request)。然而servlet容器里定义了自己的ServletRequest对象(HttpServletRequest),为了解耦两者通过Adapter适配器进行转换。

Adapter适配器的存在,就是为了桥接tomcat的Request对象与标准的ServletRequest对象之间的差异,确保Tomcat连接器能够顺利地与Servlet容器进行交互,同时保持系统的灵活性和扩展性。

2.2 Servlet Container容器

container容器核心功能,主要负责处理网络连接器connector发过来的ServletRequest请求。

2.2.1 Container的主要功能

它主要的功能有:

1、负责管理Servlet的生命周期,包括Servlet的初始化、服务处理、销毁等。

2、负责http请求的调度与执行。当connector连接器接收到客户端http请求后,会将请求转给对应Container容器,Container根据请求路径和配置信息,找到对应的Servlet并调用服务方法处理请求。

3、负责响应生成HttpServletResponse对象,并返回给connector,通过connector返回给客户端。

2.2.2 Container的容器结构

Servlet web容器里,主要有四大组:engine、host、context、wrapper。彼此是父子层级关系,具体包含层级关系如下图:

Engine:代表Servlet引擎,一个Service只能有一个engine引擎,用来管理多个虚拟站点host。

Host:代表一个虚拟主机、站点。一个tomcat可以配置多个虚拟主机地址,然后里面可以包含多个context。

Context:表示一个web服务程序。也就是我们最熟悉的一个个应用。

wrapper:表示一个Servlet,是Container容器中最底层,没有下一级子类。

如上图所见,自上而下,有点像二叉树分散开来,tomcat通过组合模式来管理这些父子关系容器。

这四种容器,都统一实现了Container接口。他们的几个在server.xml有相关配置标签配置参数。

三、Tomcat架构解析

刚才已经对核心功能的connector、Container进行了详细介绍,接下来从整体再看。整体外部组件就比较简单。

直接看tomcat的server.xml文件可见其组成,server.xml我们非常熟悉,看起来更加直观,一目了然。

代码语言:txt
复制
<Server port="8005" shutdown="SHUTDOWN"> 
  <Service name="Catalina"> 
    <Connector port="8080" protocol="HTTP/1.1"/> 
    <Engine name="Catalina" defaultHost="localhost"> 
      <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> 
        <Context path="/ladingjieniu" docBase="ladingjieniu.war"/>  
      </Host> 
    </Engine> 
  </Service> 
</Server> 

server:是tomcat的最顶级容器,一个tomcat里只有一个server。server表示tomcatServlet容器以及其他组件,负责组装并启动关闭connector连接器、Servlet容器引擎。

service:一个server有多个service。service的出现,关键在于解决了网络协议解析和Servlet容器解耦问题,具体是整合多个connector连接器和Container(里面只有一个engine Servlet引擎)绑定到一起,便于两个核心功能的维护和扩展。

connector、Container就和上文说的一致。一个主要负责处理外部的tcp、http请求。另一个是具体负责Servlet请求逻辑。

彼此之间的父子层级和关联数量关系如下,一个server有多个service,一个service只有一个engine引擎,一个engine可以配置多个host虚拟站点,而一个host可以有多个context web服务,一个context web服务里有多个Servlet wrapper。

四、tomcat server.xml 配置说明

代码语言:txt
复制
<?xml version="1.0" encoding="UTF-8"?>
<!--
Server是tomcat的最顶级容器,是server.xml的最外层标签,代表整个tomcat容器
参数port,是tomcat用来监听shutdown命令的TCP/IP端口号,默认为
8005。设置为-1,表示禁止通过端口关闭Tomcat,此时shutdown.bat/sh命令不能使用。
如果在一台服务器启动多个tomcat服务实例,该端口需保持差异。
shutdown,用来指定shutdown 命令的别名,默认值SHUTDOWN
-->
<Server port="8005" shutdown="SHUTDOWN">
    <!--监听器组件,不同组件功能不一样-->
    <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
    
    <!--配置全局JNDI资源-->
    <GlobalNamingResources>
        <!-- 用户数据库Realm,配合tomcat-users.xml,管理tomcat host manager的用户身份验证 -->
        <Resource name="ladingjieniu" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml"/>
    </GlobalNamingResources>

    <!--
     一个server里有多个service,里面有一个engine和多个connector。相当于一个服务
     可以配置多个connector去监听外部请求,并在engine Servlet处理。
    -->
    <Service name="Catalina">
        <!-- 供Service内各个Connector组件共享使用的线程池执行器,我们可以定义多个命名线程池 -->
        <!--
        配置连接器使用连接池参数:
        maxThreads:最大并发数
        minSpareThreads:线程池的核心线程数
        maxIdleTime:Executor中空闲线程超时关闭的毫秒数,默认1分钟
        maxConnections 与tomcat建立的最大socket连接数,默认是10000
        acceptCount;请求的队列最大长度,默认是100
        -->
        <Executor name="tomcatThreadPool" namePrefix="ladingjieniushuojishu-exec-"
            maxThreads="100" minSpareThreads="50"/>

        <!-- 连接器组件,负责接收和响应连接,然后创建Request和Response对象转发给engine
             默认连接器,监听端口8080,并支持HTTP/1.1协议,支持指定命名共享线程池的连接器。
             port:连接器监听的请求端口,必须唯一
             protocol:连接器监听的请求协议类型,默认是http/1.1
             redirectPort:如果当前连接器不支持SSL(HTTPS)请求,但是收到SSL(HTTPS)请求时,Catalina将重定向至指定端口的Connector。
             URIEncoding:用于解码URL的字符编码,则默认使用 UTF-8。
             connectionTimeout:此连接器在建立连接之后等待请求URI的超时时间(毫秒),默认值为60000(即 60秒)。
             当client与tomcat建立连接之后,在"connectionTimeout"时间之内,仍然没有得到client的请求数据,此时连接将会被断开。
             maxConnections:tomcat在任何给定时间能同时接收和处理的最大连接数,默认1w。
             acceptCount:当同时连接数超过maxConnections时,系统会继续接收连接,并放入Accept等待队列的长度,默认是100.
             同时连接数超过maxConnections,且等待队列也放满,后续收到的请求都将被直接拒绝。
             
        -->
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000" executor="tomcatThreadPool"
                   redirectPort="8443"/>

        <!--在端口8009上定义使用AJP/1.3协议访问的连接器-->
        <!--
        <Connector protocol="AJP/1.3"
                   address="::1"
                   port="8009"
                   redirectPort="8443" />
        -->

        <!--
        请求处理组件,一个Service标签只能有一个Engine标签
        Engine组件从一个或多个Connector中接收请求并处理,并将完成的响应返回给Connector,最终响应给客户端。
        -->
        <Engine name="Catalina" defaultHost="localhost">
            <!--
            一个虚拟主机,可以指定应用程序的基本目录,包含了一个或多个Web应用
            默认的虚拟主机名为"localhost",
            如果配置多个Host,可以让一个tomcat支持从不同的域名访问Web应用。
            autoDeploy : Tomcat 在 运行时应定期检查新的或更新的 Web 应用程序。
            appBase :虚拟主机的应用程序基础目录。
            unpackWARs:是否将Web应用的WAR文件解压。如果为true,则先解压然后运行解压的Web应用,如果为false,直接使用WAR文件运行Web应用。
            -->
            <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
                <!--通过日志记录在该Host中处理的相关请求-->
                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                       prefix="localhost_access_log" suffix=".txt"
                       pattern="%h %l %u %t &quot;%r&quot; %s %b"/>
            </Host>
            <!--
            支持配置多个web服务,域名映射, 需要修改本地hosts文件 。
            -->
            <Host name="www.ladingjieniu.com" appBase="lading" unpackWARs="true" autoDeploy="true">
            <!-- 
            Context的path,表示该 Web 应用程序的虚拟上下文路径。
            docBase,表示web应用程序的物理根目录,若该路径是相对路径的话,则是相对于appBase而言,若是绝对路径,则与appBase无关。
            reloadable是否支持重新加载web应用程序类,默认为false。
            如果设为true,Tomcat服务器在运行时会监视在WEB-INF/classes和Web-INF/lib目录下的class文件变化,服务器自重新加载该Web应用。

            -->
                  <Context docBase="/app/ladingjieniu/myweb-1.0-SNAPSHOT" path="/myweb" reloadable="true" />
            </Host>
        </Engine>
    </Service>
</Server>

推荐阅读拉丁解牛相关专题系列(欢迎交流讨论公众号搜:拉丁解牛说技术)

1、JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC

2、JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?

3、JVM进阶调优系列(3)堆内存的对象什么时候被回收?

4、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?

5、JVM进阶调优系列(1)类加载器原理一文讲透

6、JAVA并发编程系列(13)Future、FutureTask异步小王子

7、MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

8、MySQL进阶突击系列(09)数据磁盘存储模型 | 一行数据怎么存?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Tomcat概述
  • 二、Tomcat核心功能
    • 2.1 connector-网络连接器
      • 2.1.1 三种IO模型
      • 2.1.2 connector的核心组件
    • 2.2 Servlet Container容器
      • 2.2.1 Container的主要功能
      • 2.2.2 Container的容器结构
  • 三、Tomcat架构解析
  • 四、tomcat server.xml 配置说明
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档