一、创建服务端Channel
netty创建服务端channel.png- newSocket():通过jdk创建底层jdk channel,SelectorProvider.provider()根据当前操作系统选择对应的provider;
- AbstractChannel:创建id/unsafe/pipeline
- AbstractNioChannel:configureBlocking(false),阻塞模式
- NioServerSocketChannelConfig:跟tcp相关参数的配置类,对创建出的channel参数进行配置;此处初始化时创建AdaptiveRecvByteBufAllocator(自适应内存分配器)并放到成员遍历allocator上
二、初始化服务端Channel
void init(Channel channel) {
//配置用户自定义的options,对应启动时的.option()
setChannelOptions(channel, newOptionsArray(), logger);
//配置用户自定义的attributes,对应启动时的.attr()
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
//配置用户自定义的childOptions,对应启动时的.childOption(), 每次accept一个新连接都会配置到新创建的channel上
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
//配置用户自定义的childAttributes,对应启动时的.childAttr(), 每次accept一个新连接都会配置到新创建的channel上
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
//配置服务端pipeline, 对应启动时的.handler()
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
//默认服务端pipeline都会添加一个ServerBootstrapAcceptor,这个特殊的channelHandler主要是用来给新创建的连接分配线程
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
- 配置用户自定义的options,对应启动时的.option()
- 配置用户自定义的attributes,对应启动时的.attr()
- 配置用户自定义的childOptions,对应启动时的.childOption(), 每次accept一个新连接都会配置到新创建的channel上
- 配置用户自定义的childAttributes,对应启动时的.childAttr(), 每次accept一个新连接都会配置到新创建的channel上
- 配置服务端pipeline, 对应启动时的.handler()
- 默认服务端pipeline都会添加一个ServerBootstrapAcceptor,这个特殊的channelHandler主要是用来给新创建的连接分配线程
三、注册selector
注册selector.png- 从代码上看先是把channel注册在group上,再从group里面找到loop把channel注册到loop上,最后变成把loop注册到channel上(通过unsafe)
- ChannelFuture regFuture = config().group().register(channel) 其中config().group()-NioEventLoopGroup;
- next().register(channel)其中next()-NioEventLoop(NioEventLoop后面会详细讲解);
- promise.channel().unsafe().register(this, promise)其中promise.channel().unsafe()-NioMessageUnsafe;
- this.eventLoop = eventLoop 绑定线程
- javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
- 通过javaChannel()(上一《一、创建服务端Channel》中newSocket()创建的jdk的channel)调用jdk底层注册,把当前jdk的channel注册到selector(在NioEventLoop里后面会详细讲解)上面;
- this:是netty的NioServerSocketChannel,作为attachment注册上去,方便后续从连接中获取
- pipeline.invokeHandlerAddedIfNeeded();事件的回调,在添加channelHandler到channel上时触发;对应handler中handlerAdded方法
- pipeline.fireChannelRegistered(); channel注册成功的事件传播出去;对应handler中channelRegistered方法
四、绑定端口
绑定端口.png- regFuture.addListener添加operationComplete事件,异步执行内部doBind0方法进行端口绑定;
- doBind0中execute的Runnable实际上被当作task添加到taskQueue中; 在NioEventLoop run方法ranTasks = runAllTasks(0);时执行(这部分逻辑会在下一章节NioEventLoop中分析)
- channel.bind会通过channel中的pipeline进行绑定(pipeline.bind);pipeline上最终会在默认的HeadContext上通过unsafe调用到jdk底层bind方法进行绑定
- 绑定之后通过pipeline.fireChannelActive()把绑定完成事件传播出去
- 传播到HeadContext时会执行channel.read
- read方法通过pipeline执行,最终在默认的HeadContext上通过unsafe调用到jdk底层interestOps方法,注册感兴趣的事件
protected void doBeginRead() throws Exception {
//《三、注册selector》时注册selector时返回的selectKey
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
//获取selectKey感兴趣的事件(当时注册的0)
final int interestOps = selectionKey.interestOps();
//readInterestOp是NioServerSocketChannel构造方法里设置的
if ((interestOps & readInterestOp) == 0) {
//在原interestOps基础上新增一个readInterestOp事件(OP_ACCEPT事件(16))
selectionKey.interestOps(interestOps | readInterestOp);
}
}
至此,服务端启动流程就结束了
newChannel() ~> init() ~> register() ~> doBind()
网友评论