前言上一篇我们对Netty做了一个基本的概述,知道了Netty是什么以及Netty的简单应用。Netty源码分析系列(一)Netty概述本篇文章,我们将谈谈Netty的架构设计。在学习一个框架之前,我们首先要了解它的设计原理,然后再进行深入的分析。接下来,我们从三个方面分析Netty的架构设计。Selector模型JavaNIO是基于Selector模型来实现非阻塞I/O的。Netty底层是基于JavaNIO实现的,所以也使用了Selector模型。Selector模型解决了每个客户端一个线程的传统阻塞I/O编程问题。Selector提供了一种机制,用于监视一个或多个NIO通道并识别何时可以使用一个或多个NIO通道进行数据传输。这样,一个线程可以管理多个通道,从而管理多个网络连接。选择器提供了选择准备好执行的任务的能力。从底层的角度来看,Selector会轮询Channel是否准备好执行每个I/O操作。Selector允许单线程处理多个Channel。选择器是一种复用技术。SelectableChannel并不是所有的Channel都能被Selector复用,只有抽象类SelectableChannel的子类才能被Selector复用。例如,FileChannel不能被选择器重用,因为FileChannel不是SelectableChannel的子类。为了与Selector一起使用,SelectableChannel必须首先通过register方法注册此类的实例。该方法返回一个新的SelectionKey对象,表示Channel已经注册到Selector。使用Selector注册后,Channel将保持注册状态,直到取消注册。一个Channel最多只能向任何特定的Selector注册一次,但同一个Channel可以向多个Selector注册。您可以通过调用isRegistered方法来确定一个Channel是否注册了一个或多个Selector。SelectableChannel可以安全地供多个并发线程使用。将Channel注册到Selector使用SelectableChannel的register方法将Channel注册到Selector。方法接口源码如下:publicfinalSelectionKeyregister(Selectorsel,intops)throwsClosedChannelException{returnregister(sel,ops,null);}publicabstractSelectionKeyregister(Selectorsel,intops,Objectatt)throwsClosedChannelException;说明如下:sel:指定Channel要注册的Selector。ops:指定Selector需要查询的通道的操作。在Selector中注册的Channel代表一个SelectionKey事件。SelectionKey的类型包括:OP_READ:可读事件;值:1<<0OP_WRITE:可写事件;value:1<<2OP_CONNECT:客户端连接服务器的事件(tcp连接),一般是创建SocketChannel客户端通道;value:1<<3OP_ACCEPT:服务端接收客户端连接的事件,一般是创建一个ServerSocketChannel服务端通道;value:1<<4具体注册代码如下://1.创建通道管理器(Selector)Selectorselector=Selector.open();//2.创建通道ServerSocketChannelServerSocketChannelserverSocketChannel=ServerSocketChannel.open();//3.channel必须是非注册到SelectorBlocking,所以FileChannel不能使用Selector,因为FileChannel是阻塞的serverSocketChannel.configureBlocking(false);//4.第二个参数指定我们感兴趣的Channel事件类型SelectionKeykey=serverSocketChannel.register(selector,SelectionKey.OP_READ);//你也可以使用OR运算符|组合多个事件,例如SelectionKeykey=serverSocketChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);值得注意的是,一个Channel只能注册到一个Selector一次,如果Channel多次注册到Selector,实际上相当于更新了SelectionKey的兴趣集。SelectionKeyChannel和Selector的关系确定后,一旦Channel处于某种就绪状态,就可以被selector查询。然后通过调用Selector的select方法完成这项工作。select方法的作用是查询感兴趣的通道操作的就绪状态。//当注册事件到达时,该方法返回,否则该方法将一直阻塞selector.select();SelectionKey包含兴趣集合,代表选中的感兴趣的事件集合。兴趣集合可以通过SelectionKey进行读写,例如://返回当前感兴趣的事件列表intinterestSet=key.interestOps();//也可以通过interestSetboolean来判断其中包含的事件isInterestedInAccept=interestSet&SelectionKey.OP_ACCEPT;booleanisInterestedInConnect=interestSet&SelectionKey.OP_CONNECT;booleanisInterestedInRead=interestSet&SelectionKey.OP_READ;booleanisInterestedInWrite=interestSet&SelectionKey.OP_WRITE;//可以通过方法interestOps(intops)key.interestOps(interestSet|SelectionKey.OP_WRITE)修改事件列表;请看,通过对兴趣集合和给定的SelectionKey常量进行位与运算,您可以确定某个事件是否在兴趣集合中。SelectionKey包含现成的集合。就绪集是通道准备就绪的操作集。选择后,首先访问就绪集合。可以这样访问就绪集合:intreadySet=key.readyOps();//也可以用四种方法判断不同的事件是否就绪key.isReadable();//读取事件是否就绪key.isWritable();//写入事件是否就绪key.isConnectable();//客户端连接事件是否就绪key.isAcceptable();//服务器连接事件是否就绪我们可以通过SelectionKey获取当前通道和选择器//返回当前事件关联的Channel,可转换的选项包括:`ServerSocketChannel`和`SocketChannel`Channelchannel=key.channel();//返回与当前事件关联的Selector对象Selectorselector=key.selector();可以将对象或其他信息附加到SelectionKey,以便可以轻松识别特定频道。key.attach(theObject);对象attachedObj=key.attachment();使用register()方法向Selector注册Channel时,也可以附加对象。SelectionKeykey=channel.register(selector,SelectionKey.OP_READ,theObject);遍历SelectionKey一旦select方法被调用,返回值表示一个或多个通道就绪,那么就可以调用选择器的selectedKey()方法来访问SelectionKey集合中就绪的通道如下:Set
