netty源码分析3-NioServerSocketChannel,NioSocketChannel

本文分析内容如下

  1. Channel主要接口分析
  2. AbstractChannel分析
  3. AbstractNioChannel分析
  4. AbstractNioByteChannel分析
  5. AbstractNioMessageChannel 分析
  6. NioSocketChannel分析
  7. AbstractNioMessageServerChannel分析
  8. NioServerSocketChannel分析

netty Channel 类图

netty源码分析3-NioServerSocketChannel,NioSocketChannel

 

Channel主要接口分析

io.netty.channel.Channel 接口方法分析

与jdk中定义Chanel要干的大致相同

  1. bind()绑定系列方法。
  2. connect()连接系列方法。
  3. read(),write()读写系列方法。
  4. accept() 接受连接方法

io.netty.channel.ServerChannel 接口方法分析

只有一个方法,返回childGroup

io.netty.channel.socket.ServerSocketChannel接口方法分析

获取地址和配置

AbstractChannel分析

AbstractChannel{

//主要属性如下

private final Unsafe unsafe;// 主要功能实现

private final DefaultChannelPipeline pipeline;// 事件执行

// netty Future实现,异步执行结果控制

private final ChannelFuture succeededFuture = new SucceededChannelFuture(this, null);

//..........省略代码.........

// 执行select(), 更新selectKeys.

private final EventLoop eventLoop;

//..........省略代码.........

}

主要构造方法

创建unsafe,pipeline,保存父Channel,保存parentGroup 中 的eventLoop

protected AbstractChannel(Channel parent, EventLoop eventLoop) {

this.parent = parent;// 保存父Channel, 当前 Channel==null

this.eventLoop = validate(eventLoop);// 非空和兼容校验,保存parentGroup 中 的eventLoop

unsafe = newUnsafe();//初始化unsafe××重要×××

pipeline = new DefaultChannelPipeline(this);//初始化pipeline××重要×××

}

 

主要方法

依赖pipeline实现的bind,connect, disconnect,read,write,writeAndFlush,都有重载方法。

例如:

public ChannelFuture bind(SocketAddress localAddress) {

return pipeline.bind(localAddress);

}

@Override

public Channel read() {

pipeline.read();

return this;

}

 

@Override

public ChannelFuture write(Object msg) {

return pipeline.write(msg);

}

小结:AbstractChannel是 Channel功能的框架实现,IO处理都依赖pipeline,

ChannelPipeline 执行 多handler 嵌入式调用(类似AOP),执行触发事件,负责调用原生NIO的Unsafe由子类创建。

NioEventLoop负责IO读写,系统task,定时任务,执行select(), 更新selectKeys.

AbstractNioChannel分析

主要属性

private final SelectableChannel ch;//原生的 ServerSocketChannel或SocketChannel

protected final int readInterestOp;// SelectionKey.OP_ACCEPT或SelectionKey.OP_READ

private volatile SelectionKey selectionKey;//使用eventLoop().selector注册产生的key

private ChannelPromise connectPromise;// 当前连接的future,如果不为null,后续连接将会失败。

private ScheduledFuture<?> connectTimeoutFuture;//连接超时future。

private SocketAddress requestedRemoteAddress;//服务器地址

主要构造方法

保存 原生channel,保存关注的事件枚举值,当前为SelectionKey.OP_ACCEPT

protected AbstractNioChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {

super(parent, eventLoop);

this.ch = ch;//保存 原生channel

this.readInterestOp = readInterestOp;// 保存关注的事件枚举值,server刚启动时 此值为SelectionKey.OP_ACCEPT

try {//设置非阻塞

ch.configureBlocking(false);

} catch (IOException e) {

try {

ch.close();

} catch (IOException e2) {

if (logger.isWarnEnabled()) {

logger.warn(

"Failed to close a partially initialized socket.", e2);

}

}

 

throw new ChannelException("Failed to enter non-blocking mode.", e);

}

}

 

