Netty源码分析--创建Channel(三)
先看一下我Netty的启动类
private void start() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO))
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.MINUTES));
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(ChannelRequestProto.ChannelRequest.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new HeartBeatServerHandler());
ch.pipeline().addLast(new XtsCoreServerHandler());
}
});
ChannelFuture future = bootstrap.bind().sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
}
Netty先创建了两个事件循环组 EventLoopGroup ,这个就对应了上文提到的模型, 第一个事件循环组的职责是 负责接收新的客户端连接 并 把客户端Channel注册到多路复用器上面。 第二个事件循环组的职责是 处理客户端的读写等事件。
Netty使用 ServerBootstrap 这个链式编程的方式把启动服务端的代码给串联起来,使用非常方便和优雅。所以我们在看源码的同时也要去好好品读Netty源码书写的巧妙,理解设计思想和方式,巧妙地运用到我们自己的代码中。接下来我会把源码贴出来,并把重要的部分进行解释和着重指出
看下 group 方法
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup); // 第一个事件循环组,我们叫父循环组吧,它是继续调用了父类的group方法,也就是 AbstractBootstrap 并且赋给 group 成员变量
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup; // 第二个事件循环组, 我们叫子循环组吧, 直接赋值给 childGroup 这个成员变量
return this; // 返回自己,这就是链式编程的原因
}
接下来是 channel 方法, 设置了 channal 的类型是 NioServerSocketChannel (当然客户端是 NioSocketChannel),这里在创建的时候用了反射的方法来创建实例,后面涉及到再具体说。
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); // NioServerSocketChannel 使用了 ReflectiveChannelFactory 工厂封装,并且设置到了 AbstractBootstrap
// 的 channelFactory 的这个成员变量中, 注意 这里的channal 是 父级 的 channal
}
.option 方法 是在设置 父级 的一些参数信息,这些就不说了, 当然这里 .handler(new LoggingHandler(LogLevel.INFO)) 方法 也是为父级的事件循环器设置的 handler 。
.childHandler(new ChannelInitializer<SocketChannel>() { // 当然这里是设置 子事件循环组器的 handlers ,但是这里的 initChannel 方法不是在这里调用的,这个后面会具体提到。
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.MINUTES));
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(ChannelRequestProto.ChannelRequest.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new HeartBeatServerHandler());
ch.pipeline().addLast(new XtsCoreServerHandler());
}
});
好了,接下来进入正式部分。
ChannelFuture future = bootstrap.bind().sync(); // 就是从这里的bind()进入
进入doBind方法
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
... 省略一大波代码
}
进入 initAndRegister
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel(); // 这里就是创建一个父级的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 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);
}
...省略一大波代码
}

这就是我前面为什么着重指出 NioServerSocketChannel 使用了 ReflectiveChannelFactory 工厂封装 , 就是上图这里了。然后进入 ReflectiveChannelFactory

使用了 NioServerSocketChannel 的无参构造方法实例化这个类。ok,那我们来看下NioServerSocketChannel 的 无参构造方法。

传入一个默认的多路复用器创建器


使用它来调用 openServerSocketChannel() 方法来创建一个ServerSocketChannel, 具体这个方法就是NIO里面的内容了,有兴趣的自己去看一下。
看到这里,我们知道channel创建完成(为父级使用),返回到这里

大家千万不要忽视一点,这里有个this,我当时就没注意到这里,粗心了,导致其中有一步始终想不通,后来重新仔细看的时候,打自己的心都有了。
这里继续调用了另外一个有参的构造方法。

不断调用父类构造方法,就进入到

这里设置了父级的成员变量channel,并且把感兴趣的key设置为16(接收新的客户端),并且设置非阻塞。这里在第一篇启动NIO服务端的时候,也有这句,大家应该也记得。
我们继续看调用的父类构造方法。

我们知道了为Channel设置了一个ID,并且创建了Pipleline.并且初始化了两个上下文分别为头和尾,通过链表链接。

我们说到这里,简单回顾一下Pipeline. 我们开看下ChannelPipeline官方说明

讲到了, Pipeline 是 Channel中出站和入站操作的处理器或拦截器的一个列表。同时官方给出了一个表单我也贴出来

下图说明了I/O读写事件是怎么在PipeLine中的Handlers中传递的。需要通过 ChannelHandlerContext, 例如 ChannelHandlerContext#fireChannelRead(Object) 和 ChannelHandlerContext#write(Object)

这点了解Netty的一下子应该就看得明白,后面设计到PipeLine的地方我们再展开讲解,继续回到NioServerSocketChannel 的有参构造方法,继续往下看。

这里为刚刚创建的channel创建了一个配置类,并且是一个内部类。传入了channel和套接字。
不断往下跟,看到这里

这里传入了一个小内存分配器,也就是说为这个channel初始化了一个分配器。
ok,我们来简单说下这个分配器,后面在Netty的内存模型部分,我们再细说。

构造方法,传入了三个默认值,并且说明了 默认分配缓冲区的大小为1024 ,最小是64,最大是65536
通篇看一下,看到了一个非常重要的静态代码块

