我正在使用基于java NIO的接受来自客户端的连接(已配置的非阻塞),并且只读取客户端发送的数据。一旦连接的客户端将长时间坚持服务器,所以我使用单线程用于" selector.select"并且"接受",但连接的客户端将每隔 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实现的建议都会对我有所帮助。
UPDATE 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开销限制异常
答案 0 :(得分:0)
6.7年后,但是嘿,我遇到了类似的问题。问题是直到实际读取套接字[socketChannel.read(...)],该操作才被视为有效。 通过将SelectionKey提交到线程池,请记住该线程将独立于选择线程执行,这意味着该操作仍按照选择线程准备就绪,并将继续将操作提交到您的线程池,直到已提交的线程之一实际读取[通道和操作被视为无效,并已从就绪集中删除。 其他人已经提到的解决方案是读取选择线程中的字节,然后将通道和读取的内容传递到线程池中以进行后处理。