Netty系列之:NIO和netty详解

简介netty为什么快呢?这是因为netty底层使用了JAVA的NIO技术,并在其基础上进行了性能的优化,虽然netty不是单纯的JAVA nio,但是netty的底层还是基于的是nio技术 。
nio是JDK1.4中引入的,用于区别于传统的IO,所以nio也可以称之为new io 。
nio的三大核心是Selector,channel和Buffer,本文我们将会深入探究NIO和netty之间的关系 。
NIO常用用法在讲解netty中的NIO实现之前,我们先来回顾一下JDK中NIO的selector,channel是怎么工作的 。对于NIO来说selector主要用来接受客户端的连接,所以一般用在server端 。我们以一个NIO的服务器端和客户端聊天室为例来讲解NIO在JDK中是怎么使用的 。
因为是一个简单的聊天室,我们选择Socket协议为基础的ServerSocketChannel,首先就是open这个Server channel:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress("localhost", 9527));serverSocketChannel.configureBlocking(false);然后向server channel中注册selector:
Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);虽然是NIO,但是对于Selector来说,它的select方法是阻塞方法,只有找到匹配的channel之后才会返回,为了多次进行select操作,我们需要在一个while循环里面进行selector的select操作:
while (true) {selector.select();Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iter = selectedKeys.iterator();while (iter.hasNext()) {SelectionKey selectionKey = iter.next();if (selectionKey.isAcceptable()) {register(selector, serverSocketChannel);}if (selectionKey.isReadable()) {serverResponse(byteBuffer, selectionKey);}iter.remove();}Thread.sleep(1000);}selector中会有一些SelectionKey,SelectionKey中有一些表示操作状态的OP Status,根据这个OP Status的不同,selectionKey可以有四种状态,分别是isReadable,isWritable,isConnectable和isAcceptable 。
当SelectionKey处于isAcceptable状态的时候,表示ServerSocketChannel可以接受连接了,我们需要调用register方法将serverSocketChannel accept生成的socketChannel注册到selector中,以监听它的OP READ状态,后续可以从中读取数据:
private static void register(Selector selector, ServerSocketChannel serverSocketChannel)throws IOException {SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}当selectionKey处于isReadable状态的时候,表示可以从socketChannel中读取数据然后进行处理:
private static void serverResponse(ByteBuffer byteBuffer, SelectionKey selectionKey)throws IOException {SocketChannel socketChannel = (SocketChannel) selectionKey.channel();socketChannel.read(byteBuffer);byteBuffer.flip();byte[] bytes= new byte[byteBuffer.limit()];byteBuffer.get(bytes);log.info(new String(bytes).trim());if(new String(bytes).trim().equals(BYE_BYE)){log.info("说再见不如不见!");socketChannel.write(ByteBuffer.wrap("再见".getBytes()));socketChannel.close();}else {socketChannel.write(ByteBuffer.wrap("你是个好人".getBytes()));}byteBuffer.clear();}上面的serverResponse方法中,从selectionKey中拿到对应的SocketChannel,然后调用SocketChannel的read方法,将channel中的数据读取到byteBuffer中,要想回复消息到channel中,还是使用同一个socketChannel,然后调用write方法回写消息给client端,到这里一个简单的回写客户端消息的server端就完成了 。
接下来就是对应的NIO客户端,在NIO客户端需要使用SocketChannel,首先建立和服务器的连接:
socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9527));然后就可以使用这个channel来发送和接受消息了:
public String sendMessage(String msg) throws IOException {byteBuffer = ByteBuffer.wrap(msg.getBytes());String response = null;socketChannel.write(byteBuffer);byteBuffer.clear();socketChannel.read(byteBuffer);byteBuffer.flip();byte[] bytes= new byte[byteBuffer.limit()];byteBuffer.get(bytes);response =new String(bytes).trim();byteBuffer.clear();return response;}向channel中写入消息可以使用write方法,从channel中读取消息可以使用read方法 。
这样一个NIO的客户端就完成了 。
虽然以上是NIO的server和client的基本使用,但是基本上涵盖了NIO的所有要点 。接下来我们来详细了解一下netty中NIO到底是怎么使用的 。
NIO和EventLoopGroup以netty的ServerBootstrap为例,启动的时候需要指定它的group,先来看一下ServerBootstrap的group方法:
public ServerBootstrap group(EventLoopGroup group) {return group(group, group);}public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {...}


推荐阅读