zoukankan      html  css  js  c++  java
  • 【Tomcat】手写迷你版Tomcat

    源码地址

    https://github.com/CoderXiaohui/mini-tomcat

    一,分析

    Mini版Tomcat需要实现的功能

    作为一个服务器软件提供服务(通过浏览器客户端发送Http请求,它可以接收到请求进行处理,处理之后的结果返回浏览器客户端)。

    1. 提供服务,接收请求(socket通信)
    2. 请求信息封装成Request对象,封装响应信息Response对象
    3. 客户端请求资源,资源分为静态资源(html)和动态资源(servlet)
    4. 资源返回给客户端浏览器

    *Tomcat的入口就是一个main函数

    二,开发——准备工作

    2.1 新建Maven工程

    image-20201227160427940

    image-20201227161023307

    2.2 定义编译级别

    <?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>com.dxh</groupId>
        <artifactId>MiniCat</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>11</source>
                        <target>11</target>
                        <encoding>utf-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    2.3 新建主类编写启动入口和端口

    这里我们把socket监听的端口号定义在主类中。

    package server;
    
    /**
     * Minicat的主类
     */
    public class Bootstrap {
        /**
         * 定义Socket监听的端口号
         */
        private int port = 8080;
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    
        /**
         * Minicat的启动入口
         * @param args
         */
        public static void main(String[] args) {
            
        }
    }
    
    

    三,开发——1.0版本

    循序渐进,一点一点的完善,1.0版本我们需要的需求是:

    • 浏览器请求http://localhost:8080,返回一个固定的字符串到页面“Hello Minicat”

    3.1 编写start方法以及遇到的问题

    start方法主要就是监听上面配置的端口,然后得到其输出流,最后写出。

    /**
     * MiniCat启动需要初始化展开的一些操作
     */
    public void start() throws IOException {
        /*
           完成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();
            outputStream.write("Hello Minicat!".getBytes());
            socket.close();
        } 
    }
    

    完整的代码:

    
    /**
     * Minicat的主类
     */
    public class Bootstrap {
        /**
         * 定义Socket监听的端口号
         */
        private int port = 8080;
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    
        /**
         * Minicat的启动入口
         * @param args
         */
        public static void main(String[] args) {
            Bootstrap bootstrap = new Bootstrap();
            try {
                //启动Minicat
                bootstrap.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * MiniCat启动需要初始化展开的一些操作
         */
        public void start() throws IOException {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("========>>Minicat start on port:"+port);
    
            while(true){
                Socket socket = serverSocket.accept();
                //有了socket,接收到请求,获取输出流
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write("Hello Minicat!".getBytes());
                socket.close();
            }
        }
    
    }
    
    

    此时,如果启动项目,从浏览器中输入http://localhost:8080/,能够正常接收到请求吗?

    不能!

    问题分析:

    启动项目,从浏览器中输入http://localhost:8080/,可看到返回结果如下图:
    image-20201227165334660

    因为Http协议是一个应用层协议,其规定了请求头、请求体、响应同样,如果没有这些东西的话浏览器无法正常显示。代码中直接把”Hello Minicat!“直接输出了,

    3.2 解决问题,修改代码:

    1. 新建一个工具类,主要提供响应头信息

      package server;
      
      /**
       * http协议工具类,主要提供响应头信息,这里我们只提供200和404的情况
       */
      public class HttpProtocolUtil {
      
          /**
           *  为响应码200提供请求头信息
           */
          public static String getHttpHeader200(long contentLength){
              return "HTTP/1.1 200 OK 
      " +
                      "Content-Type: text/html 
      " +
                      "Content-Length: "+contentLength +"
      "+
                      "
      ";
          }
      
      
          /**
           *  为响应码404提供请求头信息(也包含了数据内容)
           */
          public static String getHttpHeader404(){
              String str404="<h1>404 not found</h1>";
              return "HTTP/1.1 404 NOT Found 
      " +
                      "Content-Type: text/html 
      " +
                      "Content-Length: "+str404.getBytes().length +"
      "+
                      "
      " + str404;
          }
      
      }
      
      
    2. 修改start方法

          public void start() throws IOException {
      
              ServerSocket serverSocket = new ServerSocket(port);
              System.out.println("========>>Minicat start on port:"+port);
      
              while(true){
                  Socket socket = serverSocket.accept();
                 
                  OutputStream outputStream = socket.getOutputStream();
                  String data = "Hello Minicat!";
                  String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
                  outputStream.write(responseText.getBytes());
                  socket.close();
              }
          }
      
    3. 访问~
      image-20201227172649749

    成功。

    四,开发——2.0版本

    需求:

    • 封装Request和Response对象
    • 返回html静态资源文件

    4.1 封装前准备

    新建一个类,Bootstrap2 (为了方便与1.0版本做对比)。获得输入流,并打印出来看看。

    package server;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * Minicat的主类
     */
    public class Bootstrap2 {
        /**
         * 定义Socket监听的端口号
         */
        private int port = 8080;
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    
        /**
         * Minicat的启动入口
         * @param args
         */
        public static void main(String[] args) {
            Bootstrap2 bootstrap = new Bootstrap2();
            try {
                //启动Minicat
                bootstrap.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * MiniCat启动需要初始化展开的一些操作
         */
        public void start() throws IOException {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("========>>Minicat start on port:"+port);
    
            while (true){
                Socket socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                //从输入流中获取请求信息
                int count = 0 ;
                while (count==0){
                    count = inputStream.available();
                }
                byte[] bytes = new byte[count];
                inputStream.read(bytes);
                System.out.println("请求信息=====>>"+new String(bytes));
                socket.close();
            }
    
        }
    
    }
    

    打印出来的信息:

    image-20201227191058136

    这里我们需要得到的是 请求方式(GET) 和 url (/) ,接下来封装Request的时候也是只封装这两个属性

    4.2封装Request、Response对象

    4.2.1 封装Request

    只封装两个参数——method和url

    1. 新建Request类

    2. 该类有三个属性(String methodString urlInputStream inputStream
      method和url都是从input流中解析出来的。

    3. GET SET方法

    4. 编写有参构造

      /**
       * 构造器 输入流传入
       */
      public Request(InputStream inputStream) throws IOException {
          this.inputStream = inputStream;
          //从输入流中获取请求信息
          int count = 0 ;
          while (count==0){
              count = inputStream.available();
          }
          byte[] bytes = new byte[count];
          inputStream.read(bytes);
          String inputsStr = new String(bytes);
          //获取第一行数据
          String firstLineStr = inputsStr.split("\n")[0];  //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);
      }
      
    5. 无参构造

    完整的Request.java

    package server;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 把我们用到的请求信息,封装成Response对象 (根据inputSteam输入流封装)
     */
    public class Request {
        /**
         * 请求方式 例如:GET/POST
         */
        private String method;
    
        /**
         * / , /index.html
         */
        private String url;
    
        /**
         * 其他的属性都是通过inputStream解析出来的。
         */
        private InputStream inputStream;
    
        /**
         * 构造器 输入流传入
         */
        public Request(InputStream inputStream) throws IOException {
            this.inputStream = inputStream;
            //从输入流中获取请求信息
            int count = 0 ;
            while (count==0){
                count = inputStream.available();
            }
            byte[] bytes = new byte[count];
            inputStream.read(bytes);
            String inputsStr = new String(bytes);
            //获取第一行数据
            String firstLineStr = inputsStr.split("\n")[0];  //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);
        }
    
        public Request() {
        }
    
        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;
        }
    a
        public InputStream getInputStream() {
            return inputStream;
        }
    
        public void setInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }
    }
    
    

    4.2.2 封装Response

    package server;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    /**
     * 封装Response对象,需要依赖于OutputStream
     *
     */
    public class Response{
        private OutputStream outputStream;
    
        public Response(OutputStream outputStream) {
            this.outputStream = outputStream;
        }
    
        public Response() {
        }
    
        /**
         * @param path 指的就是 Request中的url ,随后要根据url来获取到静态资源的绝对路径,进一步根据绝对路径读取该静态资源文件,最终通过输出流输出
         */
        public void outputHtml(String path) throws IOException {
            //获取静态资源的绝对路径
            String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);
    
            //输出静态资源文件
            File file = new File(absoluteResourcePath);
            if (file.exists() && file.isFile()){
                //读取静态资源文件,输出静态资源
                StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
            }else{
                //输出404
                output(HttpProtocolUtil.getHttpHeader404());
            }
        }
    
        //使用输出流输出指定字符串
        public void output(String context) throws IOException {
            outputStream.write(context.getBytes());
        }
    }
    

    2.0版本只考虑输出静态资源文件

    我们来分析一下outputHtml(String path)这个方法

    首先,path就指 Request中的url,我们要用这个url找到该资源的绝对路径:

    1. 根据path,获取静态资源的绝对路径

      public static String getAbsolutePath(String path){
          String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
          return absolutePath.replaceAll("\\","/")+path;
      }
      
    2. 判断静态资源是否存在

      • 不存在:输出404
    3. 存在:读取静态资源文件,输出静态资源

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

    把上述的第一步和第三步的方法封装到一个类中:

    package server;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class StaticResourceUtil {
    
        /**
         * 获取静态资源方法的绝对路径
         */
        public static String getAbsolutePath(String path){
            String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
            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;
            }
        }
    
    }
    
    

    测试:

    1. 修改Bootstrap2.java中的start()方法

      public void start() throws IOException {
              ServerSocket serverSocket = new ServerSocket(port);
              System.out.println("========>>Minicat start on port:"+port);
      
              while (true){
                  Socket socket = serverSocket.accept();
                  InputStream inputStream = socket.getInputStream();
                  //封装Resuest对象和Response对象
                  Request request = new Request(inputStream);
                  Response response = new Response(socket.getOutputStream());
                  response.outputHtml(request.getUrl());
                  socket.close();
             }
      }
      
    2. 在项目的resources文件夹新建index.html文件

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Static resource </title>
      </head>
      <body>
      Hello ~ Static resource
      </body>
      </html>
      
    3. 运行main方法

    4. 浏览器输入:http://localhost:8080/index.html

    5. 结果展现:
      image-20201227205755188

    五,开发——3.0版本

    3.0版本就要定义Servlet了,大致分为以下几步:

    1. 定义servlet规范
    2. 编写Servlet
    3. 加载解析Servlet配置

    5.1 定义servlet规范

    public interface Servlet {
        void init() throws Exception;
        void destroy() throws Exception;
        void service(Request request,Response response) throws Exception;
    }
    

    定义一个抽象类,实现Servlet,并且增加两个抽象方法doGet , doPost.

    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 init() throws Exception {
    
        }
    
        @Override
        public void destroy() throws Exception {
    
        }
    
        @Override
        public void service(Request request, Response response) throws Exception {
            if ("GET".equals(request.getMethod())){
                doGet(request, response);
            }else{
                doPost(request, response);
            }
        }
    }
    

    5.2 编写Servlet继承HttpServlet

    新建DxhServlet.java,并继承HttpServlet重写doGet和doPost方法

    package server;
    
    import java.io.IOException;
    
    public class DxhServlet extends HttpServlet{
        @Override
        public void doGet(Request request, Response response) {
            String content="<h1>DxhServlet 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>DxhServlet post</h1>";
            try {
                response.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length)+content);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void init() throws Exception {
            super.init();
        }
    
        @Override
        public void destroy() throws Exception {
            super.destroy();
        }
    }
    
    

    接下来要把DxhServlet配置到一个配置文件中,当MiniCat启动时,加载进去。

    5.3 加载解析Servlet配置

    5.3.1 配置文件

    resources目录下,新建web.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <web-app>
        <servlet>
            <servlet-name>dxh</servlet-name>
            <servlet-class>server.DxhServlet</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>dxh</servlet-name>
            <url-pattern>/dxh</url-pattern>
        </servlet-mapping>
    </web-app>
    

    标准的配置Servlet的标签。servlet-class改成自己写的Servlet全限定类名,url-pattern/dxh,一会请求http://localhost:8080/dxh,来访问这个servlet

    5.3.2 解析配置文件

    复制一份Bootstrap2.java,命名为Bootstrap3.java

    1. 加载解析相关的配置 ,web.xml
      引入dom4j和jaxen的jar包

      <dependency>
      	<groupId>dom4j</groupId>
      	<artifactId>dom4j</artifactId>
      	<version>1.6.1</version>
      </dependency>
      <dependency>
      	<groupId>jaxen</groupId>
      	<artifactId>jaxen</artifactId>
      	<version>1.1.6</version>
      </dependency>
      
    2. Bootstrap3.java中增加一个方法

      //用于下面存储url-pattern以及其对应的servlet-class的实例化对象
      private Map<String,HttpServlet> servletMap = new HashMap<>();
      
      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();
              /**
               * 1, 找到所有的servlet标签,找到servlet-name和servlet-class
               * 2, 根据servlet-name找到<servlet-mapping>中与其匹配的<url-pattern>
               */
              List<Element> selectNodes = rootElement.selectNodes("//servlet");
              for (int i = 0; i < selectNodes.size(); i++) {
                  Element element = selectNodes.get(i);
                  /**
                   * 1, 找到所有的servlet标签,找到servlet-name和servlet-class
                   */
                  //<servlet-name>dxh</servlet-name>
                  Element servletNameElement =(Element)element.selectSingleNode("servlet-name");
                  String servletName = servletNameElement.getStringValue();
                  //<servlet-class>server.DxhServlet</servlet-class>
                  Element servletClassElement =(Element)element.selectSingleNode("servlet-class");
                  String servletClass = servletClassElement.getStringValue();
                  /**
                   * 2, 根据servlet-name找到<servlet-mapping>中与其匹配的<url-pattern>
                   */
                  //Xpath表达式:从/web-app/servlet-mapping下查询,查询出servlet-name=servletName的元素
                  Element servletMapping =(Element)rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']'");
                  // /dxh
                  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();
          }
      }
      

      这段代码的意思就是读取web.xml转换成Document,然后遍历根元素内中的servlet标签(servlet是可以配置多个的),通过XPath表达式获得servlet-nameservlet-class,以及与其对应的<servlet-mapping>标签下的url-pattern,然后存在Map中。注意,这里Map的Key是url-patternValue是servlet-class的实例化对象

    5.4 接收请求,处理请求改造

    image-20201227230820862

    image-20201227231617987

    这里进行了判断,判断servletMap中是否存在url所对应的value,如果没有,当作静态资源访问,如果有,取出并调用service方法,在HttpServlet的service方法中已经做了根据request判断具体调用的是doGet还是doPost方法。

    测试:

    在浏览器中输入:
    http://localhost:8080/index.html,可以访问静态资源
    image-20201227231858786

    输入:http://localhost:8080/dxh
    image-20201227231925246

    可以访问【5.2中编写的Servlet】动态资源~

    到此位置,一个简单的Tomcat Demo已经完成。

    六,优化——多线程改造(不使用线程池)

    6.1 问题分析

    在现有的代码中,接收请求这部分它是一个IO模型——BIO,阻塞IO。

    它存在一个问题,当一个请求还未处理完成时,再次访问,会出现阻塞的情况。

    可以在DxhServletdoGet方法中加入Thread.sleep(10000);然后访问http://localhost:8080/dxhhttp://localhost:8080/index.html做个测试

    那么我们可以使用多线程对其进行改造。
    image-20201227233057004

    把上述代码放到一个新的线程中处理。

    6.2 复制Bootstrap3

    复制Bootstrap3,命名为Bootstrap4。把start()方法中上图的部分(包括socket.close()剪切到下面的线程处理类的run方法中:

    6.3 定义一个线程处理类

    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();
                //封装Resuest对象和Response对象
                Request request = new Request(inputStream);
                Response response = new Response(socket.getOutputStream());
                String url = request.getUrl();
                //静态资源处理
                if (servletMap.get(url)==null){
                    response.outputHtml(request.getUrl());
                }else{
                    //动态资源处理
                    HttpServlet httpServlet = servletMap.get(url);
                    httpServlet.service(request,response);
                }
                socket.close();
            }catch (Exception e){
    
            }
        }
    }
    

    6.4 修改Bootstrap4的start()方法

    public void start() throws Exception {
        //加载解析相关的配置 ,web.xml,把配置的servlet存入servletMap中
        loadServlet();
    
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("========>>Minicat start on port:"+port);
        /**
         * 可以请求动态资源
         */
        while (true){
            Socket socket = serverSocket.accept();
            //使用多线程处理
            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            requestProcessor.start();
        }
    }
    

    再次做6.1章节的测试, OK 没有问题了。

    七,优化——多线程改造(使用线程池)

    这一步,我们使用线程池进行改造。

    复制Bootstrap4,命名为Bootstrap5。

    修改start()方法。线程池的使用不再赘述。代码如下:

    public void start() throws Exception {
        //加载解析相关的配置 ,web.xml,把配置的servlet存入servletMap中
        loadServlet();
    
        /**
         * 定义线程池
         */
        //基本大小
        int corePoolSize = 10;
        //最大
        int maxPoolSize = 50;
        //如果线程空闲的话,超过多久进行销毁
        long keepAliveTime = 100L;
        //上面keepAliveTime的单位
        TimeUnit unit = TimeUnit.SECONDS;
        //请求队列
        BlockingQueue<Runnable> workerQueue = new ArrayBlockingQueue<>(50);
        //线程工厂,使用默认的即可
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //拒绝策略,如果任务太多处理不过来了,如何拒绝
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize
                ,maxPoolSize
                ,keepAliveTime
                ,unit
                ,workerQueue
                ,threadFactory
                ,handler);
    
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("========>>Minicat start on port(多线程):"+port);
        /**
         * 可以请求动态资源
         */
        while (true){
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            threadPoolExecutor.execute(requestProcessor);
        }
    }
    

    OK ,再次测试,成功~

    MINI版Tomcat到此完成。

    八,总结

    总结一下编写一个MINI版本的Tomcat都需要做些什么:

    1. 定义一个入口类,需要监听的端口号和入口方法——main方法
    2. 定义servlet规范(接口),并实现它——HttpServlet
    3. 编写http协议工具类,主要提供响应头信息
    4. 在main方法中调用start()方法用于启动初始化和请求进来时的操作
    5. 加载解析配置文件(web.xml)
    6. 当请求进来时,解析inputStream,并封装为Request和Response对象。
    7. 判断请求资源的方式(动态资源还是静态资源)
  • 相关阅读:
    东北师范大学信息化建设-北大公益论坛演讲稿
    HTML5定稿一周年,你必须要重新认识HTML5了
    从Java的角度理解前端框架,nodejs,reactjs,angularjs,requirejs,seajs
    mui开发webapp(2)
    mui开发webapp(1)
    html5+ plus和phoneGap、cordova的比较
    mui开发
    Web前端技能
    javascript权威指南第六版学习
    鼠标移入的box-shadow参考
  • 原文地址:https://www.cnblogs.com/isdxh/p/14199711.html
Copyright © 2011-2022 走看看