Netty源码分析
先来一段测试的服务端代码方便我们debug跟踪源码(用的是netty-all-4.1.39版本)
Netty-Server
public class TestServer {
public static void main(String[] args) {
new ServerBootstrap()
.group(new NioEventLoopGroup(),new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler());
}
})
.bind(9999);
}
}
首先来看看bind(9999)这个方法,我们启用debug模式方便跟踪源码
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;
}
}
final ChannelFuture regFuture = initAndRegister();
在这里会进行初始化ServerSocketChannel并给他绑定一个selector选择器,这里其实也是一个异步操作,主线程进去执行这些操作
regFuture.isDone()是判断主线程去执行 initAndRegister();结束没如果结束了则走里面如果没结束则走下面
regFuture.addListener(new ChannelFutureListener()则是主线程在等待回调,等待nioEventLoopGroup中的线程去执行绑定doBind0(regFuture, channel, localAddress, promise);
大家可以看看这两张图
图一是主线程执行到regFuture.addListener
图二是nioEventLoopGroup中的线程去执行doBind0(regFuture, channel, localAddress, promise);
接下来我们再展开分析initAndRegister()方法
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
channel.unsafe().closeForcibly();
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
首先看第一句channelFactory.newChannel();很明显这是个Channel的工厂,我们的NioServerSocketChannel应该就是在这里创建的,跟踪进去
可以发现他跳到了ReflectiveChannelFactory类中
@Override
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
发现他是调用了public io.netty.channel.socket.nio.NioServerSocketChannel()构造器,那我们来验证一下在他的构造方法上打个断点
会发现果然调用到这里来了,只是返回的是一个ServerSocketChannel,不过这是netty中的,且NioServerSocketChannel实现了它,那我们来看看jdk中的ServerSocketChannel是怎么创建的呢
很显然他也是调用了同样的方法,所以可以得出他两就是一回事,会一同创建
接下来在看init(channel);
@Override
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
//此处省略代码...
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));
}
});
}
});
}
有些不必要的代码我就没显示出来了,大家可以看到刚刚创建的NioServerSocketChannel在这里立马就往里面添加了一个处理器,但是注意,这里并没有执行,这里我们先在initChannel方法中打一个断点,看看他后续在什么时候调用
接下来就是给NioServerSocketChannel注册一些东西,并且完成线程的切换,也就是主线程在这里会把执行权交给nioEventLoopGroup中的线程
ChannelFuture regFuture = config().group().register(channel);
这里比较长,一直点进去跟踪到io.netty.channel.AbstractChannel类
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
...
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
在这里可以看到,他会首先判断当前的线程是不是nioEventLoopGroup中的线程,这里我们可以想到,他并不是,因为他是主线程,所以会走到下面,会提交一个任务给nioEventLoopGroup的线程来执行,打个断点来验证
会发现果然交给了nioEventLoopGroup中的线程,在这里也完成了线程的切换,注意nioEventLoopGroup中的线程不会一开始就创建好,而是在首次调用时创建,也就是懒加载模式
下面继续跟进
private void register0(ChannelPromise promise) {
try {
//1.绑定选择器
doRegister();
neverRegistered = false;
registered = true;
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
会在doRegister();方法中绑定选择器,点进去
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
这个javaChannel就是我们之前创建好的原生java中的ServerSocketChannel,也就是和NioServerSocketChannel一起创建的
第一个参数就是selector,这已经是在eventLoop中管理了所以可以直接调用,第二个参数就是关注的事件,目前是0表示不关注任何事件,第三个就是附件,表示将来发生了事件谁来处理这个事件,
到这里就完成了原生的channel注册到了netty中的channel上
下面就到了pipeline.invokeHandlerAddedIfNeeded();这一行方法上,大家还记不记得我们之前有一个处理器只添加进去了,但是还没有用,那就是在这里会回调到那个方法上
果然运行到这一行代码上,所以这个initChannel方法是在注册完成之后调用的
接下来会发现他又给我们这个ServerSocketChannel上添加了一个处理器,而这个处理器就是专门处理连接的
然后就是运行到safeSetSuccess(promise)方法上,大家还记不记得我们的主线程执行了final ChannelFuture regFuture = initAndRegister();方法返回了一个regFuture然后添加了一个异步回调方法来执行doBind0(),那什么时候会回调呢,就是当regFuture拿到结果后就会调用,那谁给他这个结果呢,答案就是safeSetSuccess(promise)方法,还有一个疑问就是怎么确定regFuture和这个promise是同一个对象下面我们来验证
在debug模式下给regFuture打上标记如下面两张图
会发现果然一样,那么就会回调执行 doBind0方法
下面跟进doBind0,因为调用链有点长所以我直接跟进到有意义代码的位置
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
doBind(localAddress);就是绑定我们的端口,我们继续跟进去看
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
这里就是判断版本,然后config.getBacklog()就是读取我们的配置,比如服务器的全连接数量,关于全连接我后续会在另一篇博客中解释
现在端口号绑定完了,接下来就是看看这个通道是不是处于激活状态了,如果是的则会通知到我们的处理器,告诉他们该干活了也就是pipeline.fireChannelActive();这行代码
目前为止我们channel中有的处理器有三个head、acceptor、tail只有中间的是我们添加的,另一个是每个channel中自带的,但是这个方法真正让处理器干活的是head处理器,所以我们看到head处理器中的方法
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
我们的channel虽然激活了,但是并没有关注任何事件,所以在这里我们需要给channel绑定一个关注事件也就是在readIfIsAutoRead();方法里,最后就会调用到io.netty.channel.nio.AbstractNioChannel中
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
这里可以看到,他给channel关注了一个accept事件,到这里我们的服务器启动就完成了,下一篇来分析NioEventLoopGroup
如果有不对之处还请指出,希望对你有收获