Netty源码分析第3章(客户端接入流程)---->第1节: 初始化NioSockectChannelConfig

Netty源码分析第三章: 客户端接入流程

概述:

之前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件之后是如何处理的?这一章我们循序渐进, 带大家继续剖析客户端接入之后的相关逻辑

第一节:初始化NioSockectChannelConfig

在剖析接入流程之前我们首先补充下第一章有关创建channel的知识:

我们在第一章剖析过channel的创建, 其中NioServerSocketChannel中有个构造方法:

public NioServerSocketChannel(ServerSocketChannel channel) {super(null, channel, SelectionKey.OP_ACCEPT);config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

当时我们并没有剖析config相关知识, 在这一章首先对此做一个补充, 这里我们看到每一个NioServerSocketChannel都拥有一个config属性, 这个属性存放着NioServerSocketChannel的相关配置, 这里创建一个NioServerSocketChannelConfig对象, 并将当前channel, 和channel对应的java底层的socket对象进行了传入, NioServerSocketChannelConfig其实是NioServerSocketChannel的内部类

我们跟到NioServerSocketChannelConfig类的构造方法中:

private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {super(channel, javaSocket);
}

我们继续跟入其父类DefaultServerSocketChannelConfig的构造方法中:

public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {super(channel);if (javaSocket == null) {throw new NullPointerException("javaSocket");}this.javaSocket = javaSocket;
}

这里继续调用了其父类的构造方法, 并保存了jdk底层的socket对象, 并且调用其父类DefaultChannelConfig的构造方法

跟到其父类DefaultChannelConfig的构造方法中:

public DefaultChannelConfig(Channel channel) {this(channel, new AdaptiveRecvByteBufAllocator());
}

这里调用了自身的构造方法, 传入了channel和一个AdaptiveRecvByteBufAllocator对象

AdaptiveRecvByteBufAllocator是一个缓冲区分配器, 用于分配一个缓冲区Bytebuf的, 有关Bytebuf的相关内容会在后面的章节详细讲解, 这里可以简单介绍作为了解, 就当对于之后知识的预习

Bytebuf相当于jdk的ByetBuffer, Netty对其做了重新的封装, 用于读写channel中的字节流, 熟悉Nio的同学对此应该并不陌生, AdaptiveRecvByteBufAllocator就是用于分配netty中ByetBuff的缓冲区分配器, 根据名字, 我们不难看出这个缓冲区是一个可变大小的字节缓冲区

我们跟到AdaptiveRecvByteBufAllocator的构造方法中:

public AdaptiveRecvByteBufAllocator() {//DEFAULT_MINIMUM:最小缓冲区长度64字节//DEFAULT_INITIAL:初始容量1024字节//最大容量65536字节this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
}

这里调用自身的构造方法并且传入了三个属性, 这三个属性的含义分别为:

DEFAULT_MINIMUM:代表要分配的缓冲区长度最少为64个字节

DEFAULT_INITIAL:代表要分配的缓冲区的初始容量为1024个字节

DEFAULT_MAXIMUM:代表要分配的缓冲区最大容量为65536个字节

我们跟到this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM)方法中

public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {//忽略验证代码//最小容量在table中的下标int minIndex = getSizeTableIndex(minimum);if (SIZE_TABLE[minIndex] < minimum) {this.minIndex = minIndex + 1;} else {this.minIndex = minIndex;}//最大容量在table中的下标int maxIndex = getSizeTableIndex(maximum);if (SIZE_TABLE[maxIndex] > maximum) {this.maxIndex = maxIndex - 1;} else {this.maxIndex = maxIndex;}this.initial = initial;
}

其中这里初始化了三个属性, 分别是:

minIndex:最小容量在size_table中的下标

maxIndex:最大容量在table中的下标

initial:初始容量1024个字节

