Tomcat源码解读

0.Tomcat源码构建

下载导入源码

下载完毕源码后导入idea直接运行BootStrap程序。

BootStrap程序启动参数

VM虚拟机处理参数:

-Dcatalina.home=D:\JavaProject\apache-tomcat-8.5.78-src\source
-Dcatalina.base=D:\JavaProject\apache-tomcat-8.5.78-src\source
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=D:\JavaProject\apache-tomcat-8.5.78-src\source\conf\logging.properties

添加对Jasper的解析程序

class ContextConfig{
    configureStart(){
        context.addServletContainerInitializer(new JasperInitializer(), null);
    }
}

项目部署到Tomcat服务器的三种方式:

  • 打war包放入到webapps目录下
  • 直接编译好的应用程序放在,webapps目录下
  • 在server.xml中添加Context标签,通过docBase指定项目路径进行部署

1.Tomcat整体架构和处理请求流程解析

参考链接:Tomcat 整体架构分析 - 程序员自由之路

image.png

Pipeline
一个管道里面有多个阀门

List

Engine
用于管理Host

List hosts;
Pipeline pipe;

List

Host
虚拟主机:通过域名进行区分

List contexts
Pipeline pipe;

List

Context

List servlets;
Pipeline pipe;

List;

Wapper --- servlet类 --- servlet
一个Wapper对应一个Servlet类,一个Servlet类对应多个Servlet实例

List servlets;
Pipeline pipeline --- valve..servlet.service(reqeustFacade, responseFacade);

自定义阀门测试

public class TestValue extends RequestFilterValve{
    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException{
        System.out.println("test valve");
        getNext().invoke(request, response);
    }
    @Override
    protected Log getLog(){ return null; }
}

配置:在server.xml中Host下添加一个Valve标签,访问该虚拟主机的所有请求都会被Valve配置的类处理。

Filter

class FilterDemo extend Filter{
    @Override
    public void doFilter(servletRequest, servletResponse, filterChain){
        //...
        filterChain.doFilter();
        //...
    }
}
Tomcat请求流程图.png

TCP

由操作系统(Windows, Linux, MacOS)实现TCP协议
优点:可靠,安全,数据不丢失
同一台主机或不同主机不同应用程序之间产生的数据,可能需要传输

TCP数据交换模式:

  • BIO(Tomcat7默认使用)
  • NIO(Http11NioProtocol这个类处理NIO情况)
  • APR----Aapche, 10(Apache组织实现的一个类似于NIO的模型)

TCP连接三次握手程序:
操作系统(Linux)中的源码文件中tcp_output.c文件

  • A---SYN--->B
  • A<---SYN_ACK---B
  • A---ACK--->B

请求--->Request
Tomcat:8080
数据--->Request

服务器A--->服务器B
数据+IP

应用程序
1.生成程序
2.建立TCP连接---->Java---->TCP
3.发送数据

TCP协议----传输----操作系统----tcp_connect()

Http协议---->应用层协议---->数据的格式(使用什么方式传输数据不关心)

Http
socket----Http协议----解析---->Request

浏览器发送一个Post请求,到客户段接收到请求的流程:

  1. 按照http协议将Post请求生成HTTP请求协议格式的文本,HTTP协议文本格式参考:HTTP协议格式详解
  2. 浏览器作为应用程序没有发送数据的能力,数据发送由操作系统实现TCP协议来完成。
  3. 操作系统实现TCP协议于服务器建立连接,通过三次握手。
  4. 通过Socket接口将数据发送过去。
  5. 服务器通过Socket接口接收客户端发送的数据,判断数据格式,根据协议(https)将数据解析成Request对象,方便对数据内容进行操作。

Tomcat整体架构和处理请求流程解析:

1.接收到socket
2.将socket交给线程池
3.一个线程处理一个socket连接
4.开始从socket中获取数据
5.解析请求行
6.解析请求头
7.将解析过的数据放入到request中
8.根据请求头解析Connection对应的值是keepalive还是close
9.将Request对象交给容器进行处理
10.容器最终会交给对应的servelt进行处理
11.servlet中可以获取请求各种信息,包括获取请求体
12.servlet中也可以使用response对象向客户端返回响应---复杂
13.servlet中的代码都执行完成后,相当于容器中已经处理完了请求,相当于请求的核心逻辑已经执行完了
14.处理InputBuffer中的pos和lastValid,以便能够处理下一个请求

