Tomcat8.5

322 阅读9分钟

第一部分:Tomcat系统架构与原理剖析

目录结构

目录说明
bin/用于存放可以执行脚本
bin/startup.batwindow用于启动tomcat的脚本
bin/startup.shlinux平台用于启动tomcat的脚本
bin/shutdown/batwindows平台用于停止服务的脚本文件
bin/shutdown.shlinux平台用于停止服务的脚本文件
conf/用于存放tomcat配置文件的目录
conf/logging.properties用于配置tomcat日志规则的配置文件
conf/server.xml用于配置tomcat应用的配置文件
conf/users.xml用于配置tomcat的用户及权限
conf/web.xml全局web.xml定义默认配置,在tomcat中web.xml是一个继承关系,我们自定义web.xml文件继承默认web.xml我们可以通过使用覆盖的方式替换默认配置。
lib/用于存放tomcat所依赖第三方jar文件
logs/用于存放tomcat在运行中产生日志文件
webapps/默认发布应用目录
webapps/docstomcat说明文档
work/用于存放jsp编译为Servlet过程临时文件,也用于缓存的目录。

浏览器访问服务器的流程

绘制一个从用户请求到响应时序图

用户通过浏览器访问服务的一次请求中会经过很多处理过程 第一步:浏览器会与服务器建立TCP连接 第二步:把用户请求的信息封装成http协议所能识别的数据结果 第三步:通过连接发送分装好的http数据到web服务器(Tomcat容器) 第四步:服务器接收到浏览器发送的数据(基于socket连接发送数据) 第五步:服务器需要把接收的数据进行解包,并把用户发送的数据 第六步:服务器需要把解包数据封装为servlet能够使用对象HttpServietRequest和HttpServletResponse对象 第七步:通过请求的url匹配可以处理请求Servlet对象,根据请求方式调用对应处理方法并把封装好的HttpServletRequest和HttpServletResponse对象对应方法 第八步:Servlet执行业务请求,并响应对应信息 第九步:Web容器拿到用户的响应信息分装为HTTP协议识别信息 第十步:通过TPC连接把响应的数据包发送给浏览器 第十一步:浏览器接收Web容器响应数据把数据渲染给用户,完成。

Tomcat系统总体架构

HTTP服务器接收到请求之后把请求交给Servlet容器处理,Servlet容器通过Servlet接口调用业务类。 java定义的Servlet接口和Servlet容器这一套标准叫做Servlet规范 注意: Tomcat是一个实现Servlet规范的要求去实现了Servlet容器,同时它也具有HTTP服务器功能。

Servlet容器的处理流程

acf5f9dcec5998f5c964a40a88aa7552.png

Tomcat的总体架构

Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来完成Tomcat的两大核心功能。

连接器 连接器主要复杂对外交流,处理Socket连接,负责网络字节流与Request和Responsede对象的转换。

容器 容器复杂内部处理:加载和管理Servlet以及具体处理Request请求。

Tomcat连接器是怎么设计的?

连接器对 Servlet 容器屏蔽了协议及 I/O 模型等的区别,无论是 HTTP 还是 AJP,在容器中获取到的都是一个标准的 ServletRequest 对象。

Coyote简介

Coyote是Tomcat中连接器组件的名称,是对外的接口。客户端通过Coyote与服务器建立连接、发送请求并接受响应。

(1)Coyote封装了底层网络通信(Socket请求及响应) (2)Coyote使Catalina容器(容器组件)与具体的请求协议及IO操作方式完全解耦。 (3)Coyote将Socket输入转换封装为Request对象,进一步封装后交由Catalina容器处理,处理请求完成后,Catalina在通过Coyote提供Response对象将结果写入输出流。 (4)Coyote复杂具体协议(应用层)和IO(传输层)相关内如。

41eb7e35df0e6cac5b54568c535bff5c.png

应用层

传输层

连接器设计

tomcat在设计者在设计连接器时考虑tomcat需要支持不同的网络协议和IO模型如HTTP1.1HTTP2ARP所以设计了EndpointProcessor连个接口,tomcat的设计者在设计连接器时并没有使用ServletHttpServletRequestHttpServletResponse而是设计了一个自己Request和Response对象,为了与Servlet容器交互设计了一个Adapter接口,这个接口主要用于把tomcat的Request和Response对象转化为servlet能使用HttpServletRequestHttpServletResponse对象。