这里的size_table就是一个数组, 里面盛放着byteBuf可分配的内存大小的集合, 分配的bytebuf无论是扩容还是收缩, 内存大小都属于size_table中的元素, 那么这个数组是如何初始化的, 我们跟到这个属性中:

private static final int[] SIZE_TABLE;

我们看到是一个final修饰的静态成员变量, 我们跟到static块中看它的初始化过程:

static {//List集合List<Integer> sizeTable = new ArrayList<Integer>();//从16开始, 每递增16添加到List中, 直到大于等于512for (int i = 16; i < 512; i += 16) {sizeTable.add(i);}//从512开始, 倍增添加到List中, 直到内存溢出for (int i = 512; i > 0; i <<= 1) {sizeTable.add(i);}//初始化数组SIZE_TABLE = new int[sizeTable.size()];//将list的内容放入数组中for (int i = 0; i < SIZE_TABLE.length; i ++) {SIZE_TABLE[i] = sizeTable.get(i);}
}

首先创建一个Integer类型的list用于盛放内存元素

这里通过两组循环为list添加元素

首先看第一组循环:

for (int i = 16; i < 512; i += 16) {sizeTable.add(i);
}

这里是通过16平移的方式, 直到512个字节, 将每次平移之后的内存大小添加到list中

再看第二组循环

for (int i = 512; i > 0; i <<= 1) {sizeTable.add(i);
}

超过512之后, 再通过倍增的方式循环, 直到int类型内存溢出, 将每次倍增之后大小添加到list中

最后初始化SIZE_TABLE数组, 将list中的元素按下表存放到数组中

这样就初始化了内存数组

再回到AdaptiveRecvByteBufAllocator的构造方法中:

public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {//忽略验证代码//最小容量在table中的下标int minIndex = getSizeTableIndex(minimum);if (SIZE_TABLE[minIndex] < minimum) {this.minIndex = minIndex + 1;} else {this.minIndex = minIndex;}//最大容量在table中的下标int maxIndex = getSizeTableIndex(maximum);if (SIZE_TABLE[maxIndex] > maximum) {this.maxIndex = maxIndex - 1;} else {this.maxIndex = maxIndex;}this.initial = initial;
}

这里分别根据传入的最小和最大容量去SIZE_TABLE中获取其下标

我们跟到getSizeTableIndex(minimum)中:

private static int getSizeTableIndex(final int size) {for (int low = 0, high = SIZE_TABLE.length - 1;;) {if (high < low) {return low;}if (high == low) {return high;}int mid = low + high >>> 1;int a = SIZE_TABLE[mid];int b = SIZE_TABLE[mid + 1];if (size > b) {low = mid + 1;} else if (size < a) {high = mid - 1;} else if (size == a) {return mid;} else {return mid + 1;}}
}

这里是通过二分查找去获取其下表

if (SIZE_TABLE[minIndex] < minimum)这里判断最小容量下标所属的内存大小是否小于最小值, 如果小于最小值则下标+1

最大容量的下标获取原理同上, 判断最大容量下标所属内存大小是否大于最大值, 如果是则下标-1

我们回到DefaultChannelConfig的构造方法:

public DefaultChannelConfig(Channel channel) {this(channel, new AdaptiveRecvByteBufAllocator());
}

刚才我们剖析过了AdaptiveRecvByteBufAllocator()的创建过程, 我们继续跟到this()中:

protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) {setRecvByteBufAllocator(allocator, channel.metadata());this.channel = channel;
}

我们看到这里初始化了channel, 在channel初始化之前, 调用了setRecvByteBufAllocator(allocator, channel.metadata())方法, 顾名思义, 这是用于设置缓冲区分配器的方法, 第一个参数是我们刚刚分析过的新建的AdaptiveRecvByteBufAllocator对象, 第二个传入的是与channel绑定的ChannelMetadata对象, ChannelMetadata对象是什么?

我们跟进到metadata()方法当中, 由于是channel是NioServerSocketChannel, 所以调用到了NioServerSocketChannel的metadata()方法:

