其中provider.openServerSocketChannel()
就是调用底层 JDK 的 API,获取了 JDK 底层的java.nio.channels.ServerSocketChannel
。
通过super(null, channel, SelectionKey.OP_ACCEPT);
一路跟踪进去,进入AbstractNioChannel
中,
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {super(parent);……ch.configureBlocking(false);……}
关键代码是ch.configureBlocking(false)
, 设置 I/O 模型为非阻塞模式 。
通过super(parent)
跟踪上去,
protected AbstractChannel(Channel parent) {this.parent = parent;id = newId();unsafe = newUnsafe();pipeline = newChannelPipeline();}
其中 id 是 Netty 中每条 Channel 的唯一标识 。
以上就是服务端 Channel 的创建过程 。
接下来是服务端 Channel 的初始化过程,回到AbstractBootstrap.initAndResgister()
方法
……final ChannelFuture initAndRegister() {……// channel 的新建channel = channelFactory.newChannel();// channel 的初始化init(channel);……}……
其中的init(channel)
方法就是服务端的 Channel 的初始化过程,Debug 进入,发现是调用了ServerBootstrap.init(channel)
方法,
@Overridevoid init(Channel channel) {……// 设置一些 Channel 的属性和配置信息……p.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(final Channel ch) {final ChannelPipeline pipeline = ch.pipeline();ChannelHandler handler = config.handler();if (handler != null) {pipeline.addLast(handler);}ch.eventLoop().execute(new Runnable() {@Overridepublic void run() {pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));}});}});}
其核心代码如上 , 主要用于定义服务端启动过程中需要执行哪些逻辑 。主要分为两块:
- 一块是添加用户自定义的处理逻辑到服务端启动流程 。
- 另一块是添加一个特殊的处理逻辑,ServerBootstrapAcceptor 是一个接入器,接受新请求 , 把新的请求传递给某个事件循环器 。
@Overrideprotected void doRegister() throws Exception {boolean selected = false;for (;;) {try {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;}}}}
在这个步骤中 , 我们可以看到关于 JDK 底层的操作selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
首先拿到在前面过程中创建的 JDK 底层的 Channel , 然后调用 JDK 的 register() 方法,将 this 也即 NioServerSocketChannel 对象当作 attachment 绑定到 JDK 的 Selector 上 , 这样后续从 Selector 拿到对应的事件之后,就可以把 Netty 领域的 Channel 拿出来 。接下来是服务端绑定端口的逻辑,见
AbstractBootstrap
中的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());}}});}
图例本文所有图例见:processon: Netty学习笔记代码hello-netty
更多内容见:Netty专栏
参考资料跟闪电侠学 Netty:Netty 即时聊天实战与底层原理
深度解析Netty源码
【五 Netty 学习:服务端启动核心流程源码说明】
推荐阅读
- 原神冒险家试炼第五天怎么过
- 剑南春和五粮液哪个好喝_剑南春和五粮液的口感区别
- 【前端必会】走进webpack生命周期,另类的学习方法
- opencvcv.line
- 骰子五个点怎么玩(骰子五个六五个七怎么玩)
- 三十六 Java开发学习----SpringBoot三种配置文件解析
- 4 MySQL学习---MySQL索引
- 五大珍贵树木 中国名贵树排名
- 基础&进阶 线段树学习笔记(一) | P3372 【模板】线段树 1 题解
- 苹果截屏的五种方法(iphone快速截图方法)