Netty如何发送消息(writeAndFlush)

  • 1.主要就是调用writeAndFlush,注意这边可以调用ChannelHandlerContext的,也可以调用channel的
  • 2.上述2种方式的主要区别就是前者是从当前handler流转到head节点,而后者则送从tail节点到head
  • 3.首先还得提前申明下该方法是异步,即我们方法执行结束的时候消息很有可能还在我们的buffer里面 未通过socket发送到对端,接下来我们详细剖析该方法。
  • 4.首先调用该方法需要2个参数,一个是object类型的消息,还有一个ChannelPromise。如果后者我们不设置,则 默认设置一个DefaultChannelPromise,内部持有当前的channel和loop。
  • 5.然后检测我们的promise,为空抛出异常,是否已经done,如果已经done但是未取消则抛出异常,如果已经done且取消了则直接返回true,再继续检测promise携带的channel是否与当前的channel一样,不一样也抛出异常。还要检测promise的class类型是否等于DefaultChannelPromise,如果是则直接返回,否则需要进一步检测primose是否为VoidChannelPromise或者CloseFuture,如果是可能需要抛出异常。
  • 6.当上述检测到无效的promise的时候会直接返回,并且如果我们的message是ReferenceCounted类型的则调用其release方法
    1. ReferenceCounted是netty自己维护的一个计数方式,用来管理对象的生命周期
  • 8.从当前的context寻找其前面的ChannelHandlerContext且outbound=true,outbound=true代表这个context里面的handler是ChannelOutboundHandler
  • 9.里面涉及到我们 ReferenceCounted 的touch方法,其实只是简单方便我们deubugger,如果我们byteBuf存在泄漏我们可以debugger观察
  • 10.netty一般使用ResourceLeakDetector来检测bytebuf的是否泄漏(所谓的泄漏就是我们不使用该对象,但是其refCnt不为0,从而导致该bytebuf不能释放)
  • 11.如果我们想使用ResourceLeakDetector来检测,我们必须使用ByteBufAllocator来获取bytebuf,而且目前只支持堆外内存(池化和非池化)和堆内池化,但是堆内非池化不支持检测。
  • 12.ResourceLeakDetector检测的原理是我们每次使用ByteBufAllocator创建bytebuf会根据我们的泄漏检测级别来进行。如果是DISABLED则直接返回,如果是小于PARANOID级别的则我们使用随机数来抽样代表每次创建是否需要reportLeak,
  • 13.最后我们需要创建一个DefaultResourceLeak对象,这个对象会存放在ConcurrentMap,key就是我们的DefaultResourceLeak 其是虚引用(我们知道我们的虚引用是指当引用对象被gc,则该虚引用则会被加入一个队列对象
    而我们所谓的泄漏是指我们的bytebuf对象被回收了但是其内部资源对象没有回收,导致即无法使用该内存且该内存也不会被回收进而导致内存泄漏。内部对象存储数据一般是数组或者directBytebuffer),而value是LeakEntry
    其只是简单的作为一个value,同时我们还获取我们的trackedHash=我们bytebuf的hash值。
  • 14.我们的reportLeak主要就是检测我们的logger是否可用,如果不可用我们的logger那就无法打印泄漏信息,那么我们需要清空我们的队列,如果logger可用则将虚引用取出来把该bytbuf的基本信息打印出来
  • 15.上面说完了内存泄漏的问题原理,我们这边继续探讨如何发送消息,我们在获取到ChannelOutboundHandler后调用其invokeWriteAndFlush方法
  • 16.上面方法先调用invokeWrite0方法然后再调用invokeFlush0。
  • 17.invokeWrite0方法最终调用ChannelOutboundHandler的write方法,在这边我们还有一个需要注意我们传递的消息可以是object对象,但是其底层还是directBytebuffer,如果我们刚开始不是传递这个消息对象,那么需要自己编写一个handler进行编码操作这个我们会在后续再细说。
  • 18.最终调用到了我们的headContext的write也就是AbstractUnsafe的write方法,而invokeFlush0最终调用的是AbstractUnsafe的flush方法
  • 19.write方法是首先获取当前channel里面的ChannelOutboundBuffer,然后会判断当前要传递的对象是否堆外内存或者FileRegion则可以直接发送,如果是bytebuf但是是非堆外内存则包装成堆外内存,如果以上都不是则直接抛出异常。
  • 20.然后我们评估下我们的要发送的内容大小,包装成Entry对象。评估大小的方式:如果是ByteBuf或者ByteBufHolder,则 直接获取发送字节的大小,如果是fileReginon则直接返回0,其他返回unknownSize(默认是8)
  • 21.我们的entry会最终存放到entry链表,有三个链表对象:tailEntry,flushedEntry,unflushedEntry.
  • 22.我们刚开始会把第一个entry设置为tailEntry和unflushedEntry,当我们执行addflush方法就是将unflushedEntry的链表的节点,转移到flushedEntry链表下,即flushedEntry链表保存的是即将要发送的entry节点,而我们每次调用addMessge都是将entry对象添加到当前tailEntry的下一个。
  • 23.flush的时候会观察是否存在FlushPending,即看当前的selectionKey是否有效以及当前的channel是否注册了write事件。当key有效且注册了write事件则代表当前的channel已经写满了需要等channel有空闲,我们需要等待我们write事件触发了再真正调用flush(写事件的就绪的本质是只要channel不为空闲就会触发写事件)
  • 24.首先需要判断inFlush0,如果为true则代表当前flush方法正在调用则直接返回,然后判断调用NioSocketChannel的doWrite方法
  • 25.该write方法首先获取writeSpinCount,即单个消息最多循环发送的次数,然后将我们的bytebuf组装ByteBuffer的数组,目前默认最大的是1024,数组的所有数据最大值不大于maxBytesPerGatheringWrite,当然不一定不超过,只是尽量不超过。然后每次发送的时候检测发送的字节大小如果等于0代表channel满了,满了就注册写事件等到channel有buffer可写数据即可
  • 26.如果成功一次性写完则尝试取消写事件并设置一个直接调用写事件的任务这样就算我们在此期间没有任何事件发生,我们还可以继续去将channelOutboundbuffer里面的entry写出去了。
  • 27至此整个写事件都已经描述完成了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343