public ChannelMetadata metadata() {return METADATA;
}

这里返回了一个成员变量METADATA, 跟到这个成员变量中:

private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);

这里创建了一个ChannelMetadata对象, 并在构造方法中传入false和16

继续跟到ChannelMetadata的构造方法中:

public ChannelMetadata(boolean hasDisconnect, int defaultMaxMessagesPerRead) {//省略验证代码//falsethis.hasDisconnect = hasDisconnect;//16this.defaultMaxMessagesPerRead = defaultMaxMessagesPerRead;
}

这里做的事情非常简单, 只初始化了两个属性:

hasDisconnect=false

defaultMaxMessagesPerRead=16

defaultMaxMessagesPerRead=16代表在读取对方的链接或者channel的字节流时(无论server还是client), 最多只循环16次, 后面的讲解将会看到

剖析完了ChannelMetadata对象的创建, 我们回到DefaultChannelConfig的构造方法:

protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) {setRecvByteBufAllocator(allocator, channel.metadata());this.channel = channel;
}

跟到setRecvByteBufAllocator(allocator, channel.metadata())方法中:

private void setRecvByteBufAllocator(RecvByteBufAllocator allocator, ChannelMetadata metadata) {if (allocator instanceof MaxMessagesRecvByteBufAllocator) {((MaxMessagesRecvByteBufAllocator) allocator).maxMessagesPerRead(metadata.defaultMaxMessagesPerRead());} else if (allocator == null) {throw new NullPointerException("allocator");}rcvBufAllocator = allocator;
}

首先会判断传入的缓冲区分配器是不是MaxMessagesRecvByteBufAllocator类型的, 因为AdaptiveRecvByteBufAllocator实现了MaxMessagesRecvByteBufAllocator接口, 所以此条件成立

之后将其转换成MaxMessagesRecvByteBufAllocator类型, 然后调用其maxMessagesPerRead(metadata.defaultMaxMessagesPerRead())方法, 这里会走到其子类DefaultMaxMessagesRecvByteBufAllocator的maxMessagesPerRead(int maxMessagesPerRead)方法中, 其中参数metadata.defaultMaxMessagesPerRead()返回就是ChannelMetadata的属性defaultMaxMessagesPerRead, 也就是16

跟到maxMessagesPerRead(int maxMessagesPerRead)方法中:

public MaxMessagesRecvByteBufAllocator maxMessagesPerRead(int maxMessagesPerRead) {//忽略验证代码//初始化为16this.maxMessagesPerRead = maxMessagesPerRead;return this;
}

这里将自身属性maxMessagesPerRead设置为16, 然后返回自身

回到DefaultChannelConfig的构造方法:

private void setRecvByteBufAllocator(RecvByteBufAllocator allocator, ChannelMetadata metadata) {if (allocator instanceof MaxMessagesRecvByteBufAllocator) {((MaxMessagesRecvByteBufAllocator) allocator).maxMessagesPerRead(metadata.defaultMaxMessagesPerRead());} else if (allocator == null) {throw new NullPointerException("allocator");}rcvBufAllocator = allocator;
}

设置完了内存分配器的maxMessagesPerRead属性, 最后将DefaultChannelConfig自身的成员变量rcvBufAllocator设置成我们初始化完毕的allocator对象

至此, 有关channelConfig有关的初始化过程剖析完成

上一节: 执行任务队列

下一节: 处理接入事件之handle的创建

posted on 2019-01-01 00:44 向南是个万人迷 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/xiangnan6122/p/10203986.html

