Tomcat currently supports BIO (blocking I/O), NIO (non-blocking I/O), AIO (asynchronous non-blocking IO, NIO upgrade), APR (Apache Portable Runtime) model, this paper mainly introduces NIO model, NIO model is widely used in a variety of distributed, communication, Java system. Such as Dubbo, Jetty, Zookeeper and other framework middleware, use NIO to implement the basic communication components
The disadvantages of BIO
In the traditional BIO model, a thread is created for each request. When a thread requests data from the kernel, it waits until the kernel is ready and returns the data
In Tomcat, Http11Protocol by the implementation of blocking Http protocol request, through the traditional ServerSocket operation, according to the incoming parameters set to listen to the port, if the port is legitimate and not occupied, the service listening success, and then through an infinite loop to listen to the client connection, If no client is connected, the main thread blocks on the Accept operation of the ServerSocket (the BIO is no longer supported in versions 8 and 9 of Tomcat).
In BIO, two blocks occur:
First block connect call: waiting for a connection request from the client. If there is no connection from the client, the server will block and wait
The second block accept call: after the client connects, the server waits for the client to send data. If the client does not send data, the server blocks for the client to send data
In Tomcat, a worker thread pool is maintained to handle socket requests. If there are no idle threads in the worker thread pool, acceptors will block. The server is under a lot of stress
NIO model
NIO model makes up for the deficiency of BIO model. It is based on the selector to detect the ready state of the Socket to notify thread processing, so as to achieve the purpose of non-blocking. Tomcat NIO is based on THE I/O reuse (SELECT /poll/epoll) model, and NIO has the following concepts:
Channel
A Chnnel is a Channel. Network data is read and written through a Channel. The difference between a Channel and a stream is that a stream is unidirectional while a Channel is bidirectional
Selector
The multiplexer Selector constantly polls the channels registered on it, and if a read or write occurs on a Channel, that Channel is ready to be selected by the Selector, The collection of ready channels can be retrieved through the SelectionKey (Channel listening key) for subsequent IO operations. So as long as there’s one thread that’s doing the Selector polling, you can access thousands and thousands of clients
In Tomcat, HTTP/1.1 protocol requests for non-blocking IO are handled by the NioEndpoint
NioEndpoint.bind()
public void bind(a) throws Exception {
this.serverSock = ServerSocketChannel.open();
this.socketProperties.setProperties(this.serverSock.socket());
InetSocketAddress addr = this.getAddress() ! =null ? new InetSocketAddress(this.getAddress(), this.getPort()) : new InetSocketAddress(this.getPort());
this.serverSock.socket().bind(addr, this.getAcceptCount());
this.serverSock.configureBlocking(true);
if (this.acceptorThreadCount == 0) {
this.acceptorThreadCount = 1;
}
if (this.pollerThreadCount <= 0) {
this.pollerThreadCount = 1;
}
this.setStopLatch(new CountDownLatch(this.pollerThreadCount));
this.initialiseSsl();
this.selectorPool.open();
}
Copy the code
Bind () enables the ServerSocketChannel to bind addresses and ports through the ServerSocketChannel
NioEndpoint.startInternal()
public void startInternal(a) throws Exception {
if (!this.running) {
this.running = true;
this.paused = false;
this.processorCache = new SynchronizedStack(128.this.socketProperties.getProcessorCache());
this.eventCache = new SynchronizedStack(128.this.socketProperties.getEventCache());
this.nioChannels = new SynchronizedStack(128.this.socketProperties.getBufferPool());
if (this.getExecutor() == null) {
this.createExecutor();
}
// Control the maximum number of connections
this.initializeConnectionLatch();
// Start poller thread
this.pollers = new NioEndpoint.Poller[this.getPollerThreadCount()];
for(int i = 0; i < this.pollers.length; ++i) {
this.pollers[i] = new NioEndpoint.Poller();
Thread pollerThread = new Thread(this.pollers[i], this.getName() + "-ClientPoller-" + i);
pollerThread.setPriority(this.threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
// Start the acceptor thread
this.startAcceptorThreads(); }}Copy the code
StartInternal () initializes connections and starts worker thread pools poller thread groups and acceptor thread groups. Acceptors listen for Socket connections. After each acceptor is started, the Accept () method of the ServerSocketChannel is looped to receive new connections. And then call the endpoint. SetSocketOptions (socket) processing a new connection, in the endpoint. SetSocketOptions (socket) is through getPoller0 (). The register (channel), Register the current NioChannel with Poller, and this logic is handled in acceptor.run ()
After calling getPoller0().register(channel), the request socket is wrapped as a PollerEvent and added to events. This is done by the Poller thread. Poller’s run() loops through the events() method to handle channal registered to a Selector (each poller turns on a Selector), listening for the channel’s OP_READ event. If the state is readable, place the task into the worker thread pool in processKey (). The whole process is roughly shown below
In the NIO model, no connection has to correspond to a processing thread, the connection is registered with the Selector, and when the Selector listens for a valid request, a corresponding thread is dispatched to handle it. When the connection has no request, there is no worker thread to handle it
Specify the IO model in Tomcat
You can specify the IO protocol for the connector in Tomcat by using the protocol attribute in connector element of server.xml. The default value is HTTP/1.1, indicating the current version of the default protocol. You can change HTTP/1.1 to the IO protocol specified below
Org. Apache. Coyote. Http11. Http11Protocol: BIO
Org. Apache. Coyote. Http11. Http11NioProtocol: NIO
Org. Apache. Coyote. Http11. Http11Nio2Protocol: NIO2
Org. Apache. Coyote. Http11. Http11AprProtocol: APR
conclusion
BIO creates one thread per connection, which is costly in performance and not suitable for high-concurrency scenarios. NIO monitors connection status and notifies thread processing based on multiplexing selector. Only when there is a request on the connection monitored, will a thread be allocated to handle it. NIO uses a small number of threads to manage a large number of connections, optimizing IO read and write, but also increasing CPU calculation, which is suitable for a large number of connection scenarios