繁体   English   中英

每个事件的 NIO 线程池

[英]NIO thread pool per event

我正在使用基于 java NIO 的它接受来自客户端的连接(配置为非阻塞)并且只读取客户端发送的数据。 一旦连接的客户端将长时间坚持服务器,所以我使用单个线程“selector.select”和“Accept”,但连接的客户端将每15 秒发送一次消息,客户端数量为 5000,每条消息是大小150 字节

我没有为每次从客户端读取的内容创建一个新线程,而是决定拥有一个包含 100 个线程的线程池,但服务器无法从所有客户端读取数据,它只是挂起 每次创建新线程时,它都能够从所有客户端读取数据。

这是我的 ReadEvent 线程

class ReadEvent implements Runnable {

    private static Logger logger = Logger.getLogger(ReadEvent.class.getName());
    private SelectionKey key;

    /**
     * Constructor to initialize the thread with the key having read event
     * pending.
     * 
     * @param key
     *            SelectionKey having read event.
     **/
    public ReadEvent(SelectionKey key) {
        this.key = key;
    }

    /**
     * Method to read the data from the key.
     **/
    @Override
    public void run() {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        synchronized (socketChannel) {
            if (socketChannel.isOpen()) {
                try {
                    ByteBuffer readBuffer = ByteBuffer.allocate(150);
                    int numRead = 0;
                    try {
                        /* ".read" is nonblocking */
                        numRead = socketChannel.read(readBuffer);
                        /*
                         * Some other IO error on reading socketChannel.
                         */
                    }
                    catch (IOException e) {
                        logger.debug(
                            "[run] Connection abruptly terminated from client",
                            e);
                        key.channel().close();
                        return;
                    }
                    if (numRead == -1) {// socket closed cleanly
                        key.channel().close();
                        return;
                    }
                    String data = null;
                    data = new String(readBuffer.array(),
                        Charset.forName("ASCII"));
                    /* Send the read data to the DataDispatcher Actor */
                    Main.getDataDispatcher().tell(data, ActorRef.noSender());
                }
                catch (IOException e) {
                    logger.debug("[run] ", e);
                    return;
                }
            }
            else {// socketChannel is closed
                try {
                    key.channel().close();// Sanitary close operation
                    return;
                }
                catch (IOException e) {
                }
            }
        }
    }
}

我无法弄清楚线程池的过载,任何关于 ReadThread 实现的建议都会对我有所帮助。

更新 1:固定线程池上的 java.lang.OutOfMemoryError

调用读取事件的片段:

每次阅读的线程:

try {
    if (!key.isValid()) {
        continue;
    }
    if (key.isAcceptable()) {
        this.accept(key);
    }
    else if (key.isReadable()) {
        new Thread(new ReadEvent(key)).start();
    }
}
catch (CancelledKeyException e) {// key has been canceled
}

上面的代码片段适用于几千个客户端。

使用线程池

ExecutorService executor = Executors.newFixedThreadPool(100);

try {
    if (!key.isValid()) {
        continue;
    }
    if (key.isAcceptable()) {
        this.accept(key);
    }
    else if (key.isReadable()) {
        executor.execute(new ReadEvent(key));
    }
}
catch (CancelledKeyException e) {// key has been canceled
}

上面的代码片段没有为所有客户端提供服务,发现堆大小逐渐增加,大部分(几乎 100%)的 CPU 用于GC ,最终得到java.lang.OutOfMemoryError: GC 开销限制超出异常

6.7 年后,嘿,我遇到了类似的问题。 问题是在实际读取套接字之前 [socketChannel.read(...)] 操作被认为是有效的。 通过将 SelectionKey 提交到线程池,请记住该线程将独立于选择线程执行,这意味着该操作仍按照选择线程准备就绪,并将继续将操作提交到您的线程池,直到其中一个提交的线程实际读取 [ socketChannel.read(...)] 通道和操作被视为无效并从就绪集中删除。 其他人已经提到的解决方案是读取选择线程内的字节,然后将通道和读取的内容传递到线程池中进行后期处理。

  case SelectionKey.OP_READ: {
       SocketChannel channel = (SocketChannel) key.channel();
       try {
            ByteBuffer buffer = ByteBuffer.allocate(1024 * 8);
            int read = channel.read(buffer);// read in selecting thread
            if (read <= -1) {
                key.cancel();//Signals the channel is closed
            } else {
               threadPool.execute(() -> {
                  //pass the channel & contents into the thread pool
               });
            }
       } catch (IOException e) {
           e.printStackTrace();
           key.cancel();
       }
  }
  break;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM