Tomcat(二)Tomcat源码剖析及调优

一. ⼿写实现迷你版 Tomcat 准备工作

(一)需求

Minicat 要做的事情:作为⼀个服务器软件提供服务的,也即我们可以通过浏览器客户端发送 http 请求,Minicat 可以接收到请求进⾏处理,处理之后的结果可以返回浏览器客户端。

1. 提供服务,接收请求(Socket通信)

2. 请求信息封装成Request对象(Response对象)

3. 客户端请求资源,资源分为静态资源(html)和动态资源(Servlet

4. 资源返回给客户端浏览器

(二)实现计划

递进式完成以上需求,计划 V1.0 V2.0 V3.0 版本的需求

1. V1.0需求:

浏览器请求 http://localhost:8080 , 返回⼀个固定的字符串到⻚⾯ "Hello Minicat!"

2. V2.0需求:

封装 Request Response 对象,返回 html 静态资源⽂件

3. V3.0需求:

可以请求动态资源( Servlet

二. ⼿写实现迷你版 Tomcat实现过程

(一)V1.0 版本

1. Minicat的主类(启动类)

package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**定义socket监听的端口号*/
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
         /*
            完成Minicat 1.0版本
            需求:浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!"
         */
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port:" + port);

        while(true) {
            Socket socket = serverSocket.accept();
            // 有了socket,接收到请求,获取输出流
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello Minicat!";
            //调用封装好的HttpProtocolUtil类的方法,输出响应头
            String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseText.getBytes());
            socket.close();
        }
    }

     /**
     * Minicat 的程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 创建HttpProtocolUtil类

创建该类的目的旨在向浏览器输出时,按照浏览器的解析格式输出文件头信息。分别为响应码200级响应码404提供请求头信息。

package server;

/**
 * http协议工具类,主要是提供响应头信息,这里我们只提供200和404的情况
 */
public class HttpProtocolUtil {

    /**
     * 为响应码200提供请求头信息
     * @return
     */
    public static String getHttpHeader200(long contentLength) {
        return "HTTP/1.1 200 OK \n" +
                "Content-Type: text/html \n" +
                "Content-Length: " + contentLength + " \n" +
                "\r\n";
    }

    /**
     * 为响应码404提供请求头信息(此处也包含了数据内容)
     * @return
     */
    public static String getHttpHeader404() {
        String str404 = "<h1>404 not found</h1>";
        return "HTTP/1.1 404 NOT Found \n" +
                "Content-Type: text/html \n" +
                "Content-Length: " + str404.getBytes().length + " \n" +
                "\r\n" + str404;
    }
}

3. 测试

浏览器中输入:localhost:8080/ 即可,浏览器中输出“Hello Minicat!”

(二)V2.0 版本

1. Minicat的主类(启动类)

package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**定义socket监听的端口号*/
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        /**
         * 完成Minicat 2.0版本
         * 需求:封装Request和Response对象,返回html静态资源文件
         */
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port:" + port);
        while(true) {
            //监听浏览器发送过来的请求
            Socket socket = serverSocket.accept();
            //接收浏览器发送过来的请求内容,这些内容就是要刷选后封装成request对象的内容。
            InputStream inputStream = socket.getInputStream();

            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            response.outputHtml(request.getUrl());
            socket.close();

        }
    }

     /**
     * Minicat 的程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 创建Request类

用Request封装socket.getInputStream();

package server;

import java.io.IOException;
import java.io.InputStream;

/**
 * 把请求信息封装为Request对象(根据InputSteam输入流封装)
 */
public class Request {

    private String method; // 请求方式,比如GET/POST
    private String url;  // 例如 /,/index.html

    private InputStream inputStream;  // 输入流,浏览器申请的其它属性从输入流中解析出来


    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    public Request() {
    }


    // 构造器,输入流传入
    public Request(InputStream inputStream) throws IOException {
        this.inputStream = inputStream;

        // 从输入流中获取请求信息,
        int count = 0;
        //在读写操作前先得知数据流里有多少个字节可以读取
        //这里循环读,直到count读到内容
        while (count == 0) {
            count = inputStream.available();
        }

        byte[] bytes = new byte[count];
        //将输入流中的字节读到数组bytes中
        inputStream.read(bytes);

        String inputStr = new String(bytes);
        // 获取第一行请求头信息
        // 以换行符进行分割
        String firstLineStr = inputStr.split("\\n")[0];  // GET / HTTP/1.1
        //把GET / HTTP/1.1按照空格进行分割
        String[] strings = firstLineStr.split(" ");

        this.method = strings[0];
        this.url = strings[1];

        System.out.println("=====>>method:" + method);
        System.out.println("=====>>url:" + url);


    }
}

