如果这个文章看不懂的话 , 建议反复阅读 Netty 与 Reactor

开篇立意。引用网友好的建议。看源码要针对性的看,最佳实践就是,带着明确的目的去看源码。抓主要问题,放弃小问题。主要的逻辑理解了,后面再来看。

源码剖析有一个非常重要的原则 —— 针对性原则,当然,这是我起的名字,意思为一定要有一个明确的目标,针对这个特定的目标死磕到底,跟这个目标无关的内容只要看懂大概逻辑就可以了,不能太深陷,否则容易迷失,特别是开源框架的源码,因为要考虑很多兼容性的问题,会有很多奇奇怪怪的代码,针对这些代码,我们可以略微一瞥,或者直接跳过,如果遇到你感兴趣的问题,但又是跟本次目标无关的,可以先记下来,等本次目标完成了,再把记录的问题归类,重新设置新的目标

何为明确的目标?目标一定是可量化的,不能是含糊的,比如,我要看懂 Netty 所有源码,这就是含糊的目标,比如,我要看懂 Netty 服务启动相关的源码,那就相对明确一些,当然,初期能有这么个目标已经不错了。随着研究的深入,会不断发现新的问题,这些新的问题又可以成为新的目标,只有这样才能不断进步。

/*** Echoes back any received data from a client.*/
public final class EchoServer {static final boolean SSL = System.getProperty("ssl") != null;static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));public static void main(String[] args) throws Exception {// Configure SSL.final SslContext sslCtx;if (SSL) {SelfSignedCertificate ssc = new SelfSignedCertificate();sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();} else {sslCtx = null;}// Configure the server.// 1. 声明线程池EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();final EchoServerHandler serverHandler = new EchoServerHandler();try {// 2. 服务端引导器ServerBootstrap b = new ServerBootstrap();// 3. 设置线程池b.group(bossGroup, workerGroup)// 4. 设置ServerSocketChannel的类型.channel(NioServerSocketChannel.class)// 5. 设置参数.option(ChannelOption.SO_BACKLOG, 100)// 6. 设置ServerSocketChannel对应的Handler,只能设置一个.handler(new LoggingHandler(LogLevel.INFO))// 7. 设置SocketChannel对应的Handler.childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();if (sslCtx != null) {p.addLast(sslCtx.newHandler(ch.alloc()));}// 可以添加多个子Handler//p.addLast(new LoggingHandler(LogLevel.INFO));p.addLast(serverHandler);}});// 8. 绑定端口// Start the server.ChannelFuture f = b.bind(PORT).sync();//  Netty 的 Channel 跟 Java 原生的 Channel 是否有某种关系?//  bind () 是否调用了 Java 底层的 Socket 相关的操作?//  Netty 服务启动之后 ChannelPipeline 里面长什么样?// 9. 等待服务端监听端口关闭,这里会阻塞主线程// Wait until the server socket is closed.f.channel().closeFuture().sync();} finally {// 10. 优雅地关闭两个线程池// Shut down all event loops to terminate all threads.bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

Netty启动的时候主要是

ChannelFuture f = serverBootstrap.bind(PORT).sync();

今天我们 针对性问题,就是搞清楚 bind () 相关的问题