组件描述
Endpoint主要用于处理网络通信和IO模型,用于监听端口当收到请求时把请求信息交给具体处理指定协议Processor对象。
Processor定义了用于处理指定协议处理器,处理器根据给定socket来解析对应的数据并把数据封装成Request和Response对象在分装为Adapter对象
AdapterAdapter用于解决Tomcat自己Request和Response对象与HttpServletRequestHttpServletReponse对象

tomcat为了解耦连接器与Servlet的并未使用Servlet的Request和Response对象存储用户请求的信息,所以当Connector调用Servlet容器时不能

组件描述
Endpoint用于监听端口和处理Socket,当有是请求来的时候把请求交给Processor处理具体请求,当有具体响应的时候处理具体的响应。不同Endpoint使用IO模型。
Processor用于解析和封装指定协议如HTTP1.1HTTP2.0, 当接收请求时候需要根据给定义Socket来解析请求信息并把请求信息封装到一个Request对象中,当收到业务类的响应时需要把响应信息转换为HTTP并输出给用户。
Adapter为了能够让Connector可以调用容器service方法,Adapter实现CoyoteRequestHttpServletRequest对象间的转换操作

32b488644bab736669ed14e59e0c0609.png

ee880033c5ae38125fa91fb3c4f8cad6.jpeg

嵌入式启动Tomcat github.com/heroku/devc…

基于源码启动Tomcat

blog.csdn.net/qq_27680317…

使用Idea运行Tomcat源码

准备:

  1. 需要安装java8+以上的jdk
  2. 需要安装ant中间件tomcat是使用ant管理项目

第一步:首先要下载源码实例使用的是8.5.56版本源码 下载地址:tomcat.apache.org/download-80…

第二步:解压源码并执行ant ide-intellij命令

ant ide-intellij

第三步:新建pom.web文件

<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Licensed to the Apache Software Foundation (ASF) under one or more
  ~ contributor license agreements.  See the NOTICE file distributed with
  ~ this work for additional information regarding copyright ownership.
  ~ The ASF licenses this file to You under the Apache License, Version 2.0
  ~ (the "License"); you may not use this file except in compliance with
  ~ the License.  You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>Tomcat8.5</artifactId>
  <name>Tomcat8.5</name>
  <version>8.5.23</version>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.easymock</groupId>
      <artifactId>easymock</artifactId>
      <version>3.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.ant</groupId>
      <artifactId>ant</artifactId>
      <version>1.10.1</version>
    </dependency>
    <dependency>
      <groupId>wsdl4j</groupId>
      <artifactId>wsdl4j</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>javax.xml</groupId>
      <artifactId>jaxrpc</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jdt</groupId>
      <artifactId>org.eclipse.jdt.core</artifactId>
      <version>3.13.0</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>Tomcat8.5</finalName>
    <sourceDirectory>java</sourceDirectory>
    <testSourceDirectory>test</testSourceDirectory>
    <resources>
      <resource>
        <directory>java</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>test</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3</version>
        <configuration>
          <encoding>UTF-8</encoding>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

第四步:直接把源码导入idea中

第五步:注释编译失败的测试类中报错的业务逻辑

  1. TestApplicationContextFacadeSecurityManager
-Dcatalina.home=/Users/zhouyushuai/Desktop/apache-tomcat-8.5.63-src
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=conf/logging.properties
-Duser.language=en

配置VM option参数的截图

-Dcatalina.home=/Users/zhouyushuai/Desktop/apache-tomcat-8.5.63-src

Bootstrap在启动Tomcat时首先会先初始化一个名字叫catalina.home的vm环境变量,tomcat在运行的时候会根据配置目录来查找confbin等Tomcat工作所需要的目录,如果不指定Bootstrap会获取源码目录作为工作目录。

