zoukankan      html  css  js  c++  java
  • How tomcat works(深入剖析tomcat)阅读笔记1-4章

    How tomcat works

    chapter 1 简单的web服务器

    这一张的主要内容就是实现一个简单的静态资源服务器,socket编程,利用java提供的socket和serverSocket编程

    整体过程如下:

    HttpServer通过serverSocket监听端口,通过阻塞的accept方法接收请求,然后创建resquest和response对象,

    //服务器启动主方法
    public static void main(String[] args) {
        HttpServer server = new HttpServer();
        server.await();
      }
    
    //await方法,几乎所有的任务都在这里完成
    public void await() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
          serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        }
        catch (IOException e) {
          e.printStackTrace();
          System.exit(1);
        }
    
        // Loop waiting for a request
        //shutdown为一个成员变量,这样的服务器的较为优雅的停止方式为接收一个uri为"/SHUTDOWN_COMMAND"的请求
        while (!shutdown) {
          Socket socket = null;
          InputStream input = null;
          OutputStream output = null;
          try {
            socket = serverSocket.accept();
            input = socket.getInputStream();
            output = socket.getOutputStream();
    
            //创建request对象,把inputstream给他
            // create Request object and parse
            Request request = new Request(input);
            request.parse();
    
            // create Response object
            Response response = new Response(output);
            response.setRequest(request);
            response.sendStaticResource();
    
            // Close the socket
            socket.close();
    
            //check if the previous URI is a shutdown command
            shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
          }
          catch (Exception e) {
            e.printStackTrace();
            continue;
          }
        }
      }
    

    request对象主要就做一件事情,从inputstream中解析uri

    parse()

    从inputstream中读取数据转成字符串,然后调用parseUri方法

    public void parse() {
        // Read a set of characters from the socket
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
          i = input.read(buffer);
        }
        catch (IOException e) {
          e.printStackTrace();
          i = -1;
        }
        for (int j=0; j<i; j++) {
          request.append((char) buffer[j]);
        }
        System.out.print(request.toString());
        uri = parseUri(request.toString());
      }
    

    parseUri(String requestString)

    private String parseUri(String requestString) {
        int index1, index2;
        index1 = requestString.indexOf(' ');
        if (index1 != -1) {
          index2 = requestString.indexOf(' ', index1 + 1);
          if (index2 > index1)
              //切割出uri
            return requestString.substring(index1 + 1, index2);
        }
        return null;
      }
    

    response对象,拿到request和outputstream,按照request中的uri找到对应的资源,然后塞入outputstream中就完事儿

    sendStaticResource()

    public void sendStaticResource() throws IOException {
      byte[] bytes = new byte[BUFFER_SIZE];
      FileInputStream fis = null;
      try {
        File file = new File(HttpServer.WEB_ROOT, request.getUri());
        if (file.exists()) {
          fis = new FileInputStream(file);
          int ch = fis.read(bytes, 0, BUFFER_SIZE);
          while (ch!=-1) {
            output.write(bytes, 0, ch);
            ch = fis.read(bytes, 0, BUFFER_SIZE);
          }
        }
        else {
          // file not found
          String errorMessage = "HTTP/1.1 404 File Not Found
    " +
                  "Content-Type: text/html
    " +
                  "Content-Length: 23
    " +
                  "
    " +
                  "<h1>File Not Found</h1>";
          output.write(errorMessage.getBytes());
        }
      }
      catch (Exception e) {
        // thrown if cannot instantiate a File object
        System.out.println(e.toString() );
      }
      finally {
        if (fis!=null)
          fis.close();
      }
    }
    

    tips:由于这个响应没有任何响应头,所以chrome现在的版本不会接受它,但是firefox可以,但也只是以纯文本形式

    chapter 2 一个简单的servlet容器

    第一章利用了java的socket编程构建了一个只能响应只能响应静态资源的http服务器,这一章实现一个简单的servlet容器,它只能处理简单的servlet和响应静态资源

    熟悉servlet接口

    servlet接口中声明了5个方法

    //初始化方法
    public void init(ServletConfig config)throws ServletException;
    
    //主要的方法,接受一个request对象和response对象,然后进行处理
    public void service(ServletRequest request,ServletResponse response)throws ServletException,Java.io.IOException;
    
    //在将servlet实例从服务中移除时,servlet容器会调用servlet实例的destory()方法,清理一些资源
    public void destory();
    
    public ServletConfig getServletConfig();
    
    public java.lang.String getServletInfo();
    

    一个功能齐全的servlet容器处理流程

    • 第一次调用某servlet时,将它载入,然后执行init()方法
    • 针对每个request请求,创建ServletRequest实例和ServletResponse实例,传递给service()方法
    • 关闭servlet时,调用destroy()方法,并且卸载该类

    本章是一个简单的实现,就不调用init方法和destroy方法,创建好ServletRequest和ServletResponse然后作为参数传递给service方法即可

    tips:在这样的servlet容器中,每次对于该servlet的请求,都会导致载入相应的servlet类,可想而知,效率是非常低的

    主要类以及流程分析

    本章主要包含了以下几个类:

    • HttpServer1
    • Request
    • Response
    • StaticResourceProcessor
    • ServletProcessor1
    • Constants

    该servlet容器的UML如下

    image.png

    处理流程

    入口类,HttpServer1,监听端口8080,构建request,response,根据是请求静态资源还是servlet分别处理

    //服务器主方法
    public static void main(String[] args) {
        HttpServer1 server = new HttpServer1();
        server.await();
      }
    
    //await
    public void await() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
          serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        }
        catch (IOException e) {
          e.printStackTrace();
          System.exit(1);
        }
    
        // Loop waiting for a request
        while (!shutdown) {
          Socket socket = null;
          InputStream input = null;
          OutputStream output = null;
          try {
            socket = serverSocket.accept();
            input = socket.getInputStream();
            output = socket.getOutputStream();
    
            // create Request object and parse
            Request request = new Request(input);
            request.parse();
    
            // create Response object
            Response response = new Response(output);
            response.setRequest(request);
    
            // check if this is a request for a servlet or a static resource
            // a request for a servlet begins with "/servlet/"
            if (request.getUri().startsWith("/servlet/")) {
              ServletProcessor1 processor = new ServletProcessor1();
              processor.process(request, response);
            }
            else {
              StaticResourceProcessor processor = new StaticResourceProcessor();
              processor.process(request, response);
            }
    
            // Close the socket
            socket.close();
            //check if the previous URI is a shutdown command
            shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
          }
          catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
          }
        }
      }
    

    request.parse()

    和第一章一样解析uri

    public void parse() {
        // Read a set of characters from the socket
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
          i = input.read(buffer);
        }
        catch (IOException e) {
          e.printStackTrace();
          i = -1;
        }
        for (int j=0; j<i; j++) {
          request.append((char) buffer[j]);
        }
        System.out.print(request.toString());
        uri = parseUri(request.toString());
      }
    
    //uri = parseUri(request.toString());
    private String parseUri(String requestString) {
        int index1, index2;
        index1 = requestString.indexOf(' ');
        if (index1 != -1) {
          index2 = requestString.indexOf(' ', index1 + 1);
          if (index2 > index1)
            return requestString.substring(index1 + 1, index2);
        }
        return null;
      }
    

    processor.process(request, response);

    请求servlet分支,接受request,response作为参数,然后调用ServletProcessor1的process方法,载入对应的servlet然后,实例化该servlet并且真正地处理请求

    public void process(Request request, Response response) {
    
        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader loader = null;
    
        try {
          // create a URLClassLoader
          URL[] urls = new URL[1];
          URLStreamHandler streamHandler = null;
          File classPath = new File(Constants.WEB_ROOT);
          // the forming of repository is taken from the createClassLoader method in
          // org.apache.catalina.startup.ClassLoaderFactory
          String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
          // the code for forming the URL is taken from the addRepository method in
          // org.apache.catalina.loader.StandardClassLoader class.
          urls[0] = new URL(null, repository, streamHandler);
          loader = new URLClassLoader(urls);
        }
        catch (IOException e) {
          System.out.println(e.toString() );
        }
        Class myClass = null;
        try {
          //载入servlet
          myClass = loader.loadClass(servletName);
        }
        catch (ClassNotFoundException e) {
          System.out.println(e.toString());
        }
    
        Servlet servlet = null;
    
        try {
          //实例化servlet并处理请求
          servlet = (Servlet) myClass.newInstance();
          servlet.service((ServletRequest) request, (ServletResponse) response);
        }
        catch (Exception e) {
          System.out.println(e.toString());
        }
        catch (Throwable e) {
          System.out.println(e.toString());
        }
    
      }
    

    processor.process(request, response);

    发送静态资源分支,这里只是调用了以下response的sendStaticResource方法

    public void process(Request request, Response response) {
        try {
          response.sendStaticResource();
        }
        catch (IOException e) {
          e.printStackTrace();
        }
      }
    

    chapter 3 连接器(Connector)

    建立一个连接器,用来增强第二章的功能,主要就是用一种更好的方式创建request和response对象

    本章应用包含三个模块:连接器模块、启动模块和核心模块

    启动模块只包含了一个类:BootStrap

    连接器模块可分为以下5个类型:

    • 连接器及其支持类(HttpConnector和HttpProcessor)
    • 表示Http请求的类(HttpRequest)及其支持类
    • 表示Http响应的类(HttpResponse)及其支持类
    • 外观类(HttpRequestFacade和HttpResponseFacade)
    • 常量类(Constants)

    核心模块包含两个类,servletProcessor和StaticResourceProcessor,具体UML如下

    image.png

    主要类以及流程分析

    启动类BootStrap,new一个connector然后调用connector.start(),不难猜到connector的start另启了一个线程

    public static void main(String[] args) {
        HttpConnector connector = new HttpConnector();
        connector.start();
      }
    

    HttpConnector

    连接器模块主要类,负责监听端口,接收请求,然后将自己传递给HttpProcessor,并且将socket作为参数,传递给HttpProcessor的process方法

    run()

    public void run() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
          serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        }
        catch (IOException e) {
          e.printStackTrace();
          System.exit(1);
        }
        while (!stopped) {
          // Accept the next incoming connection from the server socket
          Socket socket = null;
          try {
            socket = serverSocket.accept();
          }
          catch (Exception e) {
            continue;
          }
          // Hand this socket off to an HttpProcessor
          HttpProcessor processor = new HttpProcessor(this);
          processor.process(socket);
        }
      }
    
    public void start() {
        Thread thread = new Thread(this);
        thread.start();
      }
    

    HttpProcessor

    负责生成resquest以及response,然后交给ServletProcessor或者StaticResourceProcessor处理

    public void process(Socket socket)

    public void process(Socket socket) {
        //SocketInputStream是InputStream的包装类,主要提供了readRequestLine方法和readHeader方法
        SocketInputStream input = null;
        OutputStream output = null;
        try {
          //2048为指定的缓冲数组大小
          input = new SocketInputStream(socket.getInputStream(), 2048);
          output = socket.getOutputStream();
    
          // create HttpRequest object and parse
          request = new HttpRequest(input);
    
          // create HttpResponse object
          response = new HttpResponse(output);
          response.setRequest(request);
    
          response.setHeader("Server", "Pyrmont Servlet Container");
    
          //解析请求行
          parseRequest(input, output);
          //解析首部行
          parseHeaders(input);
    	
          //然后就可以根据请求的uri决定交给什么processor了
          //这两者就和第二章的一样了
          //check if this is a request for a servlet or a static resource
          //a request for a servlet begins with "/servlet/"
          if (request.getRequestURI().startsWith("/servlet/")) {
            ServletProcessor processor = new ServletProcessor();
            processor.process(request, response);
          }
          else {
            StaticResourceProcessor processor = new StaticResourceProcessor();
            processor.process(request, response);
          }
    
          // Close the socket
          socket.close();
          // no shutdown for this application
        }
        catch (Exception e) {
          e.printStackTrace();
        }
      }
    
    解析请求行parseRequest

    private void parseRequest(SocketInputStream input, OutputStream output)

    private void parseRequest(SocketInputStream input, OutputStream output)
        throws IOException, ServletException {
    
        // Parse the incoming request line
        //HttpRequestLine为请求行对象,利用readRequestLine方法填充HttpProcessor的HttpRequestLine成员变量
        input.readRequestLine(requestLine);
        //经过上面的处理,这里就能拿到方法名了
        String method =
          new String(requestLine.method, 0, requestLine.methodEnd);
        //uri暂时还拿不到,因为可能带参,此时的uri字符数组保存的是带参的uri
        String uri = null;
        String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);
    
        // Validate the incoming request line
        if (method.length() < 1) {
          throw new ServletException("Missing HTTP request method");
        }
        else if (requestLine.uriEnd < 1) {
          throw new ServletException("Missing HTTP request URI");
        }
        // Parse any query parameters out of the request URI
        //处理带参uri
        int question = requestLine.indexOf("?");
        if (question >= 0) {
            //包请求参数塞进queryString,回头需要的时候解析
          request.setQueryString(new String(requestLine.uri, question + 1,
            requestLine.uriEnd - question - 1));
          uri = new String(requestLine.uri, 0, question);
        }
        else {
          request.setQueryString(null);
          uri = new String(requestLine.uri, 0, requestLine.uriEnd);
        }
    
    	//检查,可略过
        // Checking for an absolute URI (with the HTTP protocol)
        if (!uri.startsWith("/")) {
          int pos = uri.indexOf("://");
          // Parsing out protocol and host name
          if (pos != -1) {
            pos = uri.indexOf('/', pos + 3);
            if (pos == -1) {
              uri = "";
            }
            else {
              uri = uri.substring(pos);
            }
          }
        }
    	
        //处理jessionId
        // Parse any requested session ID out of the request URI
        String match = ";jsessionid=";
        int semicolon = uri.indexOf(match);
        if (semicolon >= 0) {
          String rest = uri.substring(semicolon + match.length());
          int semicolon2 = rest.indexOf(';');
          if (semicolon2 >= 0) {
            request.setRequestedSessionId(rest.substring(0, semicolon2));
            rest = rest.substring(semicolon2);
          }
          else {
            request.setRequestedSessionId(rest);
            rest = "";
          }
          request.setRequestedSessionURL(true);
          uri = uri.substring(0, semicolon) + rest;
        }
        else {
          request.setRequestedSessionId(null);
          request.setRequestedSessionURL(false);
        }
    
        // Normalize URI (using String operations at the moment)
        String normalizedUri = normalize(uri);
    
        // Set the corresponding request properties
        //把请求行的属性都塞进去,至此请求行的数据解析完毕
        ((HttpRequest) request).setMethod(method);
        request.setProtocol(protocol);
        if (normalizedUri != null) {
          ((HttpRequest) request).setRequestURI(normalizedUri);
        }
        else {
          ((HttpRequest) request).setRequestURI(uri);
        }
    
        if (normalizedUri == null) {
          throw new ServletException("Invalid URI: " + uri + "'");
        }
      }
    

    HttpRequestLine

    HttpRequestLine为请求行对象,用字符数组来存储请求行的数据,代码一瞥

    public HttpRequestLine(char[] method, int methodEnd,
                               char[] uri, int uriEnd,
                               char[] protocol, int protocolEnd) {
    
            this.method = method;
            this.methodEnd = methodEnd;
            this.uri = uri;
            this.uriEnd = uriEnd;
            this.protocol = protocol;
            this.protocolEnd = protocolEnd;
    
    }
    
        public char[] method;
        public int methodEnd;
        public char[] uri;
        public int uriEnd;
        public char[] protocol;
        public int protocolEnd;
    

    SocketInputStream的readRequestLine方法

    public void readRequestLine(HttpRequestLine requestLine)

    public void readRequestLine(HttpRequestLine requestLine)
            throws IOException {
    
            // Recycling check
            if (requestLine.methodEnd != 0)
                requestLine.recycle();
    
            // Checking for a blank line
        	//跳过空行
            int chr = 0;
            do { // Skipping CR or LF
                try {
                    //调用read方法,
                    chr = read();
                } catch (IOException e) {
                    chr = -1;
                }
            } while ((chr == CR) || (chr == LF));
            if (chr == -1)
                throw new EOFException
                    (sm.getString("requestStream.readline.error"));
        	//由于read方法中将pos指向了下一个字节了,需要回退一步
            pos--;
    
            // Reading the method name
    
            int maxRead = requestLine.method.length;
            int readStart = pos;
            int readCount = 0;
    
            boolean space = false;
    
            while (!space) {
                //超过method字符数组的最大长度了
                // if the buffer is full, extend it
                if (readCount >= maxRead) {
                    if ((2 * maxRead) <= HttpRequestLine.MAX_METHOD_SIZE) {
                        char[] newBuffer = new char[2 * maxRead];
                        System.arraycopy(requestLine.method, 0, newBuffer, 0,
                                         maxRead);
                        requestLine.method = newBuffer;
                        maxRead = requestLine.method.length;
                    } else {
                        throw new IOException
                            (sm.getString("requestStream.readline.toolong"));
                    }
                }
                // We're at the end of the internal buffer
                //如果已经读取到inputstream读取出来的buffer的最后一个位置了
                if (pos >= count) {
                    int val = read();
                    if (val == -1) {
                        throw new IOException
                            (sm.getString("requestStream.readline.error"));
                    }
                    pos = 0;
                    readStart = 0;
                }
                //正常都走到这一步
                if (buf[pos] == SP) {
                    space = true;
                }
                //这里method字符数组是以空格节未的,并没有在buf[pos]=SP的时候break
                requestLine.method[readCount] = (char) buf[pos];
                readCount++;
                pos++;
            }
    		//以空格结尾,readCount去掉空格
            requestLine.methodEnd = readCount - 1;
    		//下面就是解析uri和protocol了
    
        }
    

    SocketInputStream的read、fill方法

    public int read()
            throws IOException {
       		//起始值pos为0,count也为0
            if (pos >= count) {
                //修改count为本次读入的字节长度
                fill();
                if (pos >= count)
                    return -1;
            }
        	//返回buf[pos]的那个字节
            return buf[pos++] & 0xff;
        }
    
    protected void fill()
            throws IOException {
            pos = 0;
            count = 0;
        	//
            int nRead = is.read(buf, 0, buf.length);
            if (nRead > 0) {
                count = nRead;
            }
        }
    
    解析首部行parseHeaders

    private void parseHeaders(SocketInputStream input)

    private void parseHeaders(SocketInputStream input)
        throws IOException, ServletException {
        //循环解析每个请求头
        while (true) {
          HttpHeader header = new HttpHeader();;
    
          // Read the next header
          //socketInputStream提供的readHeader方法,上面已经详细解读过readRequestLine方法了,这里不详细解读,填充header
          //HttpHeader和HttpRequestLine结构也差不多
          input.readHeader(header);
          if (header.nameEnd == 0) {
            if (header.valueEnd == 0) {
              return;
            }
            else {
              throw new ServletException
                (sm.getString("httpProcessor.parseHeaders.colon"));
            }
          }
    
          String name = new String(header.name, 0, header.nameEnd);
          String value = new String(header.value, 0, header.valueEnd);
          request.addHeader(name, value);
          //对于cookie和content-length两个header需要特殊处理一下
          // do something for some headers, ignore others.
          if (name.equals("cookie")) {
            Cookie cookies[] = RequestUtil.parseCookieHeader(value);
            for (int i = 0; i < cookies.length; i++) {
              if (cookies[i].getName().equals("jsessionid")) {
                // Override anything requested in the URL
                if (!request.isRequestedSessionIdFromCookie()) {
                  // Accept only the first session id cookie
                  request.setRequestedSessionId(cookies[i].getValue());
                  request.setRequestedSessionCookie(true);
                  request.setRequestedSessionURL(false);
                }
              }
              request.addCookie(cookies[i]);
            }
          }
          else if (name.equals("content-length")) {
            int n = -1;
            try {
              n = Integer.parseInt(value);
            }
            catch (Exception e) {
              throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength"));
            }
            request.setContentLength(n);
          }
          else if (name.equals("content-type")) {
            request.setContentType(value);
          }
        } //end while
      }
    
    懒加载的parameter

    请求参数在被调用之前是不会解析的,在request的getParameter、getParameterMap、getParameterNames等方法被调用时,首先会调用parseParameter方法

    需要注意的一点是,ParameterMap是不支持修改的,它被锁住了,很简单,用一个lock变量,修改的直接return

    protected void parseParameters()

    protected void parseParameters() {
        if (parsed)
          return;
        ParameterMap results = parameters;
        if (results == null)
          results = new ParameterMap();
        //先开锁
        results.setLocked(false);
        String encoding = getCharacterEncoding();
        if (encoding == null)
          encoding = "ISO-8859-1";
    
        //parse请求头中的参数
        // Parse any parameters specified in the query string
        String queryString = getQueryString();
        try {
          RequestUtil.parseParameters(results, queryString, encoding);
        }
        catch (UnsupportedEncodingException e) {
          ;
        }
    
        //parse请求体中的参数
        // Parse any parameters specified in the input stream
        String contentType = getContentType();
        if (contentType == null)
          contentType = "";
        int semicolon = contentType.indexOf(';');
        if (semicolon >= 0) {
          contentType = contentType.substring(0, semicolon).trim();
        }
        else {
          contentType = contentType.trim();
        }
        if ("POST".equals(getMethod()) && (getContentLength() > 0)
          && "application/x-www-form-urlencoded".equals(contentType)) {
          try {
            int max = getContentLength();
            int len = 0;
            byte buf[] = new byte[getContentLength()];
            ServletInputStream is = getInputStream();
            while (len < max) {
              int next = is.read(buf, len, max - len);
              if (next < 0 ) {
                break;
              }
              len += next;
            }
            is.close();
            if (len < max) {
              throw new RuntimeException("Content length mismatch");
            }
            RequestUtil.parseParameters(results, buf, encoding);
          }
          catch (UnsupportedEncodingException ue) {
            ;
          }
          catch (IOException e) {
            throw new RuntimeException("Content read fail");
          }
        }
    
        // Store the final results
        //再把parameterMap锁上,并且将parsed置为true避免重复parse
        results.setLocked(true);
        parsed = true;
        parameters = results;
      }
    

    chapter 4 Tomcat默认的连接器

    总体介绍

    第三章实现了简单的连接器,第四章来看看Tomcat默认的连接器,虽然该连接器已经被弃用,换成了另一个运行速率更快的连接器——Coyote——替代,但是还是值得一看的,相较于第三章简单的连接器,Tomcat4的默认连接器有以下优化:

    • 更为全面地支持Http协议,包括从0.9到1.1,也支持1.1的新特性
    • 每一个connector不再只绑定一个HttpProcessor,将会利用多线程支持多Http连接
    • 大量利用字符数组,减少了代价较高的字符串操作
    • 利用了池的技术,缓存了HttpProcessor对象,避免每次来都需要创建,节省了对象创建的开销

    但仍然还存在着上来就直接parse所有的Header不管有没有使用,效率比较低的问题

    它的主要工作原理和第三章的连接器基本类似,细微之处不同稍后源代码讨论

    默认的连接器UML图如下:

    image.png

    首先需要注意的一点是,于第三章不同,HttpConnector来负责Request、Response的创建了,而HttpProcessor专注于parse信息,也就是填充request和response

    Http1.1的新特性

    持久连接

    减少了频繁创建TCP连接带来的系统消耗,也减少了三次握手的次数,提高了速度,在Http1.1中,默认采用持久连接,也可以显式使用,在request首部行加上

    Connection: Keep-alive
    
    块编码

    有了持久连接之后,一个TCP连接可能被共享,那么多个对象的数据可能混在一起,那么如何把它们区分开来呢?利用transfer-encoding的特殊请求头,它用来指明字节流会分块发送,对每一个块,块的长度后面会有一个CRLF,然后才是数据,一个事务以一个长度为0的块标记,“0 ”表示事务已经结束

    //e.g利用两个块发送 I'am as helpess as a kitten up a tree.
    
    //发送内容如下
    1D
    
    I'am as helpess as a kitten u
    9
    
    p a tree.
    0
    
    
    状态码100

    当浏览器想发送一个比较大的请求体的时候,不知道服务器能不能接收时,可以先发送一个带有如下请求头的请求

    Except: 100-continue
    

    期待服务的回应为100-continue,收到这个回应说明可以接收,这个时候浏览器才会真正地发出这个比较大的请求,避免浪费带宽、系统资源

    如果服务器收到带有上述请求头的请求,并且支持的话,就发送如下的回复

    HTTP/1.1 100 continue
    

    这样浏览器就会发送真正的请求了

    主要类以及流程分析

    创建ServerSocket

    首先调用initialize方法

    void initialize()

    public void initialize()
        throws LifecycleException {
            if (initialized)
                throw new LifecycleException (
                    sm.getString("httpConnector.alreadyInitialized"));
            this.initialized=true;
            Exception eRethrow = null;
            // Establish a server socket on the specified port
            try {
                //调用open方法
                serverSocket = open();
            } catch (IOException ioe) {
             	...   
            }
            if ( eRethrow != null )
                throw new LifecycleException(threadName + ".open", eRethrow);
    }
    

    ServerSocket open()

    private ServerSocket open()
        throws IOException, KeyStoreException, NoSuchAlgorithmException,
               CertificateException, UnrecoverableKeyException,
               KeyManagementException
        {
    
            // Acquire the server socket factory for this Connector
            ServerSocketFactory factory = getFactory();
    		//根据是否有限定的IP地址选择不同的工厂方法
            // If no address is specified, open a connection on all addresses
            if (address == null) {
                log(sm.getString("httpConnector.allAddresses"));
                try {
                    //不限定端口地址,也就是说监听任何IP的请求
                    return (factory.createSocket(port, acceptCount));
                } catch (BindException be) {
                    throw new BindException(be.getMessage() + ":" + port);
                }
            }
    
            // Open a server socket on the specified address
            try {
                InetAddress is = InetAddress.getByName(address);
                log(sm.getString("httpConnector.anAddress", address));
                try {
                    return (factory.createSocket(port, acceptCount, is));
                } catch (BindException be) {
                    throw new BindException(be.getMessage() + ":" + address +
                                            ":" + port);
                }
            } catch (Exception e) {
                log(sm.getString("httpConnector.noAddress", address));
                try {
                    return (factory.createSocket(port, acceptCount));
                } catch (BindException be) {
                    throw new BindException(be.getMessage() + ":" + port);
                }
            }
    
        }
    

    factory.createSocket(port, acceptCount)

    //ServerSocketFactory中定义,在DefalutServerSocketFactory中实现
    public ServerSocket createSocket (int port, int backlog)
        throws IOException, KeyStoreException, NoSuchAlgorithmException,
               CertificateException, UnrecoverableKeyException,
               KeyManagementException {
    
            return (new ServerSocket(port, backlog));
    
        }
    

    至此创建了ServerSocket并与HttpConnector的serverSocket成员变量绑定

    监听请求

    start()

    public void start() throws LifecycleException {
    
            // Validate and update our current state
            if (started)
                throw new LifecycleException
                    (sm.getString("httpConnector.alreadyStarted"));
            threadName = "HttpConnector[" + port + "]";
            lifecycle.fireLifecycleEvent(START_EVENT, null);
            started = true;
    
            // Start our background thread
        	//new了一个thread 并且调用了thread的start方法
            threadStart();
    
            // Create the specified minimum number of processors
            while (curProcessors < minProcessors) {
                if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                    break;
                HttpProcessor processor = newProcessor();
                recycle(processor);
            }
    
        }
    

    void run()

    start之后进入run方法

    public void run() {
    
            // Loop until we receive a shutdown command
        	//stopped成员变量
            while (!stopped) {
    
                // Accept the next incoming connection from the server socket
                Socket socket = null;
                try {
                    //接收请求
                    socket = serverSocket.accept();
                    //设置超时时长
                    if (connectionTimeout > 0)
                        socket.setSoTimeout(connectionTimeout);
                } catch (AccessControlException ace) {
                    log("socket accept security exception: " + ace.getMessage());
                    continue;
                } catch (IOException e) {
                    if (started && !stopped)
                        log("accept: ", e);
                    break;
                }
    
                // Hand this socket off to an appropriate processor
                //从成员变量processors,这个processor池中拿一个HttpProcessor出来
                //如果池中没有,则创建一个
                HttpProcessor processor = createProcessor();
                if (processor == null) {
                    try {
                        log(sm.getString("httpConnector.noProcessor"));
                        //如果不允许创建了,则直接把socket close掉,也就是直接抛弃这个请求
                        socket.close();
                    } catch (IOException e) {
                        ;
                    }
                    continue;
                }
                //把socket交给processor
                processor.assign(socket);
    
                // The processor will recycle itself when it finishes
    
            }
    
            // Notify the threadStop() method that we have shut ourselves down
            synchronized (threadSync) {
                threadSync.notifyAll();
            }
    
        }
    

    HttpProcessor createProcessor()

    private HttpProcessor createProcessor() {
    
            synchronized (processors) {
                if (processors.size() > 0)
                    return ((HttpProcessor) processors.pop());
                if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
                    //stack中没有空闲的的processor了,并且当前Processor数量还没超过允许的最大Processor数量,则创建一个新的并返回
                    return (newProcessor());
                } else {
                    //如果maxProcessors<0 说明processor数量不受限制,可以无限制创建
                    if (maxProcessors < 0) {
                        return (newProcessor());
                    } else {
                        //说明curProcessors > maxProcessors 没有空闲的processor了,也不允许创建
                        return (null);
                    }
                }
            }
    
        }
    

    HttpProcessor newProcessor()

    private HttpProcessor newProcessor() {
    		//创建processor并且绑定connector
            HttpProcessor processor = new HttpProcessor(this, curProcessors++);
            if (processor instanceof Lifecycle) {
                try {
                    //注意!!! 这里启动了HttpProcessor线程
                    ((Lifecycle) processor).start();
                } catch (LifecycleException e) {
                    log("newProcessor", e);
                    return (null);
                }
            }
            created.addElement(processor);
            return (processor);
    
        }
    

    processor.assign(socket)

    connector把socket交付给HttpProcessor就结束了,它并不像之前那样还需要等待HttpProcessor的执行结果,它可以去继续监听其他请求了,它这里就只负责这里就负责唤醒了一下在run方法中由于没有socket阻塞的processor

    synchronized void assign(Socket socket) {
    
            // Wait for the Processor to get the previous Socket
        	//available默认值为false,代表是否有新的socket过来
            while (available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
    
            // Store the newly available Socket and notify our thread
            this.socket = socket;
            available = true;
        	//notifyAll唤醒了谁呢?唤醒了HttpProcessor的主线程
        	//它在newProcessor方法中启动,然后在await方法中等待
            notifyAll();
    
            if ((debug >= 1) && (socket != null))
                log(" An incoming request is being assigned");
    
        }
    
    处理请求

    HttpProcessor

    run()

    public void run() {
    
            // Process requests until we receive a shutdown signal
        	//stopped成员变量
            while (!stopped) {
    
                // Wait for the next socket to be assigned
                Socket socket = await();
                if (socket == null)
                    continue;
    
                // Process the request from this socket
                try {
                    process(socket);
                } catch (Throwable t) {
                    log("process.invoke", t);
                }
    
                // Finish up this request
                //把processor重新又压回connector的栈内重复利用
                connector.recycle(this);
    
            }
    
            // Tell threadStop() we have shut ourselves down successfully
            synchronized (threadSync) {
                threadSync.notifyAll();
            }
    
        }
    

    await()

    private synchronized Socket await() {
    
            // Wait for the Connector to provide a new Socket
            while (!available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
    		//被唤醒之后availabe被修改成了true了,将socket赋值给局部变量,并且将availabe修改为false
            // Notify the Connector that we have received this Socket
            Socket socket = this.socket;
            available = false;
            notifyAll();
    
            if ((debug >= 1) && (socket != null))
                log("  The incoming request has been awaited");
    
            return (socket);
    
        }
    

    image.png

    这里引用一下原文的解释

    为什么这里需要返回一个局部变量?

    这样做的话,就可以在当前socket被处理完成之前,接收下一个socket???黑人问号,这个process方法不是单线程的嘛?它不是必须完成解析才能被回收嘛?怎么同时接收两个socket的

    为什么还需要notifyAll呢?

    防止connector在执行到processor.assign()方法阻塞,此时available为true,connector一直阻塞,知道processor唤醒它

    recycle(HttpProcessor processor)

    void recycle(HttpProcessor processor) {
    
            //        if (debug >= 2)
            //            log("recycle: Recycling processor " + processor);
            processors.push(processor);
    
        }
    

    处理请求的主方法

    **private void process(Socket socket) **

    private void process(Socket socket) {
            boolean ok = true;
            boolean finishResponse = true;
            SocketInputStream input = null;
            OutputStream output = null;
    
            // Construct and initialize the objects we will need
            try {
                input = new SocketInputStream(socket.getInputStream(),
                                              connector.getBufferSize());
            } catch (Exception e) {
                log("process.create", e);
                ok = false;
            }
    
            keepAlive = true;
    		//原来宣扬了半天的持久连接新特性就是个keepAlive变量
            while (!stopped && ok && keepAlive) {
    
                finishResponse = true;
    
                try {
                    request.setStream(input);
                    request.setResponse(response);
                    output = socket.getOutputStream();
                    response.setStream(output);
                    response.setRequest(request);
                    ((HttpServletResponse) response.getResponse()).setHeader
                        ("Server", SERVER_INFO);
                } catch (Exception e) {
                    log("process.create", e);
                    ok = false;
                }
    
    
                // Parse the incoming request
                try {
                    if (ok) {
    					//如同第三章那样,这里负责处理填充Request,如果想追究如何处理Http1.1的新特性,可以查看,这里不细说
                        parseConnection(socket);
                        parseRequest(input, output);
                        if (!request.getRequest().getProtocol()
                            .startsWith("HTTP/0"))
                            parseHeaders(input);
                        if (http11) {
                            // Sending a request acknowledge back to the client if
                            // requested.
                            ackRequest(output);
                            // If the protocol is HTTP/1.1, chunking is allowed.
                            if (connector.isChunkingAllowed())
                                response.setAllowChunking(true);
                        }
    
                    }
                } catch (EOFException e) {
                    ...
                } 
    
                // Ask our Container to process this request
                try {
                    ((HttpServletResponse) response).setHeader
                        ("Date", FastHttpDateFormat.getCurrentDate());
                    if (ok) {
                        //从processor绑定的connector中获取contianer然后把parse好的request和response传递给他,处理请求
                        connector.getContainer().invoke(request, response);
                    }
                } catch (ServletException e) {
                    ...
                } 
    
                // Finish up the handling of the request
                if (finishResponse) {
                    try {
                        //
                        response.finishResponse();
                    } catch (IOException e) {
                        ok = false;
                    } catch (Throwable e) {
                        log("process.invoke", e);
                        ok = false;
                    }
                    try {
                        request.finishRequest();
                    } catch (IOException e) {
                        ok = false;
                    } catch (Throwable e) {
                        log("process.invoke", e);
                        ok = false;
                    }
                    try {
                        if (output != null)
                            output.flush();
                    } catch (IOException e) {
                        ok = false;
                    }
                }
    
                // We have to check if the connection closure has been requested
                // by the application or the response stream (in case of HTTP/1.0
                // and keep-alive).
                if ( "close".equals(response.getHeader("Connection")) ) {
                    keepAlive = false;
                }
    
                // End of request processing
                status = Constants.PROCESSOR_IDLE;
    
                // Recycling the request and the response objects
                //request和response对象也是需要回收的,在newProcessor的时候调用HttpConnector的createxxx方法创建作为processor的成员变量
                request.recycle();
                response.recycle();
    
            }
    
            try {
                shutdownInput(input);
                socket.close();
            } catch (IOException e) {
                ;
            } catch (Throwable e) {
                log("process.invoke", e);
            }
            socket = null;
    
    
    
        }
    
    处理请求(Container)

    核心方法invoke,也就是之前的servletProcessor做的事情,通过类加载器找到对应的servlet然后让他处理

    invoke方法

    public void invoke(Request request, Response response)
        throws IOException, ServletException {
    
        String servletName = ( (HttpServletRequest) request).getRequestURI();
        servletName = servletName.substring(servletName.lastIndexOf("/") + 1);
        URLClassLoader loader = null;
        try {
          URL[] urls = new URL[1];
          URLStreamHandler streamHandler = null;
          File classPath = new File(WEB_ROOT);
          String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
          urls[0] = new URL(null, repository, streamHandler);
          loader = new URLClassLoader(urls);
        }
        catch (IOException e) {
          System.out.println(e.toString() );
        }
        Class myClass = null;
        try {
          myClass = loader.loadClass(servletName);
        }
        catch (ClassNotFoundException e) {
          System.out.println(e.toString());
        }
    
        Servlet servlet = null;
    
        try {
          servlet = (Servlet) myClass.newInstance();
          servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
        }
        catch (Exception e) {
          System.out.println(e.toString());
        }
        catch (Throwable e) {
          System.out.println(e.toString());
        }
    
    
    
      }
    
  • 相关阅读:
    JavaSE之流程控制结构
    JavaSE的基本语法
    排序算法
    Java实现归并排序
    Java中线程池的介绍
    Java锁的升降级及synchoronized优化
    ThreadLocal应用与原理
    spring bean 实例化bean前后进行相应的设置
    maven依赖配置和依赖范围
    springmvc校验
  • 原文地址:https://www.cnblogs.com/danzZ/p/13964935.html
Copyright © 2011-2022 走看看