Netty源码分析第3章(客户端接入流程)----第1节: 初始化NioSockectChannelConfig相关推荐

  1. Netty源码分析第6章(解码器)----第4节: 分隔符解码器

    Netty源码分析第6章(解码器)---->第4节: 分隔符解码器 Netty源码分析第六章: 解码器 第四节: 分隔符解码器 基于分隔符解码器DelimiterBasedFrameDecode ...

  2. Netty源码分析第1章(Netty启动流程)----第4节: 注册多路复用

    Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用 Netty源码分析第一章:Netty启动流程   第四节:注册多路复用 回顾下以上的小节, 我们知道了channe ...

  3. Netty源码分析第7章(编码器和写数据)----第2节: MessageToByteEncoder

    Netty源码分析第7章(编码器和写数据)---->第2节: MessageToByteEncoder Netty源码分析第七章: Netty源码分析 第二节: MessageToByteEnc ...

  4. Netty源码分析第5章(ByteBuf)----第5节: directArena分配缓冲区概述

    Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述 Netty源码分析第五章: ByteBuf 第五节: directArena分配缓冲区概述 上 ...

  5. Netty源码分析系列之常用解码器(下)——LengthFieldBasedFrameDecoder

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 前言 在上一篇文章中分析了三个比较简单的解码器,今天接着分析最后一个常用的解码器:Leng ...

  6. 【Netty源码分析摘录】(八)新连接的接入

    文章目录 1.问题 2.检测新连接接入 3.创建客户端 channel 4. 绑定 NioEventLoop 4.1 register0 4.1.1 doRegister() 4.1.2 pipeli ...

  7. Netty源码分析系列之服务端Channel的端口绑定

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 微信公众号 问题 本文内容是接着前两篇文章写的,有兴趣的朋友可以先去阅读下两篇文章: Ne ...

  8. Netty源码分析(六)—Future和Promis分析

    Netty源码分析(六)-Future和Promis分析 Future用来在异步执行中获取提前执行的结果 个人主页:tuzhenyu's page 原文地址:Netty源码分析(六)-Future和P ...

  9. Android 系统(78)---《android framework常用api源码分析》之 app应用安装流程

    <android framework常用api源码分析>之 app应用安装流程 <android framework常用api源码分析>android生态在中国已经发展非常庞大 ...

  10. 【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析

    [SemiDrive源码分析][X9芯片启动流程]12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析 一.s ...

最新文章

  1. java awt button_AWT Button类
  2. python的构建工具setup.py
  3. 线程池中keepAliveTime的理解
  4. java多线程w3c_多线程
  5. 在.NET3.5平台上使用LinQ to SQL + NBear 创建三层WEB应用
  6. Codeforces 815C. Karen and Supermarket【树形DP】
  7. MongoDB(1)--简单介绍以及安装
  8. 1024程序员节再次引爆星城!千万程序员线上线下互动,共迎新程序员时代
  9. Java虚拟机性能监测工具Visual VM与OQL对象查询语言
  10. spring aop的简单使用
  11. url的地址循环怎么写_电子邮件地址怎么写
  12. Linux上,最常用的一批命令解析
  13. 元宇宙NFT商城系统|艺术数字藏品平台源码部署
  14. c语言中按位取反 程序,C语言问题,if条件里面按位取反
  15. 支持c语言的otp单片机,单片机驱动语音OTP芯片程序
  16. vue开发pc端支付方式(支付宝、微信、银联)
  17. 笔记本电脑怎么用u盘重装系统,u盘给电脑安装系统的方法
  18. Android开机向导setupwizard,设置系统语言,WiFi向导
  19. 解决studio 3T时间到期方法
  20. 华环光端机MySQL_华环155M光端机 H9MO-LME-01T1642/EJ

热门文章

  1. ASP.NET中Form验证登录后反复跳转回登录页面的问题
  2. kaggle案例:数据科学社区调查报告(附学习视频)
  3. 9、kubernetes之statefulset控制器
  4. 高通平台camera bring-up
  5. 【HTTP】 Fiddler简介
  6. css重新认识(2)
  7. 过滤器为JSP文件生成静态页面
  8. 数据结构之 普利姆算法总结
  9. hdu 4057(ac自动机+状态压缩dp)
  10. javascript基础修炼(8)——指向FP世界的箭头函数