依次往sizeTable添加元素:[16 , (512-16)]之间16的倍数。即,16、32、48...496
然后再往sizeTable中添加元素:[512 , 512 * (2^N)),N > 1; 直到数值超过Integer的限制(2^31 - 1);
根据sizeTable长度构建一个静态成员常量数组SIZE_TABLE,并将sizeTable中的元素赋值给SIZE_TABLE数组。注意List是有序的,所以是根据插入元素的顺序依次的赋值给SIZE_TABLE,SIZE_TABLE从下标0开始。SIZE_TABLE为预定义好的以从小到大的顺序设定的可分配缓冲区的大小值的数组。因为AdaptiveRecvByteBufAllocator作用是可自动适配每次读事件使用的buffer的大小。这样当需要对buffer大小做调整时,只要根据一定逻辑从SIZE_TABLE中取出值,然后根据该值创建新buffer即可。
先了解这些,具体更加详细的内容,我们后面再介绍
Netty源码分析--创建Channel(三)的更多相关文章
- Netty源码分析之NioEventLoop(三)—NioEventLoop的执行
前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...
- [编织消息框架][netty源码分析]8 Channel 实现类NioSocketChannel职责与实现
Unsafe是托委访问socket,那么Channel是直接提供给开发者使用的 Channel 主要有两个实现 NioServerSocketChannel同NioSocketChannel 致于其它 ...
- Netty源码分析-- 处理客户端接入请求(八)
这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客 ...
- Netty源码分析--内存模型(上)(十一)
前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们 ...
- netty源码分析系列文章
netty源码分析系列文章 nettynetty源码阅读netty源码分析 想在年终之际将对netty研究的笔记记录下来,先看netty3,然后有时间了再写netty4的,希望对大家有所帮助,这个是 ...
- Netty源码分析第2章(NioEventLoop)---->第1节: NioEventLoopGroup之创建线程执行器
Netty源码分析第二章: NioEventLoop 概述: 通过上一章的学习, 我们了解了Server启动的大致流程, 有很多组件与模块并没有细讲, 从这个章开始, 我们开始详细剖析netty的各个 ...
- Netty源码分析第3章(客户端接入流程)---->第2节: 处理接入事件之handle的创建
Netty源码分析第三章: 客户端接入流程 第二节: 处理接入事件之handle的创建 上一小节我们剖析完成了与channel绑定的ChannelConfig初始化相关的流程, 这一小节继续剖析客户端 ...
- Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建
Netty源码分析第三章: 客户端接入流程 第三节: NioSocketChannel的创建 回到上一小节的read()方法: public void read() { //必须是NioEventLo ...
- Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建
Netty源码分析第一章: Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...
随机推荐
- Android子线程中更新UI的4种方法
方法一:用Handler 1.主线程中定义Handler: Handler mHandler = new Handler() { @Override public void handleMessage ...
- WPF 曲线图表控件(自制)(一)
原文:WPF 曲线图表控件(自制)(一) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/koloumi/article/details/775092 ...
- Python: 文件操作与数据读取
文件及目录操作 python中对文件.文件夹(文件操作函数)的操作需要涉及到os模块,主要用到的几个函数是, import os 返回指定目录下的所有文件和目录名: os.listdir() 重命名: ...
- DDD实战8_2 利用Unity依赖注入,实现接口对应实现类的可配置
1.在Util类库下新建DIService类 /// <summary> /// 创建一个类,对应在配置文件中配置的DIServices里面的对象的 key /// </summar ...
- Windows10的Ubuntu子系统开启桌面环境
原文:Windows10的Ubuntu子系统开启桌面环境 Ubuntu 优势之一就是桌面环境比较好,所以咱们的子系统当然也不能少了这一环节,本小结开始安装Ubuntu 桌面系统. 安装环境 使用下面指 ...
- 给WPF示例图形加上方便查看大小的格子
原文:给WPF示例图形加上方便查看大小的格子 有时,我们为了方便查看WPF图形的样式及比例等,需要一些辅助性的格线,置于图形.图像的背景中. 比如下图,就是为了更清晰地查看折线的图形,我们画了用于标示 ...
- 自学 Python
如何系统地自学 Python? 最近开始系统的学习Python,以及整理的一些资料.github记录着个人自学 Python 的过程,持续更新.欢迎大家一起来完善这个自学Python学习的项目,给 ...
- WPF: Creation of Text Labels for 3D Scene
原文:WPF: Creation of Text Labels for 3D Scene 转载:http://www.codeproject.com/KB/WPF/WPF_Text3D.aspx Do ...
- VirtualBox虚拟机网络环境Host-Only(对Win10和VirtualBox都有截图)
之前在选择配置虚拟机网络环境的时候 桥接也是不错的,但是自己在使用的时候由于访问频繁会出现断网现象.所以就开始使用Host-Only模式.开始并不是很明白为什么这么设置,也挖了很多坑.经常出现虚拟机无 ...
- 跟我学ASP.NET MVC之十一:URL路由
摘要: 在MVC框架之前,ASP.NET假定在请求的URLs和服务器硬盘文件之间有直接的关系.服务器的职责是接收浏览器请求,从相应的文件发送输出. 这种方法只能工作于Web表单,每一个ASPX页面既是 ...