Request
method
url
header

应用层:
RequestFacade ----> connector.Request---->

底层:框架内部使用
coyote.Request
method
uri

2.Tomcat中关于长连接的底层原理与源码实现

3.Tomcat中关于请求处理的原理与源码实现

4.Tomcat中分块传输与响应请求的原理与源码实现

5.Tomcat中使用nio模型处理请求原理与源码实现

6.Tomcat中自定义加载器的使用与源码实现

6.1 类加载器

Java中的类遵循按需加载

所谓类加载器,就是用于加载Java类到Java虚拟机中的组件,它负责读取Java字节码,并转换成java.lang.Class类的一个实例,使字节码.class文件得以运行。一般类加载器负责根据一个指定的类找到对应的字节码,然后根据这些字节码定义一个Java类。另外,它还可以加载资源,包括图像文件和配置文件。

类加载器在实际使用中给我们带来的好处是,它可以使Java类动态地加载到JVM中并运行,即可在程序运行时再加载类,提供了很灵活的动态加载方式。

  • 启动类加载器(Bootstap ClassLoader): 加载对象是Java核心库,把一些核心的Java类加载进JVM中,这个加载器使用原生代码(C/C++)实现,并不是继承java.lang.ClassLoader,它是所有其他类加载器的最终父加载器,负责加载/jre/lib目录下JVM指定的类库,其实它属于JVM整体的一部分,JVM一启动就将这些指定的类加载到内存中,避免以后过多的I/O操作,提高系统的运行效率,启动类加载器无法被Java程序直接使用。
  • 扩展类加载器(Extension ClassLoader): 加载的对象为Java的扩展库,即加载/jre/lib/ext目录里面的类。这个类又启动类加载器加载,但因为启动类加载器并非用Java实现,已经脱离了Java体系,所以如果尝试调用扩展类加载器的getParent()方法获取父类加载器会得到null。然而,它的父类加载器是启动类加载器。
  • 应用程序类加载器(Application ClassLoader): 亦叫系统类加载器(System ClassLoader),它负责加载用户类路径(CLASSPATH)指定的类库,如果程序没有自己定义类加载器,就默认使用应用程序类加载器,它也由启动类加载器加载,但它的父加载类被设置成了扩展类加载器。如果要使用这个加载器,可通过ClassLoader.getSystemClassLoader()获取。

a.类加载器处于的位置

image.png

b.双亲委派

双拼委派模型会在类加载器加载类时首先委托给父类加载器,除非父类加载器不能加载才自己加载

c.应用程序启动参数加载

"C:\Program Files\Java\jdk-11.0.6\bin\java.exe"
"-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar=51177:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.4\bin"
-Dfile.encoding=UTF-8
-classpath
D:\JavaProject\apache-tomcat-8.5.78-src\target\classes;
C:\Users\water.m2\repository\org\easymock\easymock\3.4\easymock-3.4.jar;
C:\Users\water.m2\repository\org\objenesis\objenesis\2.2\objenesis-2.2.jar;
C:\Users\water.m2\repository\org\apache\ant\ant\1.7.0\ant-1.7.0.jar;
C:\Users\water.m2\repository\org\apache\ant\ant-launcher\1.7.0\ant-launcher-1.7.0.jar;
C:\Users\water.m2\repository\wsdl4j\wsdl4j\1.6.2\wsdl4j-1.6.2.jar;
C:\Users\water.m2\repository\javax\xml\jaxrpc-api\1.1\jaxrpc-api-1.1.jar;
C:\Users\water.m2\repository\org\eclipse\jdt\core\compiler\ecj\4.5.1\ecj-4.5.1.jar;
C:\Users\water.m2\repository\javax\xml\soap\javax.xml.soap-api\1.4.0\javax.xml.soap-api-1.4.0.jar
com.luban.initialize.demo.Test

d.ClassLoader源码

package java.lang;

public abstract class ClassLoader {

    private final ClassLoader parent;

    // class loader name
    private final String name;

    // Maps class name to the corresponding lock object when the current
    // class loader is parallel capable.
    // Note: VM also uses this field to decide if the current class loader
    // is parallel capable and the appropriate lock object for class loading.
    private final ConcurrentHashMap parallelLockMap;

