带着疑问分析源码:ServerBootstrap bind的过程究竟做了什么,它是如何去接受请求处理请求的
###分析bind方法
#####粗略的跟踪ServerBootstrap的bind方法,画出时序图,红色标注了重要的步骤
可以发现,bind过程主要做了四个事情:
(1)创建ServerChannel,用于接收外部的连接请求:newChannel
(2)初始化channel:init(channel)
(3)将channel注册到线程池里:register
(4)绑定端口:doBind0
1. newChannel
newChannel的过程中会创建pipeline,pipeline中创建TailContext和HeadContext,并形成双向链表
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
这就是默认的pipeline,HeadContext实现了inbound/outbound,TailContext实现了inbound
2. init(channel)
观察ServerBootstrap的init方法,可以发现主要做了3个事情
//代码经过删减
void init(Channel channel) throws Exception {
setChannelOptions(channel, options, logger); //1. 设置option
channel.attr(key).set(e.getValue()); //2. 设置attribute
ChannelPipeline p = channel.pipeline(); //3. 获取上一步中初始化好的pipeline
p.addLast(new ChannelInitializer<Channel>() {//4. 为pipeline添加一个ChannelInitializer
});
}
这里最重要的就是p.addLast,观察DefaultChannelPipeline的addLast方法
//代码经过删减
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
newCtx = newContext(group, filterName(name, handler), handler);//1. 为handler构造一个handlerContext
addLast0(newCtx);//2. 将handlerContext插入到tailContext的前面
callHandlerAdded0(newCtx);//3. 调用handler的handlerAdd方法
}
而ChannelInitializer又是一种特殊的handler,它实际不会处理什么业务逻辑,当ChannelInitializer被加到pipeline后,触发了ChannelInitializer的handlerAdd方法,handlerAdd方法会先调用override的initChannel方法,然后把自己从pipeline中remove。
在ServerBootstrap的init方法中所添加的handler是这样的。
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
initChannel会先往pipeline里加入用户设置的handler,然后最后再加入ServerBootstrapAcceptor。
如果此处用户没有设置handler,则此时的pipeline是这样的。
3. register
会向线程池申请一个线程并注册
//类名或对象名::调用方法
MultithreadEventLoopGroup::register //1. 向线程池申请注册
MultithreadEventLoopGroup::next //2. 获取下一个线程
AbstractUnsafe::register //3. 交给Unsafe进行注册
AbstractSelectableChannel::register //4. 最终的注册操作
ChannelPipeline::fireChannelRegistered //5. 触发注册事件
4. doBind0(暂不详述)
绑定到某一个端口
###如何接受请求
观察NioEventLoop的processSelectedKey方法,得出接收到消息后的大致流程
//类名或对象名::调用方法
NioEventLoop::processSelectedKey //1. 开始处理消息
unsafe::read //2. 读取消息
pipeline::fireChannelRead //3. 交给channel对应的pipeline处理消息
AbstractChannelHandlerContext::invokeChannelRead(head, msg)//4. 从pipeline的head开始调用channelRead
ctx::findContextInbound //5. 从head开始寻找第一个inbound的handler
ctx::fireChannelRead //6. 将read事件传递给handler
handler::channelRead //7. 最终的业务逻辑所在
一般handler的channelRead方法中,会在处理完消息后,选择性地把read时间传递给pipeline中的下一个inboundhandler。
所以,当ServerChannel接收到连接消息后,其对应pipeline中的ServerBootstrapAcceptor会处理channelRead事件。
此处,我们先看unsafe.read,对应NioMessageUnsafe的read方法
//代码有所删减
public void read() {
int localRead = doReadMessages(readBuf);//1. 接收消息
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));//2. 交给pipeline处理
}
}
这里是怎么处理消息的呢,观察NioServerSocketChannel的doReadMessages方法
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());//接收连接请求,创建SocketChannel
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));//封装成NioSocketChannel放到bufList里
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
可知ServerChannel为每一个连接请求创建NioSocketChannel,并传递下去处理。
观察ServerBootstrapAcceptor的channelRead方法
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler); //为pipeline添加handler
setChannelOptions(child, childOptions, logger);//设置option
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());//设置attr
}
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
可以看出,这里做了类似初始化ServerChannel的事情:
(1)添加handler
(2)设置option
(3)设置attr
至此,连接请求处理完毕,生成的NioSocketChannel也会收到消息,然后交给pipeline处理消息。