gprs发送信号对方如何接收_和接收缓冲区比较:Netty发送缓冲区是如何设计的,why?...

点击上方蓝字关注我吧!
本篇文章大概3300字,阅读时间大约10分钟
前面文章,透彻分析了Netty的接收缓冲区优化的套路和实现细节,以及写数据和刷新数据的宏观流程和细节:
从源码出发:在宏观上把握Netty写数据到应用层缓冲区的过程
从源码出发:在宏观上把握Netty刷新数据到网络的过程
其中,反复提到了一个组件叫ChannelOutboundBuffer,对这个缓冲区的着墨并不多。本文就单独总结这个发送缓冲区的设计思想和实现细节,以及为何这样设计。
Netty为每个已经创建的Channel都绑定了一个ChannelOutboundBuffer对象,如下一个Netty的Channel的代码片段:黄色1处是Channel聚合的Unsafe,在每个Netty的Channel里,都会通过其unsafe获取一个独立的ChannelOutboundBuffer,如黄色2所示。即它调控的粒度是Channel级。
如下是官方对ChannelOutboundBuffer的解释:
*(Transport implementors only)an internal data structure used by{@link AbstractChannel}to store its pending*outbound write requests.*<p>*All methods must be called by a transport implementation from an I/O thread,except the following ones:*<ul>*<li>{@link#size()}and{@link#isEmpty()}li>*<li>{@link#isWritable()}li>*<li>{@link#getUserDefinedWritability(int)}and{@link#setUserDefinedWritability(int,boolean)}li>*ul>*p>*/
即它是一个Netty内部的数据结构,由每个Channel独立保存一份,并且只能被Netty的I/O线程调度,除了一些get方法,比如size()方法,isEmpty方法等。回忆文章从源码出发:在宏观上把握Netty写数据到应用层缓冲区的过程中拆解的流程,当用户调用writeAndFlush方法发消息时,首先添加消息到ChannelOutboundBuffer,最终会层层调用到pipeline的头结点的write方法,内部会调用unsafe的write方法,如下由于unsafe的write和flush方法会被客户端,服务端复用,故被实现在了AbstractUnsafe类:
首先在黄色1处,拿到发送缓冲区的一个快照,正常情况黄色2不会执行,进入黄色3处的filter0utboundMessage(msg)方法——对非堆外内存进行转换,转换为堆外内存,即Netty的设计理念是——所有的I/O操作都走堆外内存,以提升性能。接着下一步是执行黄色4处代码——拿到当前msg消息对象的大小(单位是字节),接着看最后一行——黄色5处调用了ChannelOutboundBuffer的addMessage方法,目的是将待发送消息msg加入应用层的发送缓冲区,并且一旦加入就会通过promise回调用户的监听器。
下面进入addMessage方法细致的看下它的实现,该方法将本次待发送的msg以及msg的字节数,和异步对象promise当做参数传入:
这里知道一个结论——ChannelOutboundBuffer的数据结构是一个单向Entry链表+Netty对象池,故上面的addMessage方法主要就是在操作单向链表的指针。
基本思路是:首先将待发送的msg——ByteBuf对象封装为ChannelOutboundBuffer内部的Entry节点,然后使用尾插法将该新节点加入Entry链表。最后在addMessage方法的结束处调用一个incrementPendingOutboundBytes方法,这是Netty的流控机制,前面也简单提过,后续专题总结。
下面梳理ChannelOutboundBuffer的单向Entry链表结构,如下是创建Entry节点的过程:
Entry entry=Entry.newInstance(msg,size,total(msg),promise);
total方法会计算当前发送消息的字节数,本demo为6,然后进入newInstance方法实例化一个Entry:
1、RECYCLER对象,它涉及到了Netty的对象池技术,此处先抓主要矛盾,不深究,后续专题总结,只需要知道它实现了一个线程安全的轻量级的对象池——通过复用已有的对象来避免频繁创建和回收对象带来的性能损耗。所以这个Entry节点可以被重复使用,即每次ChannelOutboundBuffer用完一个Entry,便将Entry的参数都置为null,然后仍回到对象池RECYCLER,下次可以拿出来重复利用,并且使用了本地线程存储来保证线程安全
2、属性pendingSize记录的是当前待发送消息msg的一个估计值,这里有一个容易费解的地方,即CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD常量,它默认是96(单位字节),即Netty会给当前待发送的消息msg额外加上96字节,算作msg的估计值,后续会通过这个值去影响流量控制机制,关于这里的设计想法暂且不表,后续专题总结。属性total是当前待发送消息本身的大小。
对Entry有了一个理解后,下面梳理一下Entry链表的几个指针和属性:
1、flushedEntry:代表被flush方法标记为已刷新的消息节点,即可以认为该Entry马上或者已经被发到网络了,它指向的是链表里第一个要被刷新出去的节点
2、unflushedEntry:代表只是通过write方法添加到了Entry链表的消息节点。它是链表里第一个等待刷新的节点
3、tailEntry:Entry链表的最后一个节点
4、next:链表中节点的下一个节点
5、属性flushed代表当前缓冲区的大小,即链表中还没有被刷新出去的节点的数量
下面看Entry链表是如何被组织的,回忆addMessage方法:
调用addMessage方法之前,Entry链表的样子如下:
调用addMessage方法后,将待发送的消息节点,即新的Entry节点,通过尾插法追加到tailEntry节点后,同时tailEntry指向新Entry,unflushedEntry也指向新Entry,其结构变化为如下模样:
当第二个待发送消息被加入Entry链表时,它的结构变为如下模样:
以此类推,用户只要没有执行flush操作,最终当前Channel的发送缓冲区的待发送消息的堆积形式如下,直到触发了流控保护后才停止堆积:
以上,也能看出这是一个FIFO的添加顺序,即Netty保证通过write添加的消息,会严格按照它的调用顺序缓存,其Entry链表被消费时总是先消费最老的节点。接着回忆flush的流程,当用户调用flush方法后,会调用到pipeline的头结点的flush方法,即unsafe的flush方法:
当调用黄色1处的addFlush方法时,会将unflushedEntry的引用赋给flushedEntry,然后将unflushedEntry置为null,即设置刷新消息的标记。如下:
通过unflushedEntry指针拿到第一个待发送的消息节点,并且判null后,将这个unflushedEntry赋值给flushedEntry,然后开一个循环设置这些节点的标记,并且告诉用户不能做取消操作了,如果当前某个消息节点在设置之前就已经取消发送了,那么就将这个节点过滤(后续不会flush),同时将totalPendingSize减小。此时Entry链表的结构如下:
由于addFlush方法里会循环的设置待发送节点的标记指针,最终Entry链表变为如下的样子;
此时addFlush结束,但是以上操作仅仅对待发送消息节点做了标记和过滤,紧接着调用的flush0方法才会执行真正的刷新操作,回忆前面的文章从源码出发:在宏观上把握Netty刷新数据到网络的过程。
此时要真的刷新数据到Socket缓冲区里,会从flushedEntry节点开始,循环的将每个flushed状态的节点取出,存入一个JDK的ByteBuffer数组,通过nioBuffers方法做转化:
得到ByteBuffer数组后,后续分策略刷新。如果消息发送成功,那么调用ChannelOutboundBuffer的remove方法,将已发送消息从Entry链表中删除,此步骤也包括清理堆外内存,如果是非池化分配,那么直接释放——通过调用UNSAFE.freeMemory(address);,如果是池化内存分配,那么将内存回收到池子里,如下是Netty清理内存的核心方法free:
清理内存后,同时更新待发送的消息节点指针flushedEntry=flushedEntry.next。如下是此时链表的样子,红色方块代表已经刷新到了网络的消息:
以上循环往复,直到Entry链表里已经没有flushed的消息节点,就把flushedEntry设置为null,最终缓冲区被排干:
做个小结——设计的意义
a)一方面是吞吐量和延时的考虑。可以联系TCP协议的Nagle算法,默认都是启用的,Java可以通过setTcpNoDelay(true)来禁用。Netty就是如下配置,参考Netty里配置的TCP_NODELAY参数作用是啥,应该如何调优?:
.childOption(ChannelOption.TCP_NODELAY,true)
如果开启了Nagle算法(关闭TCP_NODELAY),那么在网络应用里就可能出现频繁的延时,用户体验极差。不过连续进行多次对小数据包的发送操作,本身就不是一个好的编程模式,在应用层就应该进行优化。对于既要求低延时,又有大量小数据传输,还同时想提高网络利用率的应用,只能用UDP自己在应用层来实现可靠性保证了。更合理的方案还是应该使用一次大数据的写操作,而不是多次小数据的写操作,所以Netty也在应用层设计实现了发送缓冲区。
b)给用户取消写操作的时间上的缓冲
Netty设计的write方法,在内部有一个addMessage操作,它会添加待发消息进ChannelOutboundBuffer,并且这个方法并不会刷新数据到Socket,只有调用了flush方法,才会将unflushedEntry的引用转移到flushedEntry引用中,表示即将刷新这个flushedEntry,至于为什么这么做?
因为Netty提供了实现了promise模式的API,使得每个I/O操作都可以被取消,例如,“我”在一些条件下,不打算发这个ByteBuf了,所以flush之前,都是可以反悔的。
c)方便实现流控
众所周知,TCP协议在发端也有发送缓冲区,在我理解,Netty在应用层搞一个缓冲区和传输层TCP协议设计缓冲区的目的一致,即在应用层提高吞吐量和实现应用层的流控。如下语句:
ctx.channel().write("hello dashuaiRPC!\r\n");
只是将hello dashuaiRPC!\r\n消息写到了Netty的ChannelOutboundBuffer对象,此处并不涉及到网络相关的操作。这样就可以在一定程度上提高服务的吞吐量,当累计当一定数量,在调用flush方法实现一次性批量发送。即write方法可以调用多次,flush才是真正的写入到Socket。并且由于发送缓冲区的存在,Netty还能方便的在应用层实现限流保护,以防止内存被打爆。
d)底层实现=单向链表+对象池
一方面可以节省内存,减少GC次数,另一方面链表实现缓冲区,也能减少数据的移动,只需要修改指针的指向即可。
e)在其它的Netty的功能实现上,起到一个辅助作用,比如辅助记录某个Channel的发消息的进度和总量,为空闲检测等做支持。可以参考文章:Netty进化之路:赏析空闲检测处理器对写检测的优化
END
点亮在看,你最好看
点击此处写留言~


