【问题标题】:Why Tomcat's Non-Blocking Connector is using a blocking socket?为什么 Tomcat 的非阻塞连接器使用阻塞套接字?
【发布时间】:2014-04-19 10:35:04
【问题描述】:

我在看Non-blocking I/O, java NIO and tomcat connector,看了tomcat的NIO Connector的代码,found this in NioEndpoint.bind():

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

我没有使用 NIO 的经验,所以有人可以解释一下当套接字配置为阻塞时它是如何非阻塞的吗?

【问题讨论】:

  • 不应该设置为false 使其变为非阻塞,这是你的问题吧?

标签: java tomcat nio


【解决方案1】:

看起来在这次提交https://github.com/apache/tomcat/blob/bd8122700c2e70e5adbaddcd289fb1a974f981fe/java/org/apache/tomcat/util/net/NioEndpoint.java引入了以下行

据我所知,NioEndpoint 正在使用阻塞 ServerSocketChannel 来阻塞并等待传入​​的连接,只有在它接受它之后,它才会以非阻塞方式处理这个传入的套接字通道(参见 setSocketOptions 方法)。

使 ServerSocketChannel 成为非阻塞的替代方法将导致作者指出忙读取 - 即一个线程将不断轮询传入的连接,因为非阻塞模式下的 accept() 可能返回 null。

你可以找到一些有用的解释here

附: 我认为神秘的 APR 代表 Apache Portable Runtime。

【讨论】:

    【解决方案2】:

    看完代码:

    正在侦听传入连接的serverSock 对象阻塞。与其新接受的连接关联的socket 通道对象是实现非阻塞 I/O 的对象。

    Acceptor class 是一个侦听传入连接的线程,在其run 方法中具有以下定义:

    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);
                        }
                    } ....
    

    如您所见,处理新的socket 的是setSocketOptions 方法,它具有以下代码:

    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); 
    

    与每个连接关联的socket 通道对象用于在相应连接的端点中发送/接收数据,是真正实现non-blocking I/O 的通道对象。

    虽然可以始终将serverSock 对象accept 方法设置为非阻塞,但我相信使select(即接受)操作非阻塞是不切实际的,并且不会服务于任何实际目的,并且在任何实际情况下都不会有用。我想不出任何非阻塞接受操作有用的用例。那是给我的。

    【讨论】:

      【解决方案3】:

      从调用者的角度来看是非阻塞的。 API 仍然需要使用阻塞(在工作线程中)或异步 I/O 来实际完成操作。否则,套接字将需要一个自旋锁,从而导致 CPU 浸泡。

      您需要查看实现的其余部分以了解此映射是如何完成的。

      【讨论】:

      • 您似乎没有听说过 java.nio.channels.Selector。
      • 你是对的——我没有。 cmets 仍然正确,因为Selector 仍然需要映射到一些底层操作系统操作,并且在 *ix 实现中可能会映射到套接字 API select() 调用。 @ultrajohn 为在套接字上选择阻塞设置提供了更准确的理由。 NIO 似乎按照我的建议将一个线程专门用于每个接受会话。
      猜你喜欢
      • 1970-01-01
      • 2017-04-18
      • 2012-05-26
      • 2011-10-07
      • 2011-08-19
      • 2010-10-31
      • 2013-10-15
      • 2012-06-13
      • 2013-07-20
      相关资源
      最近更新 更多