在介绍基于Netty服务器启动过程之前,先来说说其相应的核心模块:
如果对Channel、ChannelPipeline、Handler之间关系不熟悉,请先了解Netty线程模型。接下来在Netty启动分析中之前,先了解下Netty的引导类,其核心类图如下:
由类图可以看到ServerBootstrap(服务端),BootStrap(客户端)均继承至AbstractBootStrap,下面给出ServerBootStrap和AbstractBootStrap关键成员变量。
类名 | ServerBootStrap | AbstractBootStrap |
---|---|---|
Channel连接相应相应配置 | childOptions | options |
参数 | childAttrs | Attrs |
线程组 | childGroup | group |
业务处理 | childHandler | Handler |
表中对比了类中的成员变量,先前在Netty线程模型中介绍过,Netty服务端有两个线程池(bossgroup和workgroup)来处理网络事件,这里AbstractBootStrap成员变量group对应bossgroup,负责处理客户端认证,ServerBootStrap中的成员变量childGroup即为线程模型中的workGroup线程池,负责处理认证成功的网络连接。其他参数也对应着bossgroup和workgroup中相应设置。
在对引导类有一个初步认知之后,接下来我们从结合一段启动代码分析Netty的启动过程:
EventLoopGroup bossGroup = new NioEventLoopGroup();//创建线程池
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
/*
**配置线程池相应参树
*/
b.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.handler(new LoggingHandler())
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
}
});
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
代码中,创建Serverbootstrap对象之后,分别执行方法:
group(EventLoopGroup bossGroup, EventLoopGroup childGroup)//初始化线程池
channel(Class extends C> channelClass)//初始化bossGroup中channel
localAddress(int inetPort)//初始化网络通信ip及端口
childHandler(ChannelHandler childHandler)//设置childGroup中的childHandler
bind(),//其启动服务
此外ServerBootstrap提供一些列public方法,来设置其成员变量及父类(AbstractBootstrap成员变量),相应方法如下:
public ServerBootstrap group(EventLoopGroup group);
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup);
public ServerBootstrap childOption(ChannelOption childOption, T value);
public ServerBootstrap childAttr(AttributeKey childKey, T value);
public ServerBootstrap childHandler(ChannelHandler childHandler);
示例demo部分给出group(EventLoopGroup parentGroup, EventLoopGroup childGroup)源码:
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);// parentGroup传给AbstractBootstrap成员
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;//childGroup传给ServerBootstrap成员
return this;
}
以看到group方法调用父类AbstractBootstrap的group(EventLoopGroup)方法,并初始化其成员变量childGroup;(先在这里说一下,这里的parentGroup对应Netty线程模型中处理客户端连接、认证的线程池,childGroup处理连接、认证成功客户端网络事件的线程池)。
在准备好所有初始化工作之后,netty就开始进入父类AbstractBootstrap的bind(),其代码如下:
public ChannelFuture bind() {
validate();//检验相应初始化参数
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
throw new IllegalStateException("localAddress not set");
}
return doBind(localAddress);
}
可以看到bind()主要是调用了doBind(localAddress);
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();//业务一
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);//业务二
return promise;
} else {
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null){
promise.setFailure(cause);
} else {
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
上面给出代码主要执行了initAndRegister();
与doBind0(regFuture, channel, localAddress, promise);
final ChannelFuture initAndRegister() {
Channel channel = null;
…
channel = channelFactory.newChannel();//NioServerSocketChannel对象
init(channel);//初始化channel
…
ChannelFuture regFuture = config().group().register(channel);//将channel注册到线程
…
return regFuture;
}
在initAndRegister中先创建通过反射创建了一个NioServerSocketChannel实例,接着调用init方法初始化该NioServerSocketChannel对象,init(Channel channel)
源码如下:
void init(Channel channel) throws Exception {
final Map, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey
init()方法设置了NioServerSocketChannel实例的attr、option等,同时添加一个LoggingHandler与ServerBootstrapAcceptor的hanlder,关于ServerBootstrapAcceptor待会说明它的作用;
接着config().group().register(channel)
,通过elcipse跟踪方法调用链是:
(SingnalThreadEventLoop)register(channel)->
SingnalThreadEventLoop.register(final ChannelPromise promise)->
AbstractUnsafe.register(EventLoop eventLoop, final ChannelPromise promise)->
AbstractUnsafe.register0(ChannelPromise promise)
AbstractUnsafe.register0
源码如下:
private void register0(ChannelPromise promise) {
…
doRegister();//将NioServerSocketChannel注册到Selector上
…
pipeline.invokeHandlerAddedIfNeeded();//将NioServerSocketChannel的handler添加至pipeline
…
pipeline.fireChannelRegistered();//在Channel的hanlder链中传播register
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();//在handler链中传播active事件
} else if (config().isAutoRead()) {
beginRead();//设置Selector中selectionkey(readInterestOp = 0)
…
}
}
}
在register0内,首先执行doRegister()->AbstractNioChannel. doRegister()
,将NioServerSocketChannel注册到Selector上。
接着调用
pipeline.invokeHandlerAddedIfNeeded();
(DefaultChannelPipeline) invokeHandlerAddedIfNeeded()->callHandlerAddedForAllHandlers()
private void callHandlerAddedForAllHandlers() {
final PendingHandlerCallback pendingHandlerCallbackHead;
synchronized (this) {
assert !registered;
registered = true;
pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
this.pendingHandlerCallbackHead = null;
}
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
}
pendingHandlerCallbackHead为DefaultChannelPipeline成员变量,在初始化调用ChannelPipeline中的addXxx(handler)系列方法时,会将handler添加至pendingHandlerCallbackHead中;
其调用链
task.execute()
->(PendingHandlerAddedTask)callHandlerAdded0()
->ctx.handler().handlerAdded(ctx)
将方法调用就是将addXxxv(handler)中handler添加至pipeline中;
接着调用pipeline.fireChannelRegistered()
中在pipeline的handler链中传播注册事件
最后调用beginRead()->AbstractUnsafe. beginRead()
方法,该方法的关键执行doBeginRead(),代码如下:
protected void doBeginRead() throws Exception {
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
doBeginRead()将Selector中selectionkey设置为读就绪(readInterestOp = 0
)
至此initAndRegister()
方法执行完毕,开始执行doBind0()
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
doBind0()
中调用链如下:
channel.bind()
->head.bind()
->AbstractUnsafe.bind()
->NioServerSocketChannel.bind()
服务端绑定端口,此时服务器可处理IO事件。
当客户端连接或由读事件时,下面给出线程池中线程执行体NioEvemtLoop相应IO事件processSelectedKey()
源代码。
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
…
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
…
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
…
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
…
}
当有客户端连接或读时间时,将会调用AbstractNioChannel.NioUnsafe的read()
方法
public void read() {
…
do {
int localRead = doReadMessages(readBuf);//添加NioSocketChannel至readBuf中
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
…
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));//在pipeline传播读事件
}
…
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
…
}
在read()方法的执行中主要调用了doReadMessages(readBuf)
与pipeline.fireChannelRead(readBuf.get(i));
doReadMessages(readBuf)
代码如下:
protected int doReadMessages(List buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());
…
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
…
return 0;
}
该方法将连接的成功客户端SocketChannel添加的buf中。
而pipeline.fireChannelRead(readBuf.get(i))
调用(DefaultChannelPipeline)fireChannelRead(Object msg)
循环处理注册的SocketChannel;相应调用链如下
(DefaultChannelPipeline)fireChannelRead(Object msg)
->AbstractChannelHandlerContext.invokeChannelRead(head, msg)
-> AbstractChannelHandlerContext.invokeChannelRead(Object msg)
-> ((ChannelInboundHandler) handler()).channelRead(this, msg);
由调用链可知,最后执行各入站处理器(ChannelInboundHandler)的channelRead()方法 。
现在我们再回过头来看服务端在初始化时,向NioServerSocektChannel
中添加的ServerBootstrapAcceptor(handler)
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
private final EventLoopGroup childGroup;
private final ChannelHandler childHandler;
private final Entry, Object>[] childOptions;
private final Entry, Object>[] childAttrs;
private final Runnable enableAutoReadTask;
ServerBootstrapAcceptor(
final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
Entry, Object>[] childOptions, Entry, Object>[] childAttrs) {
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;
enableAutoReadTask = new Runnable() {
@Override
public void run() {
channel.config().setAutoRead(true);//在客户端认证成功之后
}
};
}
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {//将channel转移至childFGroup
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
for (Entry, Object> e: childAttrs) {
child.attr((AttributeKey) e.getKey()).set(e.getValue());
}
try {
childGroup.register(child).addListener(new ChannelFutureListener() {//将NioSocketChannel注册到childGroup中
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
…
}
在客户端连接到达时,ServerBootstrapAcceptor
的channelRead(ChannelHandlerContext ctx, Object msg)
方法被调用,而在调用在方法时,传入Object msg
是连接成功的NioSocketChannel
,前文说过childGroup
为处理连接成功客户端Channel事件线程池,childGroup.register(child)
即将连接成功NioSocketChannel
注册到childGroup
中。到这里基于Nio 的Netty服务端启动流程分析完毕。
附本文贴出的源代码,来说Maven中央仓库,坐标如下:
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.15.Finalversion>
dependency>
此外,贴出的代码为了方便读者专注与代码主线流程,将一些异常处理,条件判断等等删除。