    public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            //name对应的类没有被加载过
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //通过递归完成双亲委派模型
                    if (parent != null) {
                        //存在父加载器,就优先使用父类加载器加载
                        c = parent.loadClass(name, false);
                    } else {
                        //ExtClassLoader的父类为BootstrapClassLoader
                        //但是ExtClassLoader属性parent中的值为null
                        //ExtClassLoader无法得到BootstrapClassLoader
                        //如果父类加载器为null,使用BootstrapClassLoader进行加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //找不到,直接抛异常
                    c = findClass(name);    

                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

    //给个className对应一个锁,保证不会被重复加载
    protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) {
            Object newLock = new Object();
            //如果parallelLockMap中存在className返回对应的value,不存在添加进去
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }

    protected final Class findLoadedClass(String name) {
        if (!checkName(name))       //判断名字是否正常
            return null;
        return findLoadedClass0(name);
    }

    private final native Class findLoadedClass0(String name);

    protected Class findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
}

e.自定义类加载器

package com.luban.initialize.demo;

import java.io.*;

public class MyClassLoader extends ClassLoader{

    private String name;

    public MyClassLoader(ClassLoader parent, String name){
        super(parent);
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        byte[] data1 = getBytes("C:\\Users\\water\\Desktop\\Person.class");
        Class clazz = this.defineClass(name, data1, 0, data1.length);
        return clazz;
    }

    @Override
    public Class loadClass(String name) throws ClassNotFoundException {


        /**
         * 重写loadClass方法会打破双亲委派模型。
         */

//        return super.loadClass(name);

        ClassLoader system = getSystemClassLoader();

        Class clazz = null;
        try{
            clazz = system.loadClass(name);
        }catch (Exception e){

        }

        if(clazz != null){
            return clazz;
        }

        clazz = findClass(name);
        /**
         * 每个类默认继承java.lang.Object,所以会先加载java.lang.Object类,
         * 在重写的findClass中并不会加载Object类,
         * 会抛出异常java.lang.SecurityException: Prohibited package name: java.lang
         * 不允许自定义的类加载器,去加载java.xxx下的类
         *
         * 先用系统类加载器加载目标类,加载不到,再使用自定义类加载器进行加载
         */
        return clazz;
    }

    public static void main(String[] args) {
        MyClassLoader loader1 = new MyClassLoader(MyClassLoader.class.getClassLoader(), "MyClassLoader1");
        MyClassLoader loader2 = new MyClassLoader(MyClassLoader.class.getClassLoader(), "MyClassLoader2");

        try{
            //BootstrapClassLoader ---> ExtClassLoader ---> AppClassLoader ---> MyClassLoader
            /**
             * 如果AppClassLoader可以从ClassPath中加载到Person,就直接加载。
             * 如果MyClassLoader不能够加载到Person,会调用ClassLoader的findClass(),会后去获取Person
             * MyClassLoader中重写了findClass()中的方法,最终会调用MyClassLoader.findClass(),使用自定义逻辑加载指定Person。
             */
            Class clazz1 = loader1.loadClass("com.luban.initialize.demo.Person");
            Class clazz2 = loader2.loadClass("com.luban.initialize.demo.Person");
            //应用程序保护,不允许自定义java.xxx包
//            Class clazz = loader.loadClass("java.xxx");
            System.out.println(clazz1.hashCode());
            System.out.println(clazz2.hashCode());
            System.out.println(clazz1.getClassLoader());
            System.out.println(clazz2.getClassLoader());
            //157627094
            //932607259
            //MyClassLoader1
            //MyClassLoader2
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }

    private byte[] getBytes(String path){

        InputStream is = null;
        byte[] data = null;

        try{
            File f = new File(path);
            is = new FileInputStream(f);
            data = new byte[(int)f.length()];     //创建合适文件大小的数组
            is.read(data);
            is.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }
}

6.2 Tomcat自定义类加载器

类加载器之间的继承关系

image.png

Tomcat --- 自定义类加载器

应用1 --- 自定义类加载器 --- WebappClassLoader实例 --- 目录

com.luban.Test

应用2 --- 自定义类加载器 --- WebappClassLoader实例 --- set目录 --- loadClass()

com.luban.Test

应用3

Context
name.
WebappClassLoader classLoader = new WebappClassLoader();
classLoader.loader()

7.Tomca请求处理详解

8.Tomcat请求处理详解

9.Tomcat中Session功能的实现

你可能感兴趣的:(Tomcat源码解读)