문제

I was reading about Non-blocking I/O, java NIO and tomcat connectors, looked at the code of tomcat's NIO Connector, and found this in NioEndpoint.bind():

serverSock.configureBlocking(true); //mimic APR behavior

I don't have experience with NIO, so can someone explain how it is non-blocking when the socket is configured to be blocking?

도움이 되었습니까?

해결책

Looks like the following line was introduced at this commit https://github.com/apache/tomcat/blob/bd8122700c2e70e5adbaddcd289fb1a974f981fe/java/org/apache/tomcat/util/net/NioEndpoint.java

As far as I can tell this is NioEndpoint is using blocking ServerSocketChannel in order for it to block and wait for an incoming connection and only after it accepts it it processes this incoming socket channel in a non-blocking manner (see setSocketOptions method).

The alternative to make ServerSocketChannel a non-blocking one will result as author points out into a busy read - that is a thread will be constantly polling for incoming connections as accept() in non-blocking mode may return null.

You can find some useful explanation here.

P.S. I presume that cryptic APR stands for Apache Portable Runtime.

다른 팁

After reading the code:

The serverSock object is listening for incoming connections is blocking. The socket channel object associated with its new accepted connection is the one that implements a non-blocking I/O.

The Acceptor class that is a thread that listens for incoming connections has the following definition in its run method:

protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            // Loop until we receive a shutdown command
            while (running) {
                // Loop if endpoint is paused
                while (paused && running) {
                   ...
                try {
                        ........
                    SocketChannel socket = null;
                    try {
                        // Accept the next incoming connection from the server
                        // socket
                        socket = serverSock.accept(); 

                    } catch (IOException ioe) {............}
                ...................
                // setSocketOptions() will add channel to the poller
                // if successful
                if (running && !paused) {
                    if (!setSocketOptions(socket)) {
                        countDownConnection();
                        closeSocket(socket);
                    }
                } ....

As you can see it's the setSocketOptions method that processes a new socket and it has the following code:

protected boolean setSocketOptions(SocketChannel socket) {
        // Process the connection
        try {
            //disable blocking, APR style, we are gonna be polling it
            socket.configureBlocking(false);
            Socket sock = socket.socket();
            socketProperties.setProperties(sock); 

The socket channel object associated with each connection that is used to send/receive data in the endpoints of the corresponding connection is the one that really implements a non-blocking I/O.

Although one can always set the serverSock object accept method to be non-blocking, I believe that making the select (i.e. accept) operation non-blocking would be impractical and would not server any real purpose and would not be useful in any real context. I could not think of any use case where a non-blocking accept operation would be useful. That is for me.

Non-blocking seen from the point of view of the caller. The API will still need to use blocking (in a worker thread) or asynchronous I/O to actually complete the operation. Otherwise the socket will need a spinlock with the resulting CPU soak.

You need to look at the remainder of the implementation to see how this mapping is accomplished.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top