标签搜索

目 录CONTENT

文章目录

手写框架之Tomcat

陈铭
2021-07-11 / 0 评论 / 2 点赞 / 399 阅读 / 1,522 字 / 正在检测是否收录...

响应流程

示意图

如图所示,Server作为一个完整的Tomcat,里面有许多Service(当然本项目就写了一个),它对应着一个Web应用。Service底下包含着Connector和Container,其中Connector用来接收请求,并根据请求的协议将请求发给Container处理,这个处理的过程是用创建的新线程进行的(BIO);而Container可以视为Servlet的容器,但不是直接的容器。Container底下Engine,Engine分发请求至对应的Host;Host只会处理与请求url的host对应的请求,接着会下发至Context;Context用来判别请求的资源路径,根据资源路径发送至Wrapper;Wrapper封装了Servlet,可以视作Servlet的直接容器。并且以上这些组件都实现了LifeCycle接口,该接口规定了各个组件生命周期的各种API。
image

关键类及其接口

Lifecycle接口

该接口定义了组件生命周期的各种行为,包括初始化、开始、停止、销毁。

public interface Lifecycle {
    // 初始化方法
    public void init();
    // 启动方法
    public void start();
    // 停止方法,和start对应
    public void stop();
    // 销毁方法,和init对应
    public void destroy();
    // 获取生命周期状态
    public LifecycleState getState();
}

Connector类

Connector类主要封装了ServerSocket,用来监听前端请求,并解析数据封装成Request、Response对象,以便传给Container类进行处理。

传递请求的本质上是创建新的线程进行处理,这是因为Tomcat处理Http请求本身就是BIO的过程,为了提升效率都是用多线程进行处理多个请求的,这里也是比较粗放的模拟了创建线程处理请求。

public class Connector implements Lifecycle, Component {
    private LifecycleState lifecycleState = LifecycleState.NEW;
    public static HttpServletRequest request;
    public static HttpServletResponse response;
    public static Component container;
    public static ServerSocket serverSocket;

