[英]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.