点
相关文章:

原 史上最简单的SpringCloud教程 | 第八篇: 消息总线(Spring Cloud Bus)(Finchley版本)
转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f8-bus/ 本文出自方志朋的博客 转载请标明出处: Spring Cloud Bus 将分布式的节点用轻量的消息代理连接起来。它可以用于广播配置文件的更改或者服务之间…

python读取数据校验数据_Python通过Schema实现数据验证方式
Schema是什么?不管我们做什么应用,只要和用户输入打交道,就有一个原则--永远不要相信用户的输入数据。意味着我们要对用户输入进行严格的验证,web开发时一般输入数据都以JSON形式发送到后端API,API要对输入数据做验证。…

String,StringBuffer
String类代表不可变的字符序列。 String s1 "hello"; String s2 "hello"; s1 s2 > true "hello"是字符串常量,存在data seg中,编译器对data seg有优化,对于已经有的数据,不是重新分配空间&a…

蒙特卡洛粒子滤波定位算法_粒子滤波——来自哈佛的详细的粒子滤波器教程【1】...
本文原版链接:https://www.seas.harvard.edu/courses/cs281/papers/doucet-johansen.pdf本文是哈佛大学相关研究人员于2008年发表的一篇关于粒子滤波的详细教程,至今已被引用1687次。目录:介绍Introduction1.1 序言Preliminary remarks1.2 教…

