撸了一天Netty的源码,我凌乱了

目录

传统NIO初始化步骤

Netty初始化时序图

Netty初始化入口

AbstractBootstrap

ReflectiveChannelFactory

NioServerSocketChannel

newSocket()  --- 重要

this

AbstractNioChannel -- 重要

ServerBootstrap

ChannelFuture regFuture = this.config().group().register(channel);  --- 重要

doBind0() -- 重要

总结

 


传统NIO初始化步骤

          1.  ServerSocketChannel server = ServerSocketChannel.open();
            // 通知服务地址IP/Port
          2.  server.bind(new InetSocketAddress(this.port));
            // NIO兼容BIO 默认是阻塞的IO模型 所以要通知server开启非阻塞IO模型
          3.  server.configureBlocking(false); // 是否阻塞设为false
            // 大堂经理准备就绪 接客
          4.  selector = Selector.open();
            // 注册 等待
          5.  server.register(selector, SelectionKey.OP_ACCEPT);

以上是传统NIO初始化的常规步骤,而Netty实质是对传统NIO模型的高度封装,所以Netty的底层调用的其实还是NIO的相关API。

Netty初始化时序图

Netty初始化入口

// Netty入口   port 绑定端口,当不设置IP时,默认使用 localHost 作为IP地址  
// 服务绑定端口
ChannelFuture future = server.bind(port).sync();

AbstractBootstrap

1.
public ChannelFuture bind(int inetPort) {
    return this.bind(new InetSocketAddress(inetPort));
}

2.
public ChannelFuture bind(SocketAddress localAddress) {
    this.validate();
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    } else {
        return this.doBind(localAddress);
    }
}

3.
// 真正调用的方法 doBind() 方法  
private ChannelFuture doBind(final SocketAddress localAddress) {
    // 初始和注册  (核心)
    final ChannelFuture regFuture = this.initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    } else if (regFuture.isDone()) {
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                Throwable cause = future.cause();
                if (cause != null) {
                    promise.setFailure(cause);
                } else {
                    promise.registered();
                    AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
                }

            }
        });
        return promise;
    }
}

4.
final ChannelFuture initAndRegister() {
    Channel channel = null;

    try {
        // 创建Channel 
        channel = this.channelFactory.newChannel();
        // 初始化
        this.init(channel);
    } catch (Throwable var3) {
        if (channel != null) {
            channel.unsafe().closeForcibly();
        }

        return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
    }
    // 注册
    ChannelFuture regFuture = this.config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }

    return regFuture;
}

ReflectiveChannelFactory

public T newChannel() {
    try {
        // 实质就是利用反射创建出 chaneel
        return (Channel)this.clazz.newInstance();
    } catch (Throwable var2) {
        throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
    }
}

那么这个反射出来的Channel到底是个什么呢?ServerBootStrap在初始化的时候我们设置过一个Channel信息,我们大胆擦测一下,这个是不是就是这里反射出来的呢?

我们可以发现,这个实质调用的是AbstractBootstrap的方法,

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    } else {
        // 这里看着有设置参数的意思
        // new ReflectiveChannelFactory(channelClass)) 这个不就是上文的类
        return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
    }
}

new ReflectiveChannelFactory(channelClass)) 这个方法很眼熟,这不就是我们上面的工厂类么?点进去发现,原来在这里类信息已经加载掉了

public ReflectiveChannelFactory(Class<? extends T> clazz) {
    if (clazz == null) {
        throw new NullPointerException("clazz");
    } else {
        // 在这里类信息已经加载了
        this.clazz = clazz;
    }
}

紧跟着,反射其实实质调用的是该类的无参构造,我们点入这个类的无参构造

NioServerSocketChannel

 

这个无参构造,其实里面有两个核心的方法,

newSocket()  --- 重要

// 创建出JAVA的核心Channel  对应文章NIO原生API中的 步骤1
private static java.nio.channels.ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        //这里创建JDK的核心Channel
        return provider.openServerSocketChannel();
    } catch (IOException var2) {
        throw new ChannelException("Failed to open a server socket.", var2);
    }
}

this

public NioServerSocketChannel(java.nio.channels.ServerSocketChannel channel) {
    //这里有两个重要的步骤 1 
    super((Channel)null, channel, 16);
    // 设置 congfig 之后会用到
    this.config = new NioServerSocketChannel.NioServerSocketChannelConfig(this, this.javaChannel().socket());
}

AbstractNioChannel -- 重要

 

这里对应的文章开头的3,开启NIO,设置BIO为false

ServerBootstrap

这里是Channel初始化完成,并开启了NIO模式,接下来就是config的设置

这里的config就对应上文中的 config,而options就是我们在server创建中所设置的一些参数,会在这里循环放入到config中,channel中的 attr参数是一个拓展参数,可以将我们需要用的属性,赋值在这里

 

以上部分就是初始化完成,接下来进入注册的环节

ChannelFuture regFuture = this.config().group().register(channel);  --- 重要

这个方法在跟踪之后,实质调用的是 AbstractNioChannel 中的 doRegister方法

protected void doRegister() throws Exception {
    boolean selected = false;

    while(true) {
        try {
            // 这里完成了真正的注册 
            this.selectionKey = this.javaChannel().register(this.eventLoop().selector, 0, this);
            return;
        } catch (CancelledKeyException var3) {
            if (selected) {
                throw var3;
            }

            this.eventLoop().selectNow();
            selected = true;
        }
    }
}

this.javaChannel().register(this.eventLoop().selector, 0, this); 这个方法实质就是文章开头的 5 ,注册

 

注册完成之后,就剩最后一步,开启IP和端口, 这个比较深,我也是找了很久才找到这个方法 ,基本是找的凌乱了 

文章开头中,我们找到AbstractBootstrap中的doBind()方法,这个方法中调用了一个doBind0()方法,这个方法,也是一个重要的方法

doBind0() -- 重要

private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
    channel.eventLoop().execute(new Runnable() {
        public void run() {
            if (regFuture.isSuccess()) {
                // 这里进行IP和端口的绑定
                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
                promise.setFailure(regFuture.cause());
            }

        }
    });
}

这里的bind的调用实质调用的是 NioServerSocketChannel 中的 doBind()方法

protected void doBind(SocketAddress localAddress) throws Exception {
    // 这里对应的是文章开头的 2  
    // 判断当前JDK版本 决定用哪个IO模型
    if (PlatformDependent.javaVersion() >= 7) {
        this.javaChannel().bind(localAddress, this.config.getBacklog());
    } else {
        this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
    }

}

总结

至此,Netty完成NIO的初始化已经全部完成,Netty封装的层级确实很深,找这些步骤真是很不容易,不画出这个时序图和与原生API对比,基本都是云里雾里没有任何头绪

之后的博文还会继续更新Netty源码的进一步分析。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值