• Tomcat源码分析(二)连接处理 转载


    本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996 

    目标:在这篇文章希望搞明白http请求到tomcat后是怎么由连接器转交到容器的?

          在上一节里已经启动了一个HttpConnector线程,并且也启动了固定数量的HttpProcessor线程。HttpConnector用来等待http连接,得到http连接后交给其中的一个HttpProcessor线程来处理。接下里具体看一下HttpConnector是怎么得到连接得,以及HttpProcessor是怎么处理的。当启动了HttpConnector线程后(在上一节已经知道怎么启动了),便在它的run方法里面循环等待:

    1. public void run() {  
    2.     // Loop until we receive a shutdown command  
    3.     while (!stopped) {  
    4.         // Accept the next incoming connection from the server socket  
    5.         Socket socket = null;  
    6.         try {  
    7.             socket = serverSocket.accept();  
    8.             if (connectionTimeout > 0)  
    9.                 socket.setSoTimeout(connectionTimeout);  
    10.             socket.setTcpNoDelay(tcpNoDelay);  
    11.         } catch (AccessControlException ace) {  
    12.             log("socket accept security exception", ace);  
    13.             continue;  
    14.         } catch (IOException e) {  
    15.             try {  
    16.                 // If reopening fails, exit  
    17.                 synchronized (threadSync) {  
    18.                     if (started && !stopped)  
    19.                         log("accept error: ", e);  
    20.                     if (!stopped) {  
    21.                         serverSocket.close();  
    22.                         serverSocket = open();  
    23.                     }  
    24.                 }  
    25.                   
    26.             } catch (IOException ioe) {  
    27.                 log("socket reopen, io problem: ", ioe);  
    28.                 break;  
    29.             } catch (KeyStoreException kse) {  
    30.                 log("socket reopen, keystore problem: ", kse);  
    31.                 break;  
    32.             } catch (NoSuchAlgorithmException nsae) {  
    33.                 log("socket reopen, keystore algorithm problem: ", nsae);  
    34.                 break;  
    35.             } catch (CertificateException ce) {  
    36.                 log("socket reopen, certificate problem: ", ce);  
    37.                 break;  
    38.             } catch (UnrecoverableKeyException uke) {  
    39.                 log("socket reopen, unrecoverable key: ", uke);  
    40.                 break;  
    41.             } catch (KeyManagementException kme) {  
    42.                 log("socket reopen, key management problem: ", kme);  
    43.                 break;  
    44.             }  
    45.   
    46.             continue;  
    47.         }  
    48.   
    49.         // Hand this socket off to an appropriate processor  
    50.         HttpProcessor processor = createProcessor();  
    51.         if (processor == null) {  
    52.             try {  
    53.                 log(sm.getString("httpConnector.noProcessor"));  
    54.                 socket.close();  
    55.             } catch (IOException e) {  
    56.                 ;  
    57.             }  
    58.             continue;  
    59.         }  
    60.        
    61.         processor.assign(socket);  
    62.   
    63.     }  
    64.   
    65.     // Notify the threadStop() method that we have shut ourselves down  
    66.    
    67.     synchronized (threadSync) {  
    68.         threadSync.notifyAll();  
    69.     }  
    70.   
    71. }  
    这里很关键的就是socket = serverSocket.accept();和processor.assign(socket); 在循环里面内,serverSocket.accept();负责接收http请求然后赋值给socket,最后交给其中一个processor处理。这里processor并不是等到需要的时候再实例化,而是在HttpConnector初始化的时候已经有了若干个processor,在httpConnector里有这样一个声明:

    1. private Stack processors = new Stack();  
    表明httpConnector里面持有一个包含HttpProcessor对象的栈,需要的时候拿出来就是了。看一下createProcessor函数就能比较明白了:

    1. private HttpProcessor createProcessor() {  
    2.      synchronized (processors) {  
    3.          if (processors.size() > 0) {  
    4.              return ((HttpProcessor) processors.pop()); //从processors栈中弹出一个processor  
    5.          }  
    6.          if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {  
    7.              return (newProcessor());  
    8.          } else {  
    9.              if (maxProcessors < 0) {  
    10.                  return (newProcessor());  
    11.              } else {  
    12.                  return (null);  
    13.              }  
    14.          }  
    15.      }  
    16.   
    17.  }  

    接下来由processor.assign(socket); 记住这个方法是异步的,不需要等待HttpProcessor来处理完成,所以HttpConnector才能不间断的传入Http请求,在HttpProcessor里有两个方法比较重要,这两个方法协调处理了由HttpConnector传来的socket:

    1.  synchronized void assign(Socket socket) {  
    2.   
    3.     // Wait for the Processor to get the previous Socket  
    4.     while (available) {  
    5.         try {  
    6.             wait();  
    7.         } catch (InterruptedException e) {  
    8.         }  
    9.     }  
    10.   
    11.     // Store the newly available Socket and notify our thread  
    12.     this.socket = socket;  
    13.     available = true;  
    14.     notifyAll();  
    15.   
    16.     if ((debug >= 1) && (socket != null))  
    17.         log(" An incoming request is being assigned");  
    18.   
    19. }  
    20.   
    21.   
    22. private synchronized Socket await() {  
    23.   
    24.     // Wait for the Connector to provide a new Socket  
    25.     while (!available) {  
    26.         try {  
    27.             wait();  
    28.         } catch (InterruptedException e) {  
    29.         }  
    30.     }  
    31.   
    32.     // Notify the Connector that we have received this Socket  
    33.     Socket socket = this.socket;  
    34.     available = false;  
    35.     notifyAll();  
    36.   
    37.     if ((debug >= 1) && (socket != null))  
    38.         log("  The incoming request has been awaited");  
    39.   
    40.     return (socket);  
    41.   
    42. }  

    看一下HttpProcessor的run方法:

    1. public void run() {  
    2.   
    3.        // Process requests until we receive a shutdown signal  
    4.        while (!stopped) {  
    5.   
    6.            // Wait for the next socket to be assigned  
    7.            Socket socket = await();  
    8.            if (socket == null)  
    9.                continue;  
    10.   
    11.            // Process the request from this socket  
    12.            try {  
    13.                process(socket);  
    14.            } catch (Throwable t) {  
    15.                log("process.invoke", t);  
    16.            }  
    17.   
    18.            // Finish up this request  
    19.            connector.recycle(this);  
    20.   
    21.        }  
    22.   
    23.        // Tell threadStop() we have shut ourselves down successfully  
    24.        synchronized (threadSync) {  
    25.            threadSync.notifyAll();  
    26.        }  
    27.   
    28.    }  

    很明显,在它的run方法一开始便是调用上面的await方法来等待(因为一开始available变量为false),所以HttpProcessor会一直阻塞,直到有线程来唤醒它。当从HttpConnector中调用processor.assign(socket),会把socket传给此HttpProcessor对象,并设置available为true,调用notifyAll()唤醒该processor线程以处理socket。同时,在await方法中又把available设置成false,因此又回到初始状态,即可以重新接受socket。

    这里处理socket的方法是process(socket),主要作用有两点,1:解析这个socket,即解析http请求,包括请求方法,请求协议等,以填充request,response对象(是不是很熟悉,在servlet和jsp开发经常用到的request,response对象就是从这里来的)。2:传入request,response对象给和HttpConnector绑定的容器,让容器来调用invoke方法进行处理。process方法主要的代码如下:

    1. private void process(Socket socket) {  
    2.                    input = new SocketInputStream(socket.getInputStream(),  
    3.                                           connector.getBufferSize());  
    4.                    //解析一下连接的地址,端口什么的  
    5.                     parseConnection(socket);  
    6.                     //解析请求头的第一行,即:方法,协议,uri  
    7.                     parseRequest(input, output);  
    8.                     if (!request.getRequest().getProtocol()  
    9.                         .startsWith("HTTP/0"))  
    10.                     parseHeaders(input);//解析http协议的头部  
    11.                     ..............................................  
    12.                     connector.getContainer().invoke(request, response);  
    13.                     .............................................  
    14. }  
    在那些parse××方法里面会对request,response对象进行初始化,然后调用容器的invoke方法进行处理,至此,http请求过来的连接已经完美的转交给容器处理,容器剩下的问题就是要最终转交给哪个servlet或者jsp的问题。前面我们知道,一个连接会跟一个容器相连,一个级别大的容器会有一个或者多个子容器,最小的容器是Wrapper,对应一个servlet,在这里我们只要知道请求的路径决定了最终会选择哪个wrapper,wrapper最终会调用servlet的。至少一开始提出来的问题已经明白了。这里又有一个问题,在调用invoke方法是有这样的connector.getContainer的代码,即通过连接器得到跟它关联的容器,这个连接器是什么时候跟容器关联上的?详见下篇:Tomcat源码分析(三)--连接器是如何与容器关联的?
  • 相关阅读:
    Haproxy基于ACL做访问控制
    K8s之Prometheus监控
    kubernetes之PV及PVC案例
    K8s常见示例
    K8s之Web服务
    Ansible 部署k8s
    K8s之网络通信
    创建资源对象实例
    kubeadm搭建K8s集群
    Go基础之函数递归实现汉诺塔
  • 原文地址:https://www.cnblogs.com/chenying99/p/2798429.html
Copyright © 2020-2023  润新知