3. 创建Response类

用response封装socket.getInputStream()

package server;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 封装Response对象,需要依赖于OutputStream
 *
 * 该对象需要提供核心方法,输出html
 */
public class Response {

    private OutputStream outputStream;

    public Response() {
    }

    public Response(OutputStream outputStream) {
        this.outputStream = outputStream;
    }


    // 使用输出流输出指定字符串
    public void output(String content) throws IOException {
        outputStream.write(content.getBytes());
    }


    /**
     *
     * @param path  url,随后要根据url来获取到静态资源的绝对路径,进一步根据绝对路径读取该静态资源文件,最终通过
     *                  输出流输出
     *              /-----> classes
     */
    public void outputHtml(String path) throws IOException {
        // 获取静态资源文件的绝对路径,自定义一个StaticResourceUtil工具类
        String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);

        // 输出静态资源文件
        // 将绝对路径中的静态资源转换成文件
        File file = new File(absoluteResourcePath);
        // 判断文件是否存在
        if(file.exists() && file.isFile()) {
            // 读取静态资源文件,输出静态资源,自定义一个StaticResourceUtil工具类
            StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
        }else{
            // 输出404
            output(HttpProtocolUtil.getHttpHeader404());
        }

    }

}

4. 创建StaticResourceUtil类

目的是:读取静态资源文件,输出静态资源

package server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class StaticResourceUtil {

    /**
     * 获取静态资源文件的绝对路径
     * @param path
     * @return
     */
    public static String getAbsolutePath(String path) {
        // 获取当前类所在的根目录开始的绝对路径
        String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
        // 再将以上的绝对路径拼接传进来的路径
        // absolutePath.replaceAll("\\\\","/")表示统一的把系统分割符转换成“/”
        return absolutePath.replaceAll("\\\\","/") + path;
    }


    /**
     * 读取静态资源文件输入流,通过输出流输出
     */
    public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {

        int count = 0;
        while(count == 0) {
            // 获取输入流中内容的字节数
            count = inputStream.available();
        }

        int resourceSize = count;
        // 输出http请求头,然后再输出具体内容
        outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());

        // 读取内容输出
        long written = 0 ;// 已经读取的内容长度
        int byteSize = 1024; // 计划每次缓冲的长度
        byte[] bytes = new byte[byteSize];

        //输出静态资源采取缓冲的方式,一步步输出静态资源
        while(written < resourceSize) {
            if(written  + byteSize > resourceSize) {  // 说明剩余未读取大小不足一个1024长度,那就按真实长度处理
                byteSize = (int) (resourceSize - written);  // 剩余的文件内容长度
                bytes = new byte[byteSize];
            }

            inputStream.read(bytes);
            outputStream.write(bytes);

            outputStream.flush();
            written+=byteSize;
        }
    }
}

5. 创建静态资源文件index.html放在工程的resources目录下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>static resouce</title>
</head>
<body>
Hello Minicat-static resouce!
</body>
</html>

6. 测试

v2.0版本只改动以上代码,其余未改动代码参照V1.0

浏览器中输入浏览器中输入:localhost:8080/ 即可,浏览器中返回index.html静态资源。

(三)V3.0 版本

1. Minicat的主类(启动类)

