Netty分布式行解碼器邏輯源碼解析
前文傳送門(mén):Netty分布式固定長(zhǎng)度解碼器實(shí)現(xiàn)原理剖析
這一小節(jié)了解下行解碼器LineBasedFrameDecoder, 行解碼器的功能是一個(gè)字節(jié)流, 以\r\n或者直接以\n結(jié)尾進(jìn)行解碼, 也就是以換行符為分隔進(jìn)行解析
同樣, 這個(gè)解碼器也繼承了ByteToMessageDecoder
行解碼器LineBasedFrameDecoder
首先看其參數(shù)
//數(shù)據(jù)包的最大長(zhǎng)度, 超過(guò)該長(zhǎng)度會(huì)進(jìn)行丟棄模式 private final int maxLength; //超出最大長(zhǎng)度是否要拋出異常 private final boolean failFast; //最終解析的數(shù)據(jù)包是否帶有換行符 private final boolean stripDelimiter; //為true說(shuō)明當(dāng)前解碼過(guò)程為丟棄模式 private boolean discarding; //丟棄了多少字節(jié) private int discardedBytes;
其中的丟棄模式, 我們會(huì)在源碼中看到其中的含義
我們看其decode方法
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); } }
這里的decode方法和我們上一小節(jié)分析的decode方法一樣, 調(diào)用重載的decode方法, 并將解碼后的內(nèi)容放到out集合中
我們跟到重載的decode方法中
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { //找這行的結(jié)尾 final int eol = findEndOfLine(buffer); if (!discarding) { if (eol >= 0) { final ByteBuf frame; //計(jì)算從換行符到可讀字節(jié)之間的長(zhǎng)度 final int length = eol - buffer.readerIndex(); //拿到分隔符長(zhǎng)度, 如果是\r\n結(jié)尾, 分隔符長(zhǎng)度為2 final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; //如果長(zhǎng)度大于最大長(zhǎng)度 if (length > maxLength) { //指向換行符之后的可讀字節(jié)(這段數(shù)據(jù)完全丟棄) buffer.readerIndex(eol + delimLength); //傳播異常事件 fail(ctx, length); return null; } //如果這次解析的數(shù)據(jù)是有效的 //分隔符是否算在完整數(shù)據(jù)包里 //true為丟棄分隔符 if (stripDelimiter) { //截取有效長(zhǎng)度 frame = buffer.readRetainedSlice(length); //跳過(guò)分隔符的字節(jié) buffer.skipBytes(delimLength); } else { //包含分隔符 frame = buffer.readRetainedSlice(length + delimLength); } return frame; } else { //如果沒(méi)找到分隔符(非丟棄模式) //可讀字節(jié)長(zhǎng)度 final int length = buffer.readableBytes(); //如果朝超過(guò)能解析的最大長(zhǎng)度 if (length > maxLength) { //將當(dāng)前長(zhǎng)度標(biāo)記為可丟棄的 discardedBytes = length; //直接將讀指針移動(dòng)到寫(xiě)指針 buffer.readerIndex(buffer.writerIndex()); //標(biāo)記為丟棄模式 discarding = true; //超過(guò)最大長(zhǎng)度拋出異常 if (failFast) { fail(ctx, "over " + discardedBytes); } } //沒(méi)有超過(guò), 則直接返回 return null; } } else { //丟棄模式 if (eol >= 0) { //找到分隔符 //當(dāng)前丟棄的字節(jié)(前面已經(jīng)丟棄的+現(xiàn)在丟棄的位置-寫(xiě)指針) final int length = discardedBytes + eol - buffer.readerIndex(); //當(dāng)前換行符長(zhǎng)度為多少 final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; //讀指針直接移到換行符+換行符的長(zhǎng)度 buffer.readerIndex(eol + delimLength); //當(dāng)前丟棄的字節(jié)為0 discardedBytes = 0; //設(shè)置為未丟棄模式 discarding = false; //丟棄完字節(jié)之后觸發(fā)異常 if (!failFast) { fail(ctx, length); } } else { //累計(jì)已丟棄的字節(jié)個(gè)數(shù)+當(dāng)前可讀的長(zhǎng)度 discardedBytes += buffer.readableBytes(); //移動(dòng) buffer.readerIndex(buffer.writerIndex()); } return null; } }
final int eol = findEndOfLine(buffer)
這里是找當(dāng)前行的結(jié)尾的索引值, 也就是\r\n或者是\n:
6-3-1
圖中不難看出, 如果是以\n結(jié)尾的, 返回的索引值是\n的索引值, 如果是\r\n結(jié)尾的, 返回的索引值是\r的索引值
我們看findEndOfLine(buffer)方法
private static int findEndOfLine(final ByteBuf buffer) { //找到/n這個(gè)字節(jié) int i = buffer.forEachByte(ByteProcessor.FIND_LF); //如果找到了, 并且前面的字符是-r, 則指向/r字節(jié) if (i > 0 && buffer.getByte(i - 1) == '\r') { i--; } return i; }
這里通過(guò)一個(gè)forEachByte方法找\n這個(gè)字節(jié), 如果找到了, 并且前面是\r, 則返回\r的索引, 否則返回\n的索引
回到重載的decode方法中:
if (!discarding) 判斷是否為非丟棄模式, 默認(rèn)是就是非丟棄模式, 所以進(jìn)入if中
if (eol >= 0) 如果找到了換行符, 我們看非丟棄模式下找到換行符的相關(guān)邏輯:
final ByteBuf frame; final int length = eol - buffer.readerIndex(); final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; if (length > maxLength) { buffer.readerIndex(eol + delimLength); fail(ctx, length); return null; } if (stripDelimiter) { frame = buffer.readRetainedSlice(length); buffer.skipBytes(delimLength); } else { frame = buffer.readRetainedSlice(length + delimLength); } return frame;
首先獲得換行符到可讀字節(jié)之間的長(zhǎng)度, 然后拿到換行符的長(zhǎng)度, 如果是\n結(jié)尾, 那么長(zhǎng)度為1, 如果是\r結(jié)尾, 長(zhǎng)度為2
if (length > maxLength) 帶表如果長(zhǎng)度超過(guò)最大長(zhǎng)度, 則直接通過(guò) readerIndex(eol + delimLength) 這種方式, 將讀指針指向換行符之后的字節(jié), 說(shuō)明換行符之前的字節(jié)需要完全丟棄
6-3-2
丟棄之后通過(guò)fail方法傳播異常, 并返回null
繼續(xù)往下看, 走到下一步, 說(shuō)明解析出來(lái)的數(shù)據(jù)長(zhǎng)度沒(méi)有超過(guò)最大長(zhǎng)度, 說(shuō)明是有效數(shù)據(jù)包
if (stripDelimiter) 表示是否要將分隔符放在完整數(shù)據(jù)包里面, 如果是true, 則說(shuō)明要丟棄分隔符, 然后截取有效長(zhǎng)度, 并跳過(guò)分隔符長(zhǎng)度
將包含分隔符進(jìn)行截取
以上就是非丟棄模式下找到換行符的相關(guān)邏輯
我們?cè)倏捶莵G棄模式下沒(méi)有找到換行符的相關(guān)邏輯, 也就是非丟棄模式下, if (eol >= 0) 中的else塊:
final int length = buffer.readableBytes(); if (length > maxLength) { discardedBytes = length; buffer.readerIndex(buffer.writerIndex()); discarding = true; if (failFast) { fail(ctx, "over " + discardedBytes); } } return null;
首先通過(guò) final int length = buffer.readableBytes() 獲取所有的可讀字節(jié)數(shù)
然后判斷可讀字節(jié)數(shù)是否超過(guò)了最大值, 如果超過(guò)最大值, 則屬性discardedBytes標(biāo)記為這個(gè)長(zhǎng)度, 代表這段內(nèi)容要進(jìn)行丟棄
6-3-3
buffer.readerIndex(buffer.writerIndex()) 這里直接將讀指針移動(dòng)到寫(xiě)指針, 并且將discarding設(shè)置為true, 就是丟棄模式
如果可讀字節(jié)沒(méi)有超過(guò)最大長(zhǎng)度, 則返回null, 表示什么都沒(méi)解析出來(lái), 等著下次解析
我們?cè)倏磥G棄模式的處理邏輯, 也就是 if (!discarding) 中的else塊:
首先這里也分兩種情況, 根據(jù) if (eol >= 0) 判斷是否找到了分隔符, 我們首先看找到分隔符的解碼邏輯:
final int length = discardedBytes + eol - buffer.readerIndex(); final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; buffer.readerIndex(eol + delimLength); discardedBytes = 0; discarding = false; if (!failFast) { fail(ctx, length); }
如果找到換行符, 則需要將換行符之前的數(shù)據(jù)全部丟棄掉
6-3-4
final int length = discardedBytes + eol - buffer.readerIndex()
這里獲得丟棄的字節(jié)總數(shù), 也就是之前丟棄的字節(jié)數(shù)+現(xiàn)在需要丟棄的字節(jié)數(shù)
然后計(jì)算換行符的長(zhǎng)度, 如果是\n則是1, \r\n就是2
buffer.readerIndex(eol + delimLength)
這里將讀指針移動(dòng)到換行符之后的位置
然后將discarding設(shè)置為false, 表示當(dāng)前是非丟棄狀態(tài)
我們?cè)倏磥G棄模式未找到換行符的情況, 也就是丟棄模式下, if (eol >= 0) 中的else塊:
discardedBytes += buffer.readableBytes(); buffer.readerIndex(buffer.writerIndex());
這里做的事情非常簡(jiǎn)單, 就是累計(jì)丟棄的字節(jié)數(shù), 并將讀指針移動(dòng)到寫(xiě)指針, 也就是將數(shù)據(jù)全部丟棄
最后在丟棄模式下, decode方法返回null, 代表本次沒(méi)有解析出任何數(shù)據(jù)
以上就是行解碼器的相關(guān)邏輯
以上就是Netty分布式行解碼器邏輯源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Netty分布式行解碼器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
又又叕出BUG啦!理智分析Java NIO的ByteBuffer到底有多難用
網(wǎng)絡(luò)數(shù)據(jù)的基本單位永遠(yuǎn)是byte,Java NIO提供ByteBuffer作為字節(jié)的容器,但該類(lèi)過(guò)于復(fù)雜,有點(diǎn)難用.本篇文章就帶大家簡(jiǎn)單了解一下 ,需要的朋友可以參考下2021-06-06JAVA通過(guò)HttpClient發(fā)送HTTP請(qǐng)求的方法示例
本篇文章主要介紹了JAVA通過(guò)HttpClient發(fā)送HTTP請(qǐng)求的方法示例,詳細(xì)的介紹了HttpClient使用,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09springboot如何統(tǒng)一設(shè)置時(shí)區(qū)
這篇文章主要介紹了springboot如何統(tǒng)一設(shè)置時(shí)區(qū)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01mybatis對(duì)象List<String> List<Integer>屬性映射方式
這篇文章主要介紹了mybatis對(duì)象List<String> List<Integer>屬性映射方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12SpringBoot使用Maven打包異常-引入外部jar的問(wèn)題及解決方案
這篇文章主要介紹了SpringBoot使用Maven打包異常-引入外部jar,需要的朋友可以參考下2020-06-06SSM使用mybatis分頁(yè)插件pagehepler實(shí)現(xiàn)分頁(yè)示例
本篇文章主要介紹了SSM使用mybatis分頁(yè)插件pagehepler實(shí)現(xiàn)分頁(yè)示例,使用分頁(yè)插件的原因,簡(jiǎn)化了sql代碼的寫(xiě)法,實(shí)現(xiàn)較好的物理分頁(yè),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-03-03詳解Java中方法重寫(xiě)和方法重載的6個(gè)區(qū)別
方法重寫(xiě)和方法重載都是面向?qū)ο缶幊讨校敲捶椒ㄖ貙?xiě)和方法重載有哪些區(qū)別,本文就詳細(xì)的來(lái)介紹一下,感興趣的可以了解一下2022-01-01Java集合的定義與Collection類(lèi)使用詳解
這篇文章主要介紹了Java集合的定義及Collection工具類(lèi)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-11-11