    static {
        try {
            serverSocket = new ServerSocket(8080);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public Connector() {

    }


    @Override
    public void init() {
        container = Service.container;
        lifecycleState = LifecycleState.INITIALIZED;
    }

    @Override
    public void start() {

        while (true) {
            try {
                Socket socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

                String method = bufferedReader.readLine();
                if (method == null) {
                    continue;
                }
                String context=method.split(" ")[1];
                if (method.startsWith("GET")) {
                    method = method.substring(0, 3);
                } else {
                    method = method.substring(0, 4);
                }

                String host="";
                while (true) {
                    String msg = bufferedReader.readLine();
                    if (msg.length()==0){
                        break;
                    }
                    if (msg.startsWith("Host")){
                        host=msg.split(":")[1].substring(1);
                        break;
                    }

                }

                request = new HttpServletRequest(method,host,context,inputStream);
                response = new HttpServletResponse(outputStream);


                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Container container = (Container) Connector.container;
                        container.start();
                        try {
                            if (socket != null)
                                socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

            } catch (IOException e) {
                e.printStackTrace();
            } finally {

            }
        }

    }

    @Override
    public void stop() {
        try {
            if (serverSocket != null)
                serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void destroy() {
        stop();
    }

    @Override
    public LifecycleState getState() {
        return null;
    }

    @Override
    public Component getChild() {
        return null;
    }

    @Override
    public void setChild(Component child) {

    }
}

Request和Response类

Request主要封装了请求路径、host、输入流对象以及请求方法等;而Response封装了输出流对象和响应头(简化响应数据的封装,直接预置了text/html类型的响应头)。

public class HttpServletRequest {
    private String method;
    private String host;
    private String context;
    private InputStream inputStream;

    public HttpServletRequest(String method, String host, String context, InputStream inputStream) {
        this.method = method;
        this.host = host;
        this.context = context;
        this.inputStream = inputStream;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getContext() {
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public String getMethod() {
        return method;
    }

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

    public InputStream getInputStream() {
        return inputStream;
    }

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


public class HttpServletResponse {

    private OutputStream outputStream;
    private String responseHeader="HTTP/1.1 200\r\nContent-Type:text/html\r\n\r\n";

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

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public void setOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public String getResponseHeader() {
        return responseHeader;
    }

    public void setResponseHeader(String responseHeader) {
        this.responseHeader = responseHeader;
    }
}

HttpServlet类

Container接收到Request、Response对象一级一级传递下去最终到了Servlet,这里才是处理请求,返回响应的业务逻辑所在之处。根据Request的请求方法类型,执行不同的方法(doGet、doPost)。并且空闲的Servlet应该及时销毁,这里也是用Timer对象进行一个周期任务,监听Servlet是否应该被销毁。

public class HttpServlet implements Lifecycle, Component {

    private LifecycleState lifecycleState = LifecycleState.NEW;
    private long deathTime;
    private Timer timer;

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws RuntimeException {
        try {
            response.getOutputStream().write((response.getResponseHeader() + "Hello!!").getBytes());
            response.getOutputStream().flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (response.getOutputStream() != null)
                    response.getOutputStream().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws RuntimeException {
        doGet(request, response);
    }


    @Override
    public void init() {
        if (lifecycleState == LifecycleState.NEW) {
            lifecycleState = LifecycleState.INITIALIZED;
        }

        System.out.println("=========HttpServlet初始化成功,超时时间为2s=========");
    }

    @Override
    public void start() {
        if (timer == null) {
            timer = new Timer();
        }
        if (lifecycleState == LifecycleState.NEW) {
            init();
        } else if (lifecycleState == LifecycleState.STOPPING || lifecycleState == LifecycleState.IDELING) {
            lifecycleState = LifecycleState.INITIALIZED;
        } else if (lifecycleState == LifecycleState.DESTROYING) {
            //如果正在销毁,返回wrapper重新执行


        }

        if (lifecycleState == LifecycleState.INITIALIZED) {
            lifecycleState = LifecycleState.STARTING;
            HttpServletRequest request = Connector.request;
            HttpServletResponse response = Connector.response;

            deathTime = System.currentTimeMillis();
            try {
                switch (request.getMethod()) {
                    case "GET":
                        doGet(request, response);
                        if (System.currentTimeMillis() - deathTime >= 2000) {
                            System.out.println("=========HttpServlet的get请求超时,超时时间为2s=========");
                        }
                        break;
                    case "POST":
                        doPost(request, response);
                        if (System.currentTimeMillis() - deathTime >= 2000) {
                            System.out.println("=========HttpServlet的post请求超时,超时时间为2s=========");
                        }
                        break;
                }

                //response返回消息
                //。。。。。。。。。。

                //闲置将被销毁
                deathTime = System.currentTimeMillis();
                lifecycleState = LifecycleState.IDELING;
                canDestory();
            } catch (Exception e) {
                stop();
                System.out.println("=========HttpServlet出现未知错误=========");
                e.printStackTrace();
                erroResponse();
            }

        }
    }

    private void erroResponse() {
        OutputStream outputStream = Connector.response.getOutputStream();
        try {
            outputStream.write((Connector.response.getResponseHeader() + "erro!!").getBytes());
            outputStream.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void canDestory() {
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (System.currentTimeMillis() - deathTime >= 2000 && lifecycleState == LifecycleState.IDELING) {
                    System.out.println("=========HttpServlet的闲置时间超过2s,将被销毁=========");
                    destroy();
                }
            }
        }, 10, 500);
    }

    @Override
    public void stop() {
        lifecycleState = LifecycleState.STOPPING;
    }

    @Override
    public void destroy() {

        lifecycleState = LifecycleState.DESTROYING;
        Component component = Connector.container;
        while (true) {
            component = component.getChild();
            if (component instanceof Wrapper) {
                break;
            }
        }
        component.setChild(null);
        timer.cancel();
        timer = null;
        System.gc();
    }

    @Override
    public LifecycleState getState() {
        return this.lifecycleState;
    }

    @Override
    public Component getChild() {
        return null;
    }

    @Override
    public void setChild(Component child) {

    }


}

测试结果

前端请求

如图所示,前端请求对应的url,得到响应。
image

servlet的初始化和销毁

servlet在请求到来时会进行初始化并且处理请求,同时会启动线程监控生命周期,对于空闲两秒的servlet会进行销毁。
image

Gitee源码

源码

2

评论区