  1. Netty 的 Channel 跟 Java 原生的 Channel 是否有某种关系?
  2. bind () 是否调用了 Java 底层的 Socket 相关的操作?
  3. Netty 服务启动之后 ChannelPipeline 里面长什么样?
public ChannelFuture bind(int inetPort) {return bind(new InetSocketAddress(inetPort));
}private ChannelFuture doBind(final SocketAddress localAddress) {// key1,初始化并注册什么呢?final ChannelFuture regFuture = initAndRegister();final Channel channel = regFuture.channel();// 注册失败if (regFuture.cause() != null) {return regFuture;}// 已完成,上面未失败,所以这里是已完成且成功了if (regFuture.isDone()) {// At this point we know that the registration was complete and successful.ChannelPromise promise = channel.newPromise();// key2,绑定什么呢?取决于initAndRegister()中异步执行的快慢,所以不一定到这里,这里可以打一个断点doBind0(regFuture, channel, localAddress, promise);return promise;} else {// 未完成// Registration future is almost always fulfilled already, but just in case it's not.final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);// 设置回调等待完成,回调内部处理注册的结果regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {Throwable cause = future.cause();// 注册失败if (cause != null) {// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an// IllegalStateException once we try to access the EventLoop of the Channel.promise.setFailure(cause);} else {// 注册成功// Registration was successful, so set the correct executor to use.// See https://github.com/netty/netty/issues/2586promise.registered();// key2,绑定什么呢?取决于initAndRegister()中异步执行的快慢,所以不一定到这里,这里可以打一个断点doBind0(regFuture, channel, localAddress, promise);}}});return promise;}}

doBind() 主要干了两件事:
initAndRegister () ,初始化并注册什么呢?
doBind0 () ,到底绑定的是什么?

让我们继续跟进到 initAndRegister() 方法中:

  final ChannelFuture initAndRegister() {Channel channel = null;try {// key1 ,通过 ChannelFactory 创建一个 Channel ,使用反射的形式创建一个 Channel//  反射的类为我们第 4 步中设置的 NioServerSocketChannel.classchannel = channelFactory.newChannel();// key2 ,初始化 Channel ,干了些什么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 GlobalEventExecutorreturn new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);}// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutorreturn new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);}// key3 ,注册 Channel 到哪里?这里有个 group() ,难道是 EventLoopGroup ?ChannelFuture regFuture = config().group().register(channel);//  失败了,关闭 Channelif (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;}

initAndRegister 主要干了三个事:
1 channelFactory.newChannel () ,通过反射的形式创建 Channel ,而且是无参构造方法, new 的时候做了哪些事儿?
2 init(channel) ,初始化 Channel 的什么?
3 register(channel) ,注册 Channel 到哪里?

这里我们可以学到的设计技巧: 1.构建(工厂模式) 2.初始化 3.业务(例如:注册,或者其他)

因为我们这里使用的是 NioServerSocketChannel ,所以,直接查看它的无参构造方法即可。
补充说明:为什么呢?这里有一个前置知识点。默认你是已经很了解Netty的整个原理了啊。不明白的话。建议反复阅读 Netty 与 Reactor
因为NIO啊,启动是NIO,启动一个建立连接的channel ,然后 该channel 管理连接事件。

// 1.  无参构造方法public NioServerSocketChannel() {this(newSocket(DEFAULT_SELECTOR_PROVIDER));}// 1.1  使用 Java 底层的 SelectorProvider 创建一个 Java 原生的 ServerSocketChannel// windows 平台下使用的是 WindowsSelectorProvider ,因为 ServerSocketChannel 是跟操作系统交互的,所以是平台相关的,每个平台下都不一样private static ServerSocketChannel newSocket(SelectorProvider provider) {try {// key ,创建 Java 原生 ServerSocketChannelreturn provider.openServerSocketChannel();} catch (IOException e) {throw new ChannelException("Failed to open a server socket.", e);}}// 1.2  有参构造方法,参数是 Java 原生的 ServerSocketChannelpublic NioServerSocketChannel(ServerSocketChannel channel) {//  调用父类的构造方法,注意 parent 参数为 null// key ,感兴趣的事件为 Accept 事件super(null, channel, SelectionKey.OP_ACCEPT);//  创建 ChannelConfigconfig = new NioServerSocketChannelConfig(this, javaChannel().socket());}// 1.2.1  调用父类构造方法protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {super(parent, ch, readInterestOp);}// 1.2.1.1  调用父类父类的构造方法protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {//  调用父类的构造方法, parent 为 nullsuper(parent);// ch 为 Java 原生的 Channelthis.ch = ch;//  感兴趣的事件,这里为 Accept 事件this.readInterestOp = readInterestOp;try {//  将 channel 设置为非阻塞(是不是跟 NIO 案例中的用法一样?!)ch.configureBlocking(false);} catch (IOException e) {try {ch.close();} catch (IOException e2) {logger.warn("Failed to close a partially initialized socket.", e2);}throw new ChannelException("Failed to enter non-blocking mode.", e);}}// 1.2.1.1.1  调用父类父类父类的构造方法protected AbstractChannel(Channel parent) {//  此时 parent 为 nullthis.parent = parent;//  赋予一个 idid = newId();//  赋值了一个 unsafe ,非 Java 的 Unsafe ,而是 Netty 自己的 Unsafeunsafe = newUnsafe();//  创建 ChannelPipelinepipeline = newChannelPipeline();}// 1.2.1.1.1.1  创建默认的 ChannelPipelineprotected DefaultChannelPipeline(Channel channel) {this.channel = ObjectUtil.checkNotNull(channel, "channel");succeededFuture = new SucceededChannelFuture(channel, null);voidPromise = new VoidChannelPromise(channel, true);// ChannelPipeline 中默认有两个节点, head 和 tail ,且是双向链表tail = new TailContext(this);head = new HeadContext(this);head.next = tail;tail.prev = head;}

到这里 NioServerSocketChannel的创建过程就完毕了,我们简单总结一下:

        1.Netty 的ServerSocketChannel 会与Java 原生的ServerSocketChannel 绑定在一起;2.会注册 Accept事件;3.会为每一个 Channel分配一个 id ;4.会为每一个 Channel创建一个叫作 unsafe 的东西;5.会为每一个 Channel分配一个 ChannelPipeline ;6.ChannelPipeline 中默认存在一个双向链表head<=>tail ;好了,

来到了第二步骤, init(channel) 方法:

主流程的第五步:贴出来给大家伙看

io.netty.bootstrap.ServerBootstrap#init

   @Overridevoid init(Channel channel) {// 将第5步中设置到ServerBootstrap中的Option设置到Channel的Config中,看上图setChannelOptions(channel, newOptionsArray(), logger);// 设置一些属性到Channel中,用法与Option一样setAttributes(channel, newAttributesArray());// 从Channel中取出ChannelPipeline,上面创建的ChannelPipeline p = channel.pipeline();//  子 Channel 的配置,子 Channel 也就是 SocketChannelfinal EventLoopGroup currentChildGroup = childGroup;final ChannelHandler currentChildHandler = childHandler;final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);// 将 ServerBootstrap 中的 Handler 设置到 ChannelPipeline 的最后// ChannelInitializer 的实现原理?p.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(final Channel ch) {final ChannelPipeline pipeline = ch.pipeline();//  第 6 步设置的 HandlerChannelHandler handler = config.handler();if (handler != null) {pipeline.addLast(handler);}//  同时,又向 ChannelPipeline 的最后添加了一个叫作 ServerBootstrapAcceptor 的 Handler//  这是什么写法??有点回调的写法?有点监听器的味道?ch.eventLoop().execute(new Runnable() {@Overridepublic void run() {// 把子 Channel 相关的参数传到这个 Handler 里面,那它是干什么的呢?pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));}});}});}

简而言之:init(channel) 方法整体来说还是比较简单的,就是把 ServerBootstrap 中的配置设置到 Channel 中。

不过依然有几处我们现在可能还不太理解的地方:
ChannelInitializer 的实现原理是什么?
ch.eventLoop().execute() 这是什么写法?
ServerBootstrapAcceptor 是干什么?

我们来到第三步
3 register(channel) ,注册 Channel 到哪里?
好了,我们再来看看 initAndRegister() 方法的最后一个关键步骤, ChannelFuture regFuture = config().group().register(channel); 注册 Channel 到什么地方?

  // key3 ,注册 Channel 到哪里?这里有个 group() ,难道是 EventLoopGroup ?ChannelFuture regFuture = config().group().register(channel);

查看源码,可以发现,这里的 group 就是我们的 bossGroup ,所以这里就是调用 bossGroup 的 register(channel)方法。

@Override
public ChannelFuture register(Channel channel) {return next().register(channel);
}

这里会调用 next() 方法选择出来一个 EventLoop 来注册 Channel ,里面实际上使用的是一个叫做 EventExecutorChooser 的东西来选择,它实际上又有两种实现方式 —— PowerOfTwoEventExecutorChooser 和 GenericEventExecutorChooser ,本质上就是从 EventExecutor 数组中选择一个 EventExecutor ,我们这里就是 NioEventLoop ,那么,它们有什么区别呢?有兴趣的可以点开它们的源码看看,我简单地提一下,本质都是按数组长度取余数 ,不过, 2的 N 次方的形式更高效。

最后,来到了 EventLoop 的 register(channel) 方法:

io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)

// io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
@Override
public ChannelFuture register(Channel channel) {return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {ObjectUtil.checkNotNull(promise, "promise");// key ,调用的是 channel 的 unsafe 的 register() 方法promise.channel().unsafe().register(this, promise);return promise;
}

可以看到,先创建了一个叫做 ChannelPromise 的东西,它是 ChannelFuture 的子类,暂时先把它当作ChannelFuture 来看待。最后,又调回了 Channel 的 Unsafe 的 register () 方法,这里第一个参数是 this ,也就是NioEventLoop ,第二个参数是刚创建的 ChannelPromise 。

io.netty.channel.AbstractChannel.AbstractUnsafe#register

  @Overridepublic final void register(EventLoop eventLoop, final ChannelPromise promise) {//  各种检查,可以跳过ObjectUtil.checkNotNull(eventLoop, "eventLoop");if (isRegistered()) {promise.setFailure(new IllegalStateException("registered to an event loop already"));return;}if (!isCompatible(eventLoop)) {promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));return;}// key1 ,将上面传进来的 EventLoop 绑定到 Channel 上  -》  上一步的是 NioEventLoop,当然还有其他的。多好扩展啊AbstractChannel.this.eventLoop = eventLoop;//  判断当前线程是否跟 EventLoop 线程是同一个if (eventLoop.inEventLoop()) {// key2 ,调用 register0register0(promise);} else {try {// key2 ,调用 register0 ,实际走的是这里,所以这里需要打个断点eventLoop.execute(new Runnable() {@Overridepublic 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);}}}

这个方法主要干了两件事:
1 把 EventLoop 与 Channel 绑定在一起;
AbstractChannel.this.eventLoop = eventLoop;
2 调用 register0 () 方法;
这里又出现了 eventLoop.execute () 这种写法,先忽略它,专注于主要逻辑。
接着,跟踪到 register0() 方法中:

io.netty.channel.AbstractChannel.AbstractUnsafe#register0

  private void register0(ChannelPromise promise) {try {//  判断检查,可以跳过// check if the channel is still open as it could be closed in the mean time when the register// call was outside of the eventLoopif (!promise.setUncancellable() || !ensureOpen(promise)) {return;}boolean firstRegistration = neverRegistered;// key1 ,调用 doRegister() 方法doRegister();neverRegistered = false;registered = true;// key2 ,调用 invokeHandlerAddedIfNeeded()//  触发添加 Handler 的回调,其中 pineline.addLast(ChannelInitializer) 的处理就是在这一步完成的//  这一步之后 pipeline 里面应该是 head<=>LoggineHandler<=>tail//  而 ServerBootstrapAcceptor 还没有加入到 pipeline 中,//  因为它设置了使用 EventLoop 的线程执行,当前线程就是 EventLoop 的线程//  所以,添加 ServerBootstrapAcceptor 会在当前任务执行完毕才会执行// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the// user may already fire events through the pipeline in the ChannelFutureListener.pipeline.invokeHandlerAddedIfNeeded();safeSetSuccess(promise);//  调用 ChannelPineline 的 fireChannelRegistered() ,实际是调用的各个 ChannelHandler 的 channelRegistered() 方法pipeline.fireChannelRegistered();// Only fire a channelActive if the channel has never been registered. This prevents firing// multiple channel actives if the channel is deregistered and re-registered.// Channel 是否已经激活,此时还未绑定到具体的地址,所以还未激活if (isActive()) {if (firstRegistration) {pipeline.fireChannelActive();} else if (config().isAutoRead()) {// This channel was registered before and autoRead() is set. This means we need to begin read// again so that we process inbound data.//// See https://github.com/netty/netty/issues/4805beginRead();}}} catch (Throwable t) {//  异常处理,可以跳过// Close the channel directly to avoid FD leak.closeForcibly();closeFuture.setClosed();safeSetFailure(promise, t);}}

这里有两个个非常重要的方法:
1 doRegister () ,一看就是干正事的方法
2 pipeline.invokeHandlerAddedIfNeeded () ,触发添加 Handler 的回调,其中 pineline.addLast (ChannelInitializer)的处理就是在这一步完成的,有兴趣的同学可以跟踪看一下,这一块我们本不详细展开

先来看 doRegister() 方法:

io.netty.channel.nio.AbstractNioChannel#doRegister

 @Overrideprotected void doRegister() throws Exception {boolean selected = false;for (;;) {try {// key ,将 EventLoop 中的 Selector 与 Java 原生的 Channel 绑定在一起,并返回这个 SelectionKey//  注意,第三个参数是 this ,代表的是当前这个 Netty 中的 Channel ,我们这里就是 NioServerSocketChannel//  它作为 Selection 的 attachment 绑定到 SelectionKey 上,与 JavaChannel 和 Selector 是同一个级别的selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);return;} catch (CancelledKeyException e) {//  异常处理,可以跳过if (!selected) {// Force the Selector to select now as the "canceled" SelectionKey may still be// cached and not removed because no Select.select(..) operation was called yet.eventLoop().selectNow();selected = true;} else {// We forced a select operation on the selector before but the SelectionKey is still cached// for whatever reason. JDK bug ?throw e;}}}}

这里其实就一行关键代码,将 Selector 与 Java 原生 Channel 绑定在一起,并将当前 Netty 的 Channel 通过attachment 的形式绑定到 SelectionKey 上,到这里,你可能会有疑问:为什么要把 Netty 的 Channel 当作附件放到 SelectionKey 中呢?

所以,整个注册的过程主要就干了三个事:

  1. 把 Channel 绑定到一个 EventLoop 上;
  2. 把 Java 原生 Channel 、 Netty 的 Channel 、 Selector 绑定到 SelectionKey 中;
  3. 触发 Register 相关的事件;

// 1. io.netty.bootstrap.AbstractBootstrap#doBind0
至此, initAndRegister() 方法内部就分析完成了,我们再来看看另一个重要方法 doBind0() :

    private static void doBind0(final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise) {//  异步执行// This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up// the pipeline in its channelRegistered() implementation.channel.eventLoop().execute(new Runnable() {@Overridepublic void run() {if (regFuture.isSuccess()) {channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {promise.setFailure(regFuture.cause());}}});}

/ 2. 调用 Channel 的 bind() 方法 io.netty.channel.AbstractChannel#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)

@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {//  调用的是 pipeline 的 bind() 方法
return pipeline.bind(localAddress, promise);
}

// 3. 调用的是 pipeline 的 bind() 方法 io.netty.channel.DefaultChannelPipeline#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)

@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {//  从尾开始调用,也就是 outbound
return tail.bind(localAddress, promise);
}

// 4. 此时 pipeline 中的 Handler 为 head<=>LoggingHandler<=>ServerBootstrapAcceptor<=>tail ,出站的 pineple 实际为 tail=>LoggingHandler=>head ,下面
我只贴主要代码
// 5. io.netty.handler.logging.LoggingHandler#bind

@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {if (logger.isEnabled(internalLevel)) {logger.log(internalLevel, format(ctx, "BIND", localAddress));
}
ctx.bind(localAddress, promise);
}

// 6. io.netty.channel.DefaultChannelPipeline.HeadContext#bind

@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {//  最后调用的是 HeadContext 这个 Handler 中 unsafe 的 bind() 方法
unsafe.bind(localAddress, promise);
}

// 7. io.netty.channel.AbstractChannel.AbstractUnsafe#bind

@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {//  省略其它代码
boolean wasActive = isActive();
try {// key ,绑定地址
doBind(localAddress);
} catch (Throwable t) {safeSetFailure(promise, t);closeIfClosed();return;
}
//  成功激活,调用 pipeline.fireChannelActive() 方法
if (!wasActive && isActive()) {invokeLater(new Runnable() {@Overridepublic void run() {pipeline.fireChannelActive();}
});
}//  设置 promise 为成功状态safeSetSuccess(promise);
}

// 8. 绕了一圈,最后又回到了 NioServerChannel 的 doBind() 方法 io.netty.channel.socket.nio.NioServerSocketChannel#doBind

@SuppressJava6Requirement(reason = "Usage guarded by java version check")
@Override
protected void doBind(SocketAddress localAddress) throws Exception {//  根据不同的 JDK 版本调用不同的方法if (PlatformDependent.javaVersion() >= 7) {//  我使用的 JDK8 版本,所以走到这里了javaChannel().bind(localAddress, config.getBacklog());
} else {javaChannel().socket().bind(localAddress, config.getBacklog());}
}

可以看到, doBind0() 最后也是通过 Java 原生 Channel 的 bind () 方法来实现的。

最后,我们来总结一下整个服务启动的过程,服务启动主要是通过两个主要的大方法来完成的:

  1. initAndRegister (),初始化并注册什么呢?
channelFactory.newChannel()
1 通过反射创建一个 NioServerSocketChannel
2 将 Java 原生 Channel 绑定到 NettyChannel 中
3 注册 Accept 事件
4 为 Channel 分配 id
5 为 Channel 创建 unsafe
6 为 Channel 创建 ChannelPipeline(默认是 head<=>tail 的双向链表)init(channel)
1 把 ServerBootstrap 中的配置设置到 Channel 中
2 添加 ServerBootstrapAcceptor 这个 Handleregister(channel)
1 把 Channel 绑定到一个 EventLoop 上
2 把 Java 原生 Channel、Netty 的 Channel、Selector 绑定到 SelectionKey 中
3 触发 Register 相关的事件

2 doBind0 (),到底绑定的是什么?

通过 Java 原生 Channel 绑定到一个本地地址上

留下来的问题有:

ChannelInitializer 是怎么实现的?
ch.eventLoop ().execute () 这种写法是在干什么?
ServerBootstrapAcceptor 是干什么的?
Netty 使用的还是 Java 原生的 Channel,那么,Selector 在哪里用的?


问题:Netty 的 ServerSocketChannel 会与 Java 原生的 ServerSocketChannel 绑定在一起,这其实就是相当于 用 NioServerSocketChannel 对 java 原生的 ServerSocketChannel 进行了封装吧?

源码剖析 Netty 服务启动 NIO相关推荐

  1. 老李推荐: 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge 1...

    老李推荐: 第8章4节<MonkeyRunner源码剖析>MonkeyRunner启动运行过程-启动AndroidDebugBridge 上一节我们看到在启动AndroidDebugBri ...

  2. 老李推荐:第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数...

    老李推荐:第8章2节<MonkeyRunner源码剖析>MonkeyRunner启动运行过程-解析处理命令行参数 MonkeyRunnerStarter是MonkeyRunner启动时的入 ...

  3. 老李推荐: 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge 2...

    第81-86行,整个方法的主体就是创建一个"Device List Monitor"的线程.线程运行方法run直接调用DeviceMonitor的deviceMonitorLoop ...

  4. 老李推荐:第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数 2...

    我们这一节会先去分析下monkeyrunner是如何对参数进行处理的,我们跳转到MonkeyRunnerOptions这个类里面的processOptions这个方法: 93   public sta ...

  5. 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动6

    这一部分的代码逻辑关系是这样的: 344行: 一个外部循环每次从上次保存下来的设备列表获得一个设备Device实例 350行: 再在一个内部循环从最新的设备列表中获得一个设备Device实例 353行 ...

  6. 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动 8

    这个方法所做的事情就是: 446行: 首先通过Device类的executeShellCommand方法发送类似"adb shell getprop"的命令去获得所有支持的系统属性 ...

  7. CC00055.hadoop——|HadoopMapReduce.V27|——|Hadoop.v27|源码剖析|DataNode启动流程|

    一.[源码剖析之DataNode启动流程] :DataNode 启动流程 ### --- datanode的Main Class是DataNode,先找到DataNode.main()public c ...

  8. 4.2.10 Kafka源码剖析, 阅读环境搭建, broker启动流程, topic创建流程, Producer生产者流程, Consumer消费者流程,

    目录 4.1 Kafka源码剖析之源码阅读环境搭建 4.1.1 安装配置Gradle 4.1.2 Scala的安装和配置 4.1.3 Idea配置 4.1.4 源码操作 4.2 Kafka源码剖析之B ...

  9. Netty服务器启动源码剖析

    Netty服务器启动源码剖析 文章目录 Netty服务器启动源码剖析 1.Netty服务器启动源码剖析 1.1.执行new NioEventLoopGroup()时发生了什么 1.1.1.NioEve ...

最新文章

  1. python+opencv选出视频中一帧再利用鼠标回调实现图像上画矩形框
  2. C# MainWindowHandle为0的解决方法
  3. python 2 3 读写中文文件 使用codecs最方便
  4. [学习笔记]圆方树广义圆方树
  5. 六十九、Springboot整合JDBC,连接Mysql
  6. 浅析三种特殊进程:孤儿进程,僵尸进程和守护进程
  7. yarn安装依赖包报错 error An unexpected error occurred: “https://registry.npm.taobao.orgnpm/element-ui: get
  8. resin php extensions sockets,linux 有关笔记
  9. 【python基础知识】对文本数据库的一些基本操作
  10. lisp将图元追加选择_DNF:哈林防具和海博伦如何选择首饰提升率最大?你选对了吗?...
  11. Android数据存储——内部存储
  12. 2014年6月计算机二级c语言答案,2014年计算机二级C语言真题及答案(4)
  13. jtag接口_USB接口—Xilinx—JTAG烧写器(电路图、PCB片段、烧写器配置固件)设计方案...
  14. 云南昆明企业等保合规安全解决方案、等保二级、等保三级解决方案
  15. java面试 socket_java网络编程面试题
  16. 信度spss怎么做_毕业季:毕业论文利用spss做信度分析步骤详解
  17. 广西一男子酒后肇事逃逸 致环卫工人被撞身亡(图)
  18. 台式计算机怎么开声音,台式电脑没有声音【设置办法】
  19. 启动jupyter notebook 报错:ImportError:DLL load failed,找不到指定模块的解决办法
  20. 第八届育才杯机器人比赛_太子湾学校:育才教育集团第五届“星升代·育才杯”校园文学大赛颁奖暨文学“微创作”比赛在太子湾学校隆重举行...

热门文章

  1. Ranger知识地图
  2. ASP.NET Core 设置允许跨域访问
  3. DOM(二)——修改内容、属性、样式
  4. redis相关(搭建和数据落盘)
  5. JS权威指南阅读笔记
  6. Django在根据models生成数据库表时报 __init__() missing 1 required positional argument 'on_d...
  7. Deep learning:十(PCA和whitening)
  8. 记录一下flex布局左边固定,右边100%
  9. matlab练习程序(图像放大/缩小,放大没有进行插值操作)
  10. Algs4-2.3.8Quick.sort()在处理N个全部重复的元素时比较次数