最通俗易懂的 - Tomcat 核心源码仿写 第二版代码

-- 更新信息 --

第一版代码实现了基本的交互功能,但只实现了单线程,此次迭代修改多线程,并升级为Maven项目,同时优化代码排版,提高代码可读性

第一版代码介绍博客地址:最通俗易懂的 - Tomcat 核心源码仿写

-- 源码地址 --

朱元杰的开源仓库 -- Tomcat核心源码仿写

-- 正文内容 --

直接上main方法代码:

public static void main(String[] args) throws IOException {

        //创建JUL日志执行器
        Logger myTomcatLogger = Logger.getLogger("MyTomcat");

        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor();

        //启动阶段
        startupPhase(myTomcatLogger);

        //注册端口
        InetAddress localHost = InetAddress.getLocalHost();
        myTomcatLogger.info("当前服务器信息:" + localHost);
        ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);

        while (true) {

            myTomcatLogger.info("等待建立连接 - " + connectSerialNumber);
            Socket socket = serverSocket.accept();
            myTomcatLogger.info("连接已建立 - " + connectSerialNumber);
            connectSerialNumber++;

            threadPoolExecutor.execute(() -> {

                //接收Http请求报文
                ArrayList<String> httpMessage = receiveHttp(socket, myTomcatLogger);

                //处理请求
                //获取请求方式
                String httpHead = httpMessage.get(0);
                String requestStyle = httpHead.split(" ")[0];

                if (requestStyle.equals("GET")) {

                    String requestPathAndParameter = httpHead.split(" ")[1];

                    //创建HttpRequest对象
                    MyHttpRequest myHttpRequest = new MyHttpRequest();

                    //获取请求路径,并将参数置入HttpRequest对象
                    String requestPath = getRequestPathAndSetMyHttpRequest(requestPathAndParameter, myHttpRequest, myTomcatLogger);

                    //还没处理 favicon.ico 请求,先屏蔽
                    if (requestPath.equals("favicon.ico")) {
                        myTomcatLogger.info("暂时无法响应 favicon.ico");
                        return;
                    }

                    //创建HttpResponse对象
                    MyHttpResponse myHttpResponse = getMyHttpResponse(socket);

                    //反射调用Servlet方法
                    servletActuator(requestPath, "doGet", myHttpRequest, myHttpResponse);

                } else {
                    myTomcatLogger.info("还未开通其他请求方式.....");
                }
            });
        }

本次引入线程池,将交互的内容交给线程池完成,main方法负责建立socket连接

可以看到有个while(true)循环,不断建立socket连接,建立起连接后就将连接丢给threadPoolExecutor.execute 去处理

在 threadPoolExecutor.execute 中完成的还是那几步:

  1. 接收Http请求报文
  2. 获取请求路径和请求参数
  3. 创建出 HttpRequest 和 HttpResponse 对象
  4. 通过反射调用请求路径对应的 servlet 方法

我将每一步的具体实现方法都封装成了一个方法,需要的就去我的Gitee上拿源码吧

另外,通过配置文件配置线程池的方法如下:

  1. 引入 org.yaml 依赖,因为我是通过 yml 文件进行配置的
		<dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.29</version>
        </dependency>
  1. 在resources 文件下编写配置文件 config.yml 内容如下:
thread:
  pool:
    coreSize: 5
    maxSize: 10
    keepAliveTime: 60
    workQueueSize: 1000
  1. 接着就可以创建线程池了
    private static ThreadPoolExecutor createThreadPoolExecutor() throws FileNotFoundException {
        //读取yml配置文件
        Yaml yaml = new Yaml();
        InputStream inputStream = new FileInputStream("src\\main\\resources\\config.yml");
        Map<String, Object> threadPoolMap = new HashMap<>();
        threadPoolMap = yaml.load(inputStream);
        Map<String, Map<String, Object>> poolMap = (Map<String, Map<String, Object>>) threadPoolMap.get("thread");
        //创建线程池
        return new ThreadPoolExecutor(
                (Integer) poolMap.get("pool").get("coreSize"),
                (Integer) poolMap.get("pool").get("maxSize"),
                (Integer) poolMap.get("pool").get("keepAliveTime"),
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>((Integer) poolMap.get("pool").get("workQueueSize")),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
end

评论

新增邮件回复功能,回复将会通过邮件形式提醒,请填写有效的邮件!