人脸识别的一些资源
人脸识别的全部源代码(在Visual C6.0下可以运行识别)http://www.61ic.com/Download/DaVinci/Code/201304/121592.html 智能分析接口(支持车牌识别,人脸识别等智能类型)提供DEMO和源码,供参考!http://www.61ic.com/Down…

mysql where关键字_MySQL WHERE 子句
我们知道从 MySQL 表中使用 SQL SELECT 语句来读取数据。如需有条件地从表中选取数据,可将 WHERE 子句添加到 SELECT 语句中。语法以下是 SQL SELECT 语句使用 WHERE 子句从数据表中读取数据的通用语法:SELECT field1, field2,...fieldN FROM table_name…

mac远程连接windows工具_Windows远程MAC系统
第一步、在 Mac 上设置好屏幕共享1. 1先请在苹果 Mac 电脑上的“系统偏好设置”窗口中打开“共享”功能,如图所示接着在共享窗口中的左侧点击启用“屏幕共享”选项,如图所示当屏幕共享功能打开以后,请点击“电脑设置”按钮,如图所…

Tomcat虚拟目录设置
ssh $host "rm -fr /var/www/$tomcat_name/webapps/*" 远程分发war包部署tomcat项目时,需要先清除项目目录。 --------------------------------------------------------------------------------------- <context path"" docBase"&qu…

