先看一下我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即可。

先了解这些,具体更加详细的内容,我们后面再介绍

 
好了,讲到这里,Channel创建完成。

 

Netty源码分析--创建Channel(三)的更多相关文章

  1. Netty源码分析之NioEventLoop(三)—NioEventLoop的执行

    前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...

  2. [编织消息框架][netty源码分析]8 Channel 实现类NioSocketChannel职责与实现

    Unsafe是托委访问socket,那么Channel是直接提供给开发者使用的 Channel 主要有两个实现 NioServerSocketChannel同NioSocketChannel 致于其它 ...

  3. Netty源码分析-- 处理客户端接入请求(八)

    这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客 ...

  4. Netty源码分析--内存模型(上)(十一)

    前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们 ...

  5. netty源码分析系列文章

    netty源码分析系列文章 nettynetty源码阅读netty源码分析  想在年终之际将对netty研究的笔记记录下来,先看netty3,然后有时间了再写netty4的,希望对大家有所帮助,这个是 ...

  6. Netty源码分析第2章(NioEventLoop)---->第1节: NioEventLoopGroup之创建线程执行器

    Netty源码分析第二章: NioEventLoop 概述: 通过上一章的学习, 我们了解了Server启动的大致流程, 有很多组件与模块并没有细讲, 从这个章开始, 我们开始详细剖析netty的各个 ...

  7. Netty源码分析第3章(客户端接入流程)---->第2节: 处理接入事件之handle的创建

    Netty源码分析第三章: 客户端接入流程 第二节: 处理接入事件之handle的创建 上一小节我们剖析完成了与channel绑定的ChannelConfig初始化相关的流程, 这一小节继续剖析客户端 ...

  8. Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建

    Netty源码分析第三章: 客户端接入流程 第三节: NioSocketChannel的创建 回到上一小节的read()方法: public void read() { //必须是NioEventLo ...

  9. Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建

    Netty源码分析第一章:  Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...

随机推荐

  1. Leetcode 171 Excel Sheet Column Number 字符串处理

    本质是把26进制转化为10进制 A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 class Solution ...

  2. 简明Python3教程 3.介绍

    介绍 Python是少有的几种既强大又简单的编程语言.你将惊喜地发现通过使用Python即可轻松专注于解决问题而非和你所用的语言格式与结构. 下面是Python的官方介绍: Python is an ...

  3. js -- 捆绑

    1.环境配置 主要參考网址: http://cocos2d.cocoachina.com/bbs/forum.php?mod=viewthread&tid=10226&extra=pa ...

  4. OpenGL(二十三) 各向异性纹理过滤

    如果使用一般的纹理过滤,当观察方向跟模型表面不是相互垂直的的情况下,会出现纹理信息的丢失,表现为图像看上去比较模糊,如下图所示,远处场景的细节信息很差: 针对这种情况,可以采用同向异性过滤的方式处理纹 ...

  5. Oltu在Jersey框架上实现oauth2.0授权模块

    oltu是一个开源的oauth2.0协议的实现,本人在此开源项目的基础上进行修改,实现一个自定义的oauth2.0模块. 关于oltu的使用大家可以看这里:http://oltu.apache.org ...

  6. OpenGL(九) 三维混色和深度缓存设置

    颜色的混合在现实世界中非常常见,例如隔着有色玻璃观看物体,此时在观察者严重呈现出来物体的颜色就是玻璃的颜色和物体的颜色的混合. OpenGL在RGBA颜色模式下使用函数glenable(GL_BLEN ...

  7. 最简单的IdentityServer实现——项目基本结构与流程

    项目结构 共分为三个组成部分: IdentityServer:用于登录.身份认证与授权 Api:提供获得授权后调用的各接口 Client(客户端,控制台):访问IdentityServer授权,再访问 ...

  8. Selenium-简介

    一.简介 Selenium是UI自动化的一个框架. Selenium1.0时代就是用js注入技术与浏览器交互. Selenium WebDriver就是调用浏览器原生的API来实现的操作.他是Clie ...

  9. 基于Netbeans的安卓Android开发环境配置 - CSDN博客

    原文:基于Netbeans的安卓Android开发环境配置 - CSDN博客 基于Netbeans的安卓Android开发环境配置 一.准备工作 NetBeans 勾选网页中的Accept-选择对应系 ...

  10. 随机森林分类器(Random Forest)

    阅读目录 1 什么是随机森林? 2 随机森林的特点 3 随机森林的相关基础知识 4 随机森林的生成 5 袋外错误率(oob error) 6 随机森林工作原理解释的一个简单例子 7 随机森林的Pyth ...