netty源码分析3-NioServerSocketChannel,NioSocketChannel
本文分析内容如下
- Channel主要接口分析
- AbstractChannel分析
- AbstractNioChannel分析
- AbstractNioByteChannel分析
- AbstractNioMessageChannel 分析
- NioSocketChannel分析
- AbstractNioMessageServerChannel分析
- NioServerSocketChannel分析
netty Channel 类图
Channel主要接口分析
io.netty.channel.Channel 接口方法分析
与jdk中定义Chanel要干的大致相同
- bind()绑定系列方法。
- connect()连接系列方法。
- read(),write()读写系列方法。
- 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);
}
}
主要方法
- 返回各个属性的get方法
- doRegister:使用原生SelectableChannel 执行 register,注册事件值为0
- doBeginRead: 订阅readInterestOp值代表的事件
- 定义了抽象方法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
主要方法
- doWrite系列方法:单条写ByteBuf功能
- setOpWrite:订阅写事件
- clearOpWrite:清除写事件
- doWriteFileRegion(FileRegion region): 原生NIO写文件,抽象方法
- 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分析
- 依赖原生channel实现的一些基本IO操作如doConnect,doFinishConnect,doDisconnect,doClose.
- 依赖原生channe和ByteBuf实现的读写方法doReadBytes(ByteBuf byteBuf)和doWriteBytes(ByteBuf buf)
- 依赖原生channe和FileRegion实现的写文件方法doWriteFileRegion(FileRegion region)
- 写数据入口方法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
主要方法
- 依赖原生channel实现的一些基本IO操作如doBind,doClose,localAddress0
- 获取配置 config()
- 执行accept并创建NioSocketChannel的方法 doReadMessages(List<Object> buf)
实际上 这个channel 只是外部的壳子,大部分重要逻辑都在其对应的Unsafe实现,结合Unsafe分析,理解起来更加清晰。对于channel,unsafe这样的设计,我表示疑惑,我看到的是代码易读性比较差,没看出什么优点, 各位有对这块设计 研究明白的 请不吝赐教。