loadServlet()方法用来加载web.xml并解析web.xml,将server.LagouServlet类实例化
package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**定义socket监听的端口号*/
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {

        // 加载解析相关的配置,web.xml
        loadServlet();

       /**
         * 完成Minicat 3.0版本
         * 需求:可以请求动态资源(Servlet)
         */
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port:" + port);
        while(true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();

            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            // 静态资源处理
            if(servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            }else{
                // 动态资源servlet请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }

            socket.close();

        }

    }

    /**
     * 加载解析web.xml,初始化Servlet
     */
    private void loadServlet() {
        //将配置文件读取成输入流
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        //开始解析web.xml
        SAXReader saxReader = new SAXReader();

        try {
            //生成文档对象
            Document document = saxReader.read(resourceAsStream);
            //获取web.xml中的根节点
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element =  selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                // 拿到servlet-name这个节点
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                // 通过节点获取该节点的值,也就是“lagou”
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                // 拿到servlet-class这个节点
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                // 通过节点获取该节点的值,也就是“server.LagouServlet”
                String servletClass = servletclassElement.getStringValue();


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());

            }



        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

     /**
     * Minicat 的程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 创建Servlet接口

创建Servlet接口,旨在建立Servlet规范,创建三个方法用来继承,其中service方法用来选择是否走doGet还是doPost。

package server;

public interface Servlet {

    void init() throws Exception;

    void destory() throws Exception;

    void service(Request request,Response response) throws Exception;
}

3. 创建HttpServlet抽象类继承Servlet接口

该类创建doGet及doPost抽象方法,重写接口的service方法

package server;

public abstract class HttpServlet implements Servlet{


    public abstract void doGet(Request request,Response response);

    public abstract void doPost(Request request,Response response);


    @Override
    public void service(Request request, Response response) throws Exception {
        if("GET".equalsIgnoreCase(request.getMethod())) {
            doGet(request,response);
        }else{
            doPost(request,response);
        }
    }
}

4. 创建LagouServlet类继承HttpServlet抽象类

package server;

import java.io.IOException;

public class LagouServlet extends HttpServlet {
    @Override
    public void doGet(Request request, Response response) {


        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String content = "<h1>LagouServlet get</h1>";
        try {
            response.output((HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(Request request, Response response) {
        String content = "<h1>LagouServlet post</h1>";
        try {
            response.output((HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init() throws Exception {

    }

    @Override
    public void destory() throws Exception {

    }
}

5. 创建web.xml

指定LagouServlet的全路径

指定访问路径。

6. 测试

v3.0版本只展示改动的代码,未改动部分参照V1.0及V2.0

浏览器中输入浏览器中输入:localhost:8080/lagou

(四)多线程改造(使用线程池)

1. Minicat的主类(启动类)

package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**定义socket监听的端口号*/
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }


        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize =50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        //这里使用队列的一个实现类ArrayBlockingQueue,长度设置成50
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        //使用默认的线程工厂
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        // 拒绝策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,// 线程池的基本大小
                maximumPoolSize, // 线程池的最大大小
                keepAliveTime, // 线程空闲时间
                unit, // 线程时间的单位
                workQueue, // 线程请求队列
                threadFactory, // 线程工厂
                handler // 线程的拒绝策略
        );

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {

        // 加载解析相关的配置,web.xml
        loadServlet();

       System.out.println("=========>>>>>>使用线程池进行多线程改造");
        /*
            多线程改造(使用线程池)
         */
        while(true) {

            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    /**
     * 加载解析web.xml,初始化Servlet
     */
    private void loadServlet() {
        //将配置文件读取成输入流
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        //开始解析web.xml
        SAXReader saxReader = new SAXReader();

        try {
            //生成文档对象
            Document document = saxReader.read(resourceAsStream);
            //获取web.xml中的根节点
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element =  selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                // 拿到servlet-name这个节点
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                // 通过节点获取该节点的值,也就是“lagou”
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                // 拿到servlet-class这个节点
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                // 通过节点获取该节点的值,也就是“server.LagouServlet”
                String servletClass = servletclassElement.getStringValue();


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());

            }



        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

     /**
     * Minicat 的程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 创建RequestProcessor类

创建线程,将浏览器的访问Request的请求塞进线程

package server;

import java.io.InputStream;
import java.net.Socket;
import java.util.Map;

public class RequestProcessor extends Thread {

    private Socket socket;
    private Map<String,HttpServlet> servletMap;

    public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {
        this.socket = socket;
        this.servletMap = servletMap;
    }

    @Override
    public void run() {
        try{
            InputStream inputStream = socket.getInputStream();

            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            // 静态资源处理
            if(servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            }else{
                // 动态资源servlet请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }

            socket.close();

        }catch (Exception e) {
            e.printStackTrace();
        }

    }
}

(五)完整代码展示

迷你版Tomcat完整代码展示

三. Tomcat 源码构建

(一)下载源码

apache-tomcat-8.5.50-src

(二)源码导⼊IDE之前准备⼯作

解压 tar.gz 压缩包,得到⽬录 apache-tomcat-8.5.50-src

进⼊ apache-tomcat-8.5.50-src ⽬录,创建⼀个pom.xml⽂件,⽂件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<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>apache-tomcat-8.5.50-src</artifactId>
    <name>Tomcat8.5</name>
    <version>8.5</version>

<build>
<!--指定源⽬录-->
    <finalName>Tomcat8.5</finalName>
    <sourceDirectory>java</sourceDirectory>
    <resources>
        <resource>
            <directory>java</directory>
        </resource>
    </resources>
    <plugins>
<!--引⼊编译插件-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <encoding>UTF-8</encoding>
                <source>11</source>
                <target>11</target>
            </configuration>
        </plugin>
    </plugins>
</build>
<!--tomcat 依赖的基础包-->
<dependencies>
    <dependency>
        <groupId>org.easymock</groupId>
        <artifactId>easymock</artifactId>
        <version>3.4</version>
    </dependency>
    <dependency>
        <groupId>ant</groupId>
        <artifactId>ant</artifactId>
        <version>1.7.0</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.core.compiler</groupId>
        <artifactId>ecj</artifactId>
        <version>4.5.1</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.soap</groupId>
        <artifactId>javax.xml.soap-api</artifactId>
        <version>1.4.0</version>
    </dependency>
</dependencies>
</project>
apache-tomcat-8.5.50-src ⽬录中创建 source ⽂件夹
conf webapps ⽬录移动到刚刚创建的 source ⽂件夹中

(三) 导⼊源码⼯程到IDE并进⾏配置

将源码⼯程导⼊到 IDEA
tomcat 的源码程序启动类 Bootstrap 配置 VM 参数,因为 tomcat 源码运⾏也需要加载配置⽂
件等。
-Dcatalina.home = /Users/yingdian/workspace/servers/apache-tomcat-8.5.50-
src/source
-Dcatalina.base = /Users/yingdian/workspace/servers/apache-tomcat-8.5.50-
src/source
-Djava.util.logging.manager = org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file = /Users/yingdian/workspace/servers/apache
tomcat-8.5.50-src/source/conf/logging.properties
运⾏ Bootstrap 类的 main 函数,此时就启动了 tomcat ,启动时候会去加载所配置的 conf ⽬录下
server.xml 等配置⽂件,所以访问 8080 端⼝即可,但此时我们会遇到如下的⼀个错误
原因是 Jsp 引擎 Jasper 没有被初始化,从⽽⽆法编译 JSP ,我们需要在 tomcat 的源码 ContextConfifig 类中的 confifigureStart ⽅法中增加⼀⾏代码将 Jsp 引擎初始化,如下:

 

四. 核⼼流程源码剖析

 Tomcat核⼼流程源码剖析_舞鹤白沙编码日志-CSDN博客

五. Tomcat 类加载机制剖析

 Tomcat 类加载机制剖析_舞鹤白沙编码日志-CSDN博客

六. Tomcat Https 的⽀持

(一)HTTPS 简介

 

Http 超⽂本传输协议,明⽂传输 ,传输不安全, https 在传输数据的时候会对数据进⾏加密
ssl 协议
TLS(transport layer security) 协议
HTTPS HTTP 的主要区别
  • HTTPS协议使⽤时需要到电⼦商务认证授权机构(CA)申请SSL证书
  • HTTP默认使⽤8080端⼝,HTTPS默认使⽤8443端⼝
  • HTTPS则是具有SSL加密的安全性传输协议,对数据的传输进⾏加密,效果上相当于HTTP的升级
  • HTTP的连接是⽆状态的,不安全的;HTTPS协议是由SSL+HTTP协议构建的可进⾏加密传输、身份认证的⽹络协议,⽐HTTP协议安全

HTTPS⼯作原理

 (二)Tomcat HTTPS 的⽀持

1. 使⽤ JDK 中的 keytool ⼯具⽣成免费的秘钥库⽂件(证书)

keytool -genkey -alias lagou -keyalg RSA -keystore lagou.keystore

 2. 配置conf/server.xml

<Connector port = "8443" protocol = "org.apache.coyote.http11.Http11NioProtocol"
maxThreads = "150" schema = "https" secure = "true" SSLEnabled = "true" >
<SSLHostConfig>
<Certificate
certificateKeystoreFile = "/Users/yingdian/workspace/servers/apache-tomcat-
8.5.50/conf/lagou.keystore" certificateKeystorePassword = "lagou123" type = "RSA"
/>
</SSLHostConfig>
</Connector>

 3. 使⽤https协议访问8443端⼝(https://localhost:8443)。

七. Tomcat 性能优化策略

系统性能的衡量指标,主要是响应时间和吞吐量。

1 )响应时间:执⾏某个操作的耗时;
2) 吞吐量:系统在给定时间内能够⽀持的事务数量,单位为 TPS Transactions PerSecond 的缩写,也就是事务数 / 秒,⼀个事务是指⼀个客户机向服务器发送请求然后服务器做出反应的过程。
Tomcat 优化从两个⽅⾯进⾏
1 JVM 虚拟机优化(优化内存模型)
2 Tomcat ⾃身配置的优化(⽐如是否使⽤了共享线程池? IO 模型?)
学习优化的原则提供给⼤家优化思路,没有说有明确的参数值⼤家直接去使⽤,必须根据⾃⼰的真实⽣产环境来进⾏调 整,调优是⼀个过程

JVM优化深度剖析_舞鹤白沙编码日志-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

enterpc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值