static {
        // Will always be non-null
        String userDir = System.getProperty("user.dir");

        // Home first
        String home = System.getProperty(Constants.CATALINA_HOME_PROP);
        File homeFile = null;

        if (home != null) {
            File f = new File(home);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

      //省略部分代码
       
        System.setProperty(
                Constants.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
    }

-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=conf/logging.properties

用于配置tomcat在启动的时候的日志管理及配置

-Duser.language

用于解决tomcat在启动的时候输入日志乱码的问题

运行Tomcat的配置信息

第六步:访问http://localhost:8080 在访问tomcat时报错,错误信息是不支持jsp。 3be764eb35ae2b2f21253cc68deb165a.png

手动初始化JasperInitializer并设置到context中用于处理jsp

protected synchronized void configureStart() {
    // Called from StandardContext.start()

    if (log.isDebugEnabled()) {
        log.debug(sm.getString("contextConfig.start"));
    }

    if (log.isDebugEnabled()) {
        log.debug(sm.getString("contextConfig.xmlSettings",
                context.getName(),
                Boolean.valueOf(context.getXmlValidation()),
                Boolean.valueOf(context.getXmlNamespaceAware())));
    }

    webConfig();
    //在原有业务逻辑中增加初始化jsp处理器
    context.addServletContainerInitializer(new JasperInitializer(), null);
  
   //省略其他业务代码
  
}

再次访问tomcat就可以正常访问了

现在就可以调试tomcat源码了。

三、Tomcat类加载器

jdk已经实现了完整类加载器了,tomcat为什么还要定义自己的类加载器?

系统类加载器描述
BootstrapClassLoader

###3.1 双亲委派机制

什么是双亲委派机制?

保证核心class文件不会被篡改,当我们尝试运行一个java应用的时候首先会触发SystemClassloader的运行,SystemClassLoader运行时会把类加载的工作一次委托给父加载器加载。

在tomcat中各类加载器都负责加载那些内容如SystemClassLoader负责加载bin/目录下的bootstrap.jar文件中的class文件。

Tomcat中的WebAppClassLoader加载器打破了双亲委派机制, WebAppClassLoader默认先加载/WEB-INF/classes在加载/WEB-INF/lib然后才调用父级载器。

四、Tomcat配置HTTPS

4.1 基于keystore证书

keytool -genkey -alias tomcat -keyalg RSA -keysize -keystore tomcat.keystore

注意:使用keystore文件类型证书需要使用certificateKeystoreFilecertificateKeystorePassword两个属性来配置证书位置证书的密码

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
       maxThreads="150" SSLEnabled="true" secure="true" schema="https">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="conf/tomcat.keystore"
                     certificateKeystorePassword="lagou123" keystoreType="RSA"/>
    </SSLHostConfig>
</Connector>

4.2 基于jks证书

Tomcat优化

一、JVM调优配置

tomcat优化方向是从tomcat和jvm两个方向优化。

查看jvm使用那个收集器

切换JVM垃圾收集器参数

参数描述
-XX:+UseSerialGC
-XX:+UseParallelGC
-XX:+UseParNewGC
-XX:ParallelGCThreads
-XX:+UseConcMarkSweepGC(CMS)
-XX:+UserG1GC
java -jar 
-XX:+UseG1GC
-Xms128m
-Xmx128m 

二、Tomcat配置调优

2.1 线程池配置

配置

 <Executor name="tomcatThreadPool" 
           namePrefix="catalina-exec-"
           maxThreads="150" 
           minSpareThreads="4"/>

线程池参数说明

参数说明
maxConnections最大连接数,当达到峰值后服务器接收到的新请求不会在被处理。额外的请求将会阻塞直到连接数低于macConnections。可以通过ulimit -a命令查看服务器限制。对于CPU要求更高(计算密集型)的应用,建议不要配置过大;CPU要求不是特别高时,建议配置在2000左右(受服务器性能影响)。需要服务器硬件支持。
maxThreads最大线程数,需要根据服务器硬件情况,进行合理配置
acceptCount最大排队等待数,当服务器接收的请求达到最大连接数(maxConnectors)时,Tomcat会把新的请求放到任务队列中进行排队。acceptCount指的就是任务队列中排队等待的请数。

禁用AJP连接器

调整IO模式

当Tomcat并发性能要求比较高或并发遇到瓶颈时可以考虑使用APR模式,APR(apache portable runtime)是从操作系统级别解决异步IO问题,使用时需要在操作系统上安装APR和navtive库(ARP的原理是使用JNI技术调用系统底层IO接口)

动静分离

可以使用nginx+tomcat相结合的部署方案,nginx负责静态资源、tomcat负责动态资源(tomcat并不擅长处理静态资源,处理静态资源时也会消耗tomcat的资源)。

参考资料 segmentfault.com/a/119000000…