30005 rust_Steam三连冠老游戏《腐蚀(RUST)》为什么突然火起来了?
Steam新一周(1月18日-1月24日)销量榜公开,《赛博朋克2077》跌落至第五,《荒野大镖客2》前进到第六,而第一人称僵尸生存网络游戏《Rust》已经三连冠了,腐蚀是2013年的老游戏了,很多人问这个游戏值不值得购买,…

(6)解构赋值的用途
解构赋值的用途1.交换变量的值 var a 100; var b 200; var t; t a; a b; b t; //解构赋值的写法完成【ES6交换变量的值】 var x 100; var y 200; [x, y] [y, x]; console.log(x); console.log(y); 优点1:直观 优点2:一一对应 优点3:节省内存空间(不用多申请变量) 2.从函…

python lstm_python-Keras中LSTM的补充
好的,所以您的问题让我开始思考,我想我已经解决了,但是什么都没有.这是我为获取LSTM实现背后的一些见识而编写的代码段.from keras.layers import LSTMfrom keras.models import Sequentialmodel Sequential()model.add(LSTM(10, input_shape(20, 30), return_sequencesTrue))…

Jtabbedpane设置透明、Jpanel设置透明
摘自https://zhidao.baidu.com/question/983204331427010139.htmljava中如何设置Jtabbedpane为透明 20在Jtabbedpane下有背景图片,如何设置让他透明呢?0oo宝贝xxX |浏览 1147 次 |举报我有更好的答案2014-12-30最佳答案你好,你可以增加以下代…

mysql 按日期删除数据库_DAY11 - MySQL入门(数据库的增、删、改、查 基本操作)...
一、 数据库的介绍二、 MySQL的基本语法l 注释:单行注释: #注释内容单行注释: -- 注释内容(注意,两个“--”之后有一个空格)多行注释: /*注释内容*/l 语句行:一条语句也称为一条命令,通常用一个…

LeetCode之461. Hamming Distance
------------------------------------------------------------------ AC代码: public class Solution {public int hammingDistance(int x, int y) {return Integer.toString(x^y,2).replaceAll("0","").length();} } 题目来源: h…

机器学习模型 知乎_机器学习:模型评估之评估方法
机器学习已经成为了人工智能的核心研究领域之一,它的研究动机就是为了让计算机系统具有人的学习能力以便实现人工智能。目前,关于机器学习定义的说法比较多,而被广泛采用的定义是“利用经验来改善计算机系统自身的性能”。由于“经验”的…

NSDate NSCalendar NSString之间的故事以及转换
1.NSDate 和 NSString 之间的转换 这之间的转换主要依靠NSDateFormatterNSDate <------> NSStringNSDateFormatter *formatter [[NSDateFormatter alloc]init];formatter.dateFormat "yyyy-MM-dd HH:mm:ss";NSString *strDate [formatter stringFrom:date];…

bzoj 4695: 最假女选手
……一道丧病线段树膜板题…… 被常数卡的死去活来……QAQ 学到了些奇技淫巧:把取min标记 和 区间最小值 合并 可以快很多…… #include <bits/stdc.h> #define lc(t) ((t) << 1) #define rc(t) (((t) << 1) | 1) #define N 2000010 #define INF …

python 打包 .app 运行 控制台窗口_Python打包工具
1 Python打包工具目前在windows平台上将Python程序打包成exe文件主要有三个工具。今天将一个Tkinter写的界面程序打包成exe文件,三个工具都试了一遍,感觉PyInstaller会比较好用一些。2 py2exe2.1 下载安装2.2 启动脚本写一个setup_py2exe.py文件from dis…

