Tomcat执行流程和核心构成并实现一个迷你版的Tomcat

Tomcat作为一个Http服务器,作用非常之大。本文主要研究Tomcat的执行流程和核心构成,并手动编写一个迷你版的Tomcat。

Tomcat执行流程

当⽤户请求某个URL资源时
1)HTTP服务器会把请求信息使⽤ServletRequest对象封装起来
2)进⼀步去调⽤Servlet容器中某个具体的Servlet
3)在 2)中,Servlet容器拿到请求后,根据URL和Servlet的映射关系,找到相应的Servlet
4)如果Servlet还没有被加载,就⽤反射机制创建这个Servlet,并调⽤Servlet的init⽅法来完成初始化
5)接着调⽤这个具体Servlet的service⽅法来处理请求,请求处理结果使⽤ServletResponse对象封装
6)把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端

核心构成

Tomcat 设计了两个核⼼组件连接器(Connector)和容器(Container)来完成 Tomcat 的两⼤核⼼功能:
1、连接器,负责对外交流: 处理Socket连接,负责⽹络字节流与Request和Response对象的转化;(Coyote)
2、容器,负责内部处理:加载和管理Servlet,以及具体处理Request请求;(Catalina)

实现一个迷你版的Tomcat

        <dependency>
            <groupId>dom4jgroupId>
            <artifactId>dom4jartifactId>
            <version>1.6.1version>
        dependency>
        <dependency>
            <groupId>jaxengroupId>
            <artifactId>jaxenartifactId>
            <version>1.1.6version>
        dependency>
1、MiniCatCtl 主要用来接受请求并返回
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

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

/**
 * @Author wangyang
 * @Create 2022/5/20 8:20
 */
public class MiniCatCtl {


    static ThreadPoolExecutor threadPoolExecutor;

    private Map<String, HttpServlet> servletMap = new HashMap<>();

    static {
        threadPoolExecutor =
                new ThreadPoolExecutor(10, 20, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    }


    public void start() throws Exception {
        ServerSocket serverSocket = new ServerSocket(8080);
        loadServlet();
        while (true) {
            Socket socket = serverSocket.accept();

            threadPoolExecutor.execute(() -> {
                try {
                    ServerRequest serverRequest = new ServerRequest(socket.getInputStream());
                    ServerResponse serverResponse = new ServerResponse(socket.getOutputStream());
                    // 静态资源处理
                    if (servletMap.get(serverRequest.getUrl()) == null) {
                        serverResponse.outputHtml(serverRequest.getUrl());
                    } else {
                        HttpServlet httpServlet = servletMap.get(serverRequest.getUrl());
                        httpServlet.service(serverRequest, serverResponse);
                    }
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            });
        }


    }

    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();

        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                // wangyang
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // com.wangyang.minicat.server.WangYangHttpServlet
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");

                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();
        }
    }


    public static void main(String[] args) throws Exception {
        MiniCatCtl main = new MiniCatCtl();
        main.start();
    }


}

2、ServerRequest 将socket输入流转为javabean
import java.io.IOException;
import java.io.InputStream;

/**
 * @Author wangyang
 * @Create 2022/5/20 16:16
 */
public class ServerRequest {

    private String url;

    private String method;

    public String getUrl() {
        return url;
    }

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

    public String getMethod() {
        return method;
    }

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

    public ServerRequest(InputStream inputStream) {
        try {
            int available = 0;
            while (available == 0) {
                available = inputStream.available();
            }
            byte[] bytes = new byte[available];
            inputStream.read(bytes);
            String message = new String(bytes);

            String[] split = message.split("\n");
            String[] requestMsg = split[0].split(" ");
            this.method = requestMsg[0];
            this.url = requestMsg[1];
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("=====>method=" + method);
        System.out.println("=====>url=" + url);

    }
}
3、ServerResponse 将socket输出流转为javabean
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Author wangyang
 * @Create 2022/5/20 16:26
 */
public class ServerResponse {

    private OutputStream outputStream;

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

    public void output(String content) throws IOException {
        outputStream.write(content.getBytes());
    }

