Java NIO 注意点
所属分类 nio
浏览量 1000
事件处理一定要移除key
Iterator< SelectionKey> it = selector.selectedKeys().iterator();
While(it.hasNext()){
SelectionKey key = it.next();
// remove key !!!
it.remove();
// handle
}
Selector返回的key集合非线程安全
正确注册Channel和更新interest
channel.register(selector, ops, attachment);
SelectionKey.interest(ops) linux上会阻塞,需要获取selector内部锁做同步
加入缓冲队列,等待注册,reactor单线程处理
if (isReactorThread()) {
channel.register(selector, ops, attachment);
} else{
register.offer(new Event(channel, ops, attachment));
selector.wakeup();
}
if (this.isReactorThread()) {
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
} else {
this.register.offer(new Event(key, SelectionKey.OP_READ));
selector.wakeup();
}
正确处理OP_WRITE
OP_WRITE处理不当很容易导致CPU 100%
OP_WRITE触发条件
socket发送缓冲区可写
远端关闭
有错误发生
正确的处理方式
仅在已经连接的channel上注册
仅在有数据可写的时候才注册
触发之后立即取消注册,否则会继续触发导致循环
处理完成后视情况决定是否继续注册
没有完全写入,继续注册
全部写入,无需注册
要写数据时才注册OP_WRITE操作
key.isWritable()是表示可写,网络不出现阻塞情况下,一直是可以写的,所认一直为true.
可以不注册OP_WRITE事件,直接写 ,注意判断返回值
正确取消注册channel
channel.close(),内部会调用key.cancel()
key.cancel()仅仅是将key加入cancelledKeys
直到下一次select才真正处理
并且channel的socketfd只有在真正取消注册后才会close(fd)
服务端,问题不大,select调用频繁
客户端,通常只有一个连接,关闭channel之后,没有调用select就关闭selector
sockfd没有关闭,停留在CLOSE_WAIT状态
正确的处理方式,取消注册也应当作为事件交给reactor处理,及时wakeup做select
适当的时候调用selector.selectNow()
Netty在超过256连接关闭的时候主动调用一次selectNow
避免同时注册OP_ACCPET和OP_READ , OP_CONNECT和OP_WRITE
不要在同一个socket同时注册多个操作
小心处理connect
SocketChannel.connect 在非阻塞模式下可能返回false,一定要判断返回值
返回false,后续处理
注册channel到selector,监听OP_CONNECT事件
在OP_CONNECT触发后,调用SocketChannel.finishConnect成功后,连接才真正建立
NIO bug
已经关闭的连接一直处于就绪状态,select(timeout)不阻塞,CPU消耗100%
升级jdk 或 代码规避
channel.close()之后马上调用select
Netty3,定期调用selectNow
一些建议
尽量不要尝试实现自己的nio框架
使用经过广泛实践的开源NIO框架Mina Netty xsocket 等
使用最新稳定版JDK
上一篇
下一篇
检查硬盘是否为SSD
NIO概述
第一个 NIO server 例子
Java NIO写事件处理技巧
java NIO buffer
Java NIO pipe