主要方法

  1. 返回各个属性的get方法
  2. doRegister:使用原生SelectableChannel 执行 register,注册事件值为0
  3. doBeginRead: 订阅readInterestOp值代表的事件
  4. 定义了抽象方法doConnect和doFinishConnect。

 

疑难点分析

protected void doBeginRead() throws Exception {

//...

final int interestOps = selectionKey.interestOps();

if ((interestOps & readInterestOp) == 0) {

//订阅readInterestOp值代表的事件, 这里 或 | 运算代表合并的意思

selectionKey.interestOps(interestOps | readInterestOp);

}

}

注意:selectionKey是使用eventLoop().selector注册产生的key,selectionKey修改订阅事件,就是修改eventLoop().selector中该channel的订阅事件。

小结:AbstractNioChannel连接提供了属性支持,保存了原生channel,事件值, 实现了使用原生SelectableChannel 执行 register的doRegister方法。实现了事件值修改方法。

 

AbstractNioByteChannel分析

主要属性:

private Runnable flushTask;// flush任务,用于flush未完全执行时创建,交给EventLoop执行。

主要构造方法

protected AbstractNioByteChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch) {

super(parent, eventLoop, ch, SelectionKey.OP_READ);

}

最重要的是定义事件值是 OP_READ

主要方法

  1. doWrite系列方法:单条写ByteBuf功能
  2. setOpWrite:订阅写事件
  3. clearOpWrite:清除写事件
  4. doWriteFileRegion(FileRegion region): 原生NIO写文件,抽象方法
  5. doReadBytes(ByteBuf buf):原生NIO读文件到buf中,抽象方法

疑难点分析

doWrite系列方法非常重要,在《写数据》篇有重点分析

小结:AbstractNioByteChannel围绕单条写实现了个多个相关方法

 

AbstractNioMessageChannel 分析

AbstractNioMessageChannel 实现了doWrite,但是 这个方法没有用途

构造方法

protected AbstractNioMessageChannel(

Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {

super(parent, eventLoop, ch, readInterestOp);

}

 

NioSocketChannel分析

  1. 依赖原生channel实现的一些基本IO操作如doConnect,doFinishConnect,doDisconnect,doClose.
  2. 依赖原生channe和ByteBuf实现的读写方法doReadBytes(ByteBuf byteBuf)和doWriteBytes(ByteBuf buf)
  3. 依赖原生channe和FileRegion实现的写文件方法doWriteFileRegion(FileRegion region)
  4. 写数据入口方法doWrite(ChannelOutboundBuffer in) ,依赖父类doWrite(ChannelOutboundBuffer in) 完成单条写,自己实现多条写。

注意: 写数据 是写给目标,读数据是从目标读取。

疑难点分析

doWrite系列方法非常重要,在《写数据》篇有重点分析。

AbstractNioMessageServerChannel分析

构造方法:保存childGroup

protected AbstractNioMessageServerChannel(

Channel parent, EventLoop eventLoop, EventLoopGroup childGroup, SelectableChannel ch, int readInterestOp) {

super(parent, eventLoop, ch, readInterestOp);

this.childGroup = childGroup;

}

 

没有做什么业务处理

只有一个方法返回 childGroup

public EventLoopGroup childEventLoopGroup() {

return childGroup;

}

NioServerSocketChannel分析

构造方法

public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childGroup) {

super(null, eventLoop, childGroup, newSocket(), SelectionKey.OP_ACCEPT);

config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());

}

创建config

主要方法

  1. 依赖原生channel实现的一些基本IO操作如doBind,doClose,localAddress0
  2. 获取配置 config()
  3. 执行accept并创建NioSocketChannel的方法 doReadMessages(List<Object> buf)

 

实际上 这个channel 只是外部的壳子,大部分重要逻辑都在其对应的Unsafe实现,结合Unsafe分析,理解起来更加清晰。对于channel,unsafe这样的设计,我表示疑惑,我看到的是代码易读性比较差,没看出什么优点, 各位有对这块设计 研究明白的 请不吝赐教。