    public void outputHtml(String path) throws IOException {
        String absolutePath = this.getClass().getResource("/").getPath();
        absolutePath = absolutePath.replaceAll("\\\\", "/") + path;

        File file = new File(absolutePath);
        if (file.exists() && file.isFile()) {
            FileInputStream fileInputStream = new FileInputStream(file);

            int count = 0;
            while (count == 0) {
                count =fileInputStream.available();
            }

            int resourceSize = count;
            output(HttpProtocolUtil.getHttpHeader200(resourceSize));
            //已经读取到的内容长度
            long written = 0;
            // 计划每次缓冲的长度
            int byteSize = 1024;
            byte[] bytes = new byte[byteSize];
            if (written < resourceSize) {
                if (written + byteSize > resourceSize) {
                    byteSize = (int)(resourceSize - written);
                    bytes = new byte[byteSize];
                }

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

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

        } else {
            // 404
            output(HttpProtocolUtil.getHttpHeader404());
        }

    }
}
4、HttpProtocolUtil 定义返回头格式
/**
 * @Author wangyang
 * @Create 2022/5/20 16:46
 */
public class HttpProtocolUtil {

    public static String getHttpHeader200(Integer contentLength) {
        return "HTTP/1.1 200 OK \n" +
                "Content-Type: text/html \n" +
                "Content-Length: " + contentLength + " \n" +
                "\r\n";
    }


    public static String getHttpHeader404() {
        String str404 = "

404 not found

"
; return "HTTP/1.1 404 not found \n" + "Content-Type: text/html \n" + "Content-Length: " + str404.getBytes().length + " \n" + "\r\n" + str404; } }
5、Servlet 接口,定义init()、destory()、service()
/**
 * @Author wangyang
 * @Create 2022/5/20 16:38
 */
public interface Servlet {

    void init() throws Exception;

    void destory() throws Exception;

    void service(ServerRequest serverRequest, ServerResponse serverResponse) throws Exception;
}
6、HttpServlet 接收Get和Post请求
/**
 * @Author wangyang
 * @Create 2022/5/20 16:38
 */
public abstract class HttpServlet implements Servlet {

    public abstract void doGet(ServerRequest serverRequest, ServerResponse serverResponse);

    public abstract  void doPost(ServerRequest serverRequest, ServerResponse serverResponse);

    @Override
    public void service(ServerRequest serverRequest, ServerResponse serverResponse) throws Exception {
        if ("GET".equalsIgnoreCase(serverRequest.getMethod())) {
            doGet(serverRequest, serverResponse);
        } else {
            doPost(serverRequest, serverResponse);
        }
    }
}
7、WangYangHttpServlet 自定义servlet
/**
 * @Author wangyang
 * @Create 2022/5/20 16:44
 */
public class WangYangHttpServlet extends HttpServlet {
    @Override
    public void doGet(ServerRequest serverRequest, ServerResponse serverResponse) {
        String content = "

wangyangServlet get

"
; try { serverResponse.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content); } catch (Exception e) { e.printStackTrace(); } } @Override public void doPost(ServerRequest serverRequest, ServerResponse serverResponse) { String content = "

wangyangServlet post

"
; try { serverResponse.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content); } catch (Exception e) { e.printStackTrace(); } } @Override public void init() throws Exception { } @Override public void destory() throws Exception { } }
8、demo.html 自定义静态页面
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>static resoucetitle>
head>
<body>
Hello Minicat-static resouce!
body>
html>
9、web.xml 定义加入到内存的servlet

<web-app>
    <servlet>
        <servlet-name>wangyangservlet-name>
        <servlet-class>com.wangyang.minicat.server.WangYangHttpServletservlet-class>
    servlet>

    <servlet-mapping>
        <servlet-name>wangyangservlet-name>
        <url-pattern>/indexurl-pattern>
    servlet-mapping>

web-app>
项目结构图

Tomcat执行流程和核心构成并实现一个迷你版的Tomcat_第1张图片

测试结果

Tomcat执行流程和核心构成并实现一个迷你版的Tomcat_第2张图片

Tomcat执行流程和核心构成并实现一个迷你版的Tomcat_第3张图片

Tomcat执行流程和核心构成并实现一个迷你版的Tomcat_第4张图片

你可能感兴趣的:(笔记,tomcat,java,服务器)