地址池命令 思科理由_思科互联网络操作系统 ——路由器接口
点击蓝字关注我们路由器接口接口配置是最重要的路由器配置之一,因为若没有接口,路由器几乎就毫无用处。另外,要与其他设备通信,接口配置必须绝对精确。配置接口时,我们需要指定网络层地址、介质类型和带宽,还需使用其他管理命令。…

mysql数据去重语句_数据库 mysql 语句
LAMP: Linux系统 A阿帕奇服务器 Mysql数据库 Php语言mysql:常用代码create table CeShi1(Uid varchar(50) primary key,Pwd varchar(50),Name varchar(50),Nation varchar(50),foreign key(Nation) references Nation(Code))写查询语句需要注意:1.创建表的时候&…

mysql中utf8_bin、utf8_general_ci、utf8_general_cs编码区别
转载地址: https://www.cnblogs.com/exmyth/p/3616672.html在mysql中存在着各种utf8编码格式,如下表:1)utf8_bin2)utf8_general_ci3)utf8_general_csutf8_bin将字符串中的每一个字符用二进制数据存储,区分大…

利用闭包实现多次ajax请求只执行最后一次
点一个按钮,则向服务器请求资源,不作处理时,多次点击后会有很多个请求在等待。我们知道一般我们用ajax是异步请求,那么我们快速重复点击一个按钮得到的结果其实我们并不知道是哪次点击的结果可能是第一次可能是最后一次也可能是第…

3dmax批量导出fbx_推荐一款超实用的3DMAX插件——模法师
模法师集成于3DMAX上,到老子云平台下载插件后,直接双击运行安装就能使用了。有多好用呢?好比游戏开了挂,效率瞬间翻几番。主要提供三大功能:1、批量格式转换简单地说,你可以把大量模型文件,同时…

python实现平衡二叉树_LeetCode 110. 平衡二叉树 | Python
# 110. 平衡二叉树---题目来源:力扣(LeetCode)[https://leetcode-cn.com/problems/balanced-binary-tree](https://leetcode-cn.com/problems/balanced-binary-tree)## 题目---给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度…

「欧拉定理」学习笔记(费马小定理)
欧拉定理:对于互质的两个正整数$a, n$,满足$a^{φ(n)} ≡ 1\ (mod\ n)$ 证明: 设集合$S$包含所有$n$以内与$n$互质的数,共有$φ(n)$个:$$S \{ x_1, x_2, ..., x_{φ(n)} \} $$ 再设集合$T$:$$T \{ a * x…

Python将MySQL表数据写入excel
背景:将mysql表查询结果写入excel。 1.使用sqlyog工具将查询结果导出到Excel.xml中,用excel打开发现:因为text字段中有回车换行操作,显示结果行是乱的。 2.用mysql -uadmin -p -h -P -NBe"select * from tb;" >>a…

nacos动态配置数据源_Jasper 怎么配置动态数据源
Jasper 本身是不支持动态数据源的,能用的解决方式是通过 api 自定义数据源,实际操作就是根据条件判断后动态设定 jdbc 的 url、用户名及密码等连接属性。比如:String userName userDetails.getUsername();// obtain a connection based on t…

Linux命令之top
top –hv | -abcHimMsS –d delay –n iterations –p pid [, pid …] top程序提供运行系统的动态实时视图,它可以显示系统概要信息以及当前由Linux内核当前管理的任务列表。所示的系统概要信息的类型以及为任务显示的信息的类型、顺序和大小都是用户可配置的&#…

seal report mysql_Seal Report开放数据库报表工具(.Net)
概述:开放数据库报表工具(.Net)简介:Seal-Report提供了一个完整的框架,用于从任何数据库生成日常报告和仪表板。Seal-Report是Microsoft .NET Framework完全用C#编写的开源工具。Seal Report算是报表工具中比较好用的一个…

注册亚马逊云服务
要英文填写还要字符限制,好严格 转载于:https://www.cnblogs.com/ZHONGZHENHUA/p/6249805.html