This paper introduces the default BIO mode based on tomcat7
As mentioned before, tomcat is transmitted through socket Interface request path
send data
Most HTTP requests are usually long connections, which can be seen from the HTTP request header
Generally, a socket connection will be established before sending an HTTP request. If tomcat judges that the socket request needs to be closed, it will write Connection:close in the response header of the HTTP request. When HTTP gets such a response, it will close the corresponding connection. If tomcat judges that the connection can continue to be used and maintains a long connection, the corresponding content will not be set in the response header, and connection: keep alive will continue to be used.
Note that if it is closed, http initiates the closure, not tomcat
Long connection means that the socket connection is not closed after the request is completed, and you can continue to send requests through the corresponding socket connection.
A socket connection needs to be established before sending an http request. A long connection means that the socket connection will not be closed after the request is completed, and you can continue to send requests through this socket connection. If the tomcat socket request needs to be closed, the Connection:close in the http response header. In this way, the socke connection will be closed after receiving the response. If the long connection is maintained, the http response header will not be set.
receive data
- Tomcat processes requests through the processor (the common interface for all protocol processors).
- The Processor will set an Endpoint internally and receive socket connections through the Endpoint's internal class Acceptor.
- When the client sends data, there is a recvbuff (buffer) in the operating system of the server. The operating system will first put the data in recvbuffbyte. If recvbuf is full, it will not be able to send data. Each socket corresponds to a buffer. When the client writes the sending data of the socket, it will also be placed in the buffer sendbuf.
Refer to the following source code:
- processSocketWapper method in AbstractHttp11Processor class
- There is an Acceptor method in the jieendpoint class, which will accept socket s through BIO
- Socket=serverSocketFactory.accept
- setSocketOption there is a soTimeout parameter in the method of setting socket parameters. This parameter means that when socket reads data from recvbuf, if the data in recvbuf is empty, the read will block. The specific blocking time is the time specified by this parameter.
- For the first time, the data obtained from the socket is written into the InputBuffer, and then the corresponding Request line (Request header), Request method, Request protocol, etc. are parsed into the Request, and some parameters are set.
Processing socket
- In the BIO mode, every time a socket is received, it is handed over to a thread. In the BIO of tomcat, each request has a corresponding thread for processing. When the socket is closed, the thread will also be released.
- NIO is a thread that processes multiple requests.
Refer to the following source code:
- Getexecutor. Of the prcocessSocket method in the JIoEndpoint class execute
- In the BIO mode, the maximum number of requests maxConnection and the maximum number of threads MaxThread are actually the same
- When calling thread processing, find the process method in AbstarctProtocol according to the content in the process call of the run method. The method will find that it is necessary to call createProcessor to create a process first. Different attributes are set according to different implementation classes, and this attribute can also be set through labels. The maxKeepAliveRequests property indicates the maximum number of activities (http requests) that can be processed on this long connection. The default is 100
- After the creation of the processor, the corresponding process method is invoked to parse the data in the http request.
- If maxKeepAliveRequests==1, the connection will be closed after processing, which means that the corresponding setting keepalive=false
- Corresponding judgment: for example, if the current number of active threads accounts for more than 75% of the maximum number of threads in the thread pool, then KeepAlive is closed and long connections are no longer supported. Failure to support long connections does not mean that connection processing is not supported, which means that socket connections after 75% will be treated as short links. However, when the number of socket connections exceeds * * 100% * * the subsequent socket connections will not be processed.
- getInputBuffer().parseRequest and getinputbuffer() Parseheaders() reads the request body and request header
- Call adapter for service processing, and call a series of methods such as fill.
The fill method reads the revfbuf in the operating system into the buffer buf of tomcat (8kb by default)
- During processing, the corresponding content will be put into the response and returned to the client. Similarly, it is first placed in the buffer, and finally the data in the buffer is returned.
- Finally, judge whether the data response is completed, clear the remaining data, and obtain the next request.
public SocketState process(SocketWrapper<S> socketWrapper) throws IOException { RequestInfo rp = request.getRequestProcessor(); // Set request status to resolution status rp.setStage(org.apache.coyote.Constants.STAGE_PARSE); // Set IO setSocketWrapper(socketWrapper); //Set input and output buffers //Bind the socket's InputStream to the InternalInputBuffer (buffer content) getInputBuffer().init(socketWrapper, endpoint); // Bind the socket's OutputStream to the InternalOutputBuffer getOutputBuffer().init(socketWrapper, endpoint); // A series of signs such as long connection keepAlive = true; // NioEndpoint returns true and bio returns false if (endpoint.getUsePolling()) { keptAlive = false; } else { keptAlive = socketWrapper.isKeptAlive(); } // If the current number of active threads accounts for more than 75% of the maximum number of threads in the thread pool (the default value), KeepAlive will be closed and long connections will no longer be supported if (disableKeepAlive()) { socketWrapper.setKeepAliveLeft(0); } // The value of keepAlive will be read from the request, and the default value is true while () { // If keepAlive is true, you need to keep getting http requests from the socket // Parse request header try { // Read data from the socket for the first time, and set the timeout for reading data from the socket // For BIO, a socket connection is not necessarily processed by Tomcat immediately after it is established, and the thread pool scheduling is required, so the waiting time should be included in the time when the socket reads data; For NIO, there is no blocking //Used for the first connection. Timeout = real time - socket establishment time setRequestLineReadTimeout(); // Parse request line if (!getInputBuffer().parseRequestLine(keptAlive)) { } if (endpoint.isPaused()) { //If the Endpoint is paused, 503 is returned } else { keptAlive = true; // Retrieve the maximum limit of request headers and cookies each time a request is processed request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());//Default 100 request.getCookies().setLimit(getMaxCookieCount());//Default 200} } //If the maximum number of keep connected requests = = 1, it means that only one request is allowed if (maxKeepAliveRequests == 1) { keepAlive = false;//The long connection parameter is directly set to false } else if (maxKeepAliveRequests > 0 && socketWrapper.decrementKeepAlive() <= 0) { // If the maximum limit of keepAlive is reached and set to false, Http requests will not be obtained from the socket keepAlive = false; } //Start processing request at adapter if (!getErrorState().isError()) { try { rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE); // Set the request status to service status, indicating that the request is being processed adapter.service(request, response); // Processing requests } } // Complete request processing rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT); // Set the status of the request to end processing the request if (!isAsync() && !comet) { // At present, the http request has been processed. Do some finishing work endRequest(); } rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT); // The request status is output end if (!isAsync() && !comet || getErrorState().isError()) { if (getErrorState().isIoAllowed()) { // Ready to process the next request getInputBuffer().nextRequest(); getOutputBuffer().nextRequest(); } } rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE); // If you find that there is no next request in the socket after processing the current Http request, you will exit the current loop // If it is keepalive, the socket will not be closed. If it is close, the socket will be closed // In the case of keepalive, because a thread processes a socket, the current thread will introduce it after exiting the while, // At that time, for the socket, it still had to continue to introduce the connection, so a new thread will be opened to continue to process the socket if (breakKeepAliveLoop(socketWrapper)) { break; } } rp.setStage(org.apache.coyote.Constants.STAGE_ENDED); }