Netty源码—二、server启动(2)

我们在使用Netty的时候的初始化代码一般如下

EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 配置服务器的NIO线程组 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChildChannelHandler()); // 绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); // 等待服务端监听端口关闭 f.channel().closeFuture().sync(); } finally { // 优雅退出,释放线程池资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }

前面已经说过线程池的启动过程,接下来就是通过builder模式构造启动参数,接下来看看bind的过程。channel的注册和ip:port的绑定都是在bind方法中进行的,bind方法的主要逻辑是

初始化channel

channel注册到selector

NioServerSocketChannel

先看看channel的初始化,server端使用的NioServerSocketChannel封装了JDK的ServerSocketChannel,初始化过程如下:

// 配置使用的channel的时候会指定对应的channelFactory public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); } final ChannelFuture initAndRegister() { Channel channel = null; try { // channelFactory是ReflectiveChannelFactory channel = channelFactory.newChannel(); init(channel); } catch (Throwable t) { if (channel != null) { // channel can be null if newChannel crashed (eg SocketException("too many open files")) channel.unsafe().closeForcibly(); // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t); } // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor 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(); } } // If we are here and the promise is not failed, it's one of the following cases: // 1) If we attempted registration from the event loop, the registration has been completed at this point. // i.e. It's safe to attempt bind() or connect() now because the channel has been registered. // 2) If we attempted registration from the other thread, the registration request has been successfully // added to the event loop's task queue for later execution. // i.e. It's safe to attempt bind() or connect() now: // because bind() or connect() will be executed *after* the scheduled registration task is executed // because register(), bind(), and connect() are all bound to the same thread. return regFuture; }

上面使用的是io.netty.channel.ReflectiveChannelFactory#newChannel来创建channel,利用反射创建实例,使用的是NioServerSocketChannel的无参构造方法,在午无参造方法中调用newChannel

// 创建serverChannel的时候先调用newSocket,然后调用下面的构造方法 public NioServerSocketChannel(ServerSocketChannel channel) { // 设置当前socket监听的事件,由于是server一定要添加accept事件 super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); } // io.netty.channel.socket.nio.NioServerSocketChannel#newSocket private static ServerSocketChannel newSocket(SelectorProvider provider) { try { /** * Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in * {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise. * * See <a href="http://github.com/netty/netty/issues/2308">#2308</a>. */ return provider.openServerSocketChannel(); } catch (IOException e) { throw new ChannelException( "Failed to open a server socket.", e); } } ServerSocketChannelImpl(SelectorProvider sp) throws IOException { super(sp); // 创建一个socket,返回的是socket对应的文件描述符 this.fd = Net.serverSocket(true); this.fdVal = IOUtil.fdVal(fd); this.state = ST_INUSE; } // sun.nio.ch.Net#serverSocket static FileDescriptor serverSocket(boolean stream) { // socket0是一个native方法,返回的是int类型的linux的文件描述符,使用newFD转化为Java的文件描述符 return IOUtil.newFD(socket0(isIPv6Available(), stream, true)); } // jdk/src/solaris/native/sun/nio/ch/Net.c JNIEXPORT int JNICALL Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, jboolean stream, jboolean reuse) { // 省略中间代码... // 调用socket方法创建一个socket,并返回对应的文件描述符 fd = socket(domain, type, 0); if (fd < 0) { return handleSocketError(env, errno); } // 省略中间代码... return fd; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfjxw.html