返回顶部
首页 > 资讯 > 后端开发 > Python >Nett分布式分隔符解码器逻辑源码剖析
  • 262
分享到

Nett分布式分隔符解码器逻辑源码剖析

2024-04-02 19:04:59 262人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录分隔符解码器我们看其中的一个构造方法我们跟到重载decode方法中我们看初始化该属性的构造方法章节总结前文传送门:Netty分布式行解码器逻辑源码解析 分隔符解码器 基于分隔符解

前文传送门:Netty分布式行解码器逻辑源码解析

分隔符解码器

基于分隔符解码器DelimiterBasedFrameDecoder, 是按照指定分隔符进行解码的解码器, 通过分隔符, 可以将二进制流拆分成完整的数据包

同样继承了ByteToMessageDecoder并重写了decode方法

我们看其中的一个构造方法

public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters) {
    this(maxFrameLength, true, delimiters);
}

这里参数maxFrameLength代表最大长度, delimiters是个可变参数, 可以说可以支持多个分隔符进行解码

我们进入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方法并将解析好的数据添加到集合list中, 其父类就可以遍历out, 并将内容传播

我们跟到重载decode方法中

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //行处理器(1)
    if (lineBasedDecoder != null) { 
        return lineBasedDecoder.decode(ctx, buffer);
    }
    int minFrameLength = Integer.MAX_VALUE;
    ByteBuf minDelim = null; 
    //找到最小长度的分隔符(2)
    for (ByteBuf delim: delimiters) {
        //每个分隔符分隔的数据包长度
        int frameLength = indexOf(buffer, delim);
        if (frameLength >= 0 && frameLength < minFrameLength) {
            minFrameLength = frameLength;
            minDelim = delim;
        }
    }
    //解码(3)
    //已经找到分隔符
    if (minDelim != null) {
        int minDelimLength = minDelim.capacity();
        ByteBuf frame;
        //当前分隔符否处于丢弃模式
        if (discardingTooLongFrame) { 
            //首先设置为非丢弃模式
            discardingTooLongFrame = false;
            //丢弃
            buffer.skipBytes(minFrameLength + minDelimLength);

            int tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            if (!failFast) {
                fail(tooLongFrameLength);
            }
            return null;
        }
        //处于非丢弃模式
        //当前找到的数据包, 大于允许的数据包
        if (minFrameLength > maxFrameLength) {
            //当前数据包+最小分隔符长度 全部丢弃
            buffer.skipBytes(minFrameLength + minDelimLength);
            //传递异常事件
            fail(minFrameLength);
            return null;
        }
        //如果是正常的长度
        //解析出来的数据包是否忽略分隔符
        if (stripDelimiter) {
            //如果不包含分隔符
            //截取
            frame = buffer.readRetainedSlice(minFrameLength);
            //跳过分隔符
            buffer.skipBytes(minDelimLength);
        } else {
            //截取包含分隔符的长度
            frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
        }
        return frame;
    } else {
        //如果没有找到分隔符
        //非丢弃模式
        if (!discardingTooLongFrame) {
            //可读字节大于允许的解析出来的长度
            if (buffer.readableBytes() > maxFrameLength) {
                //将这个长度记录下
                tooLongFrameLength = buffer.readableBytes();
                //跳过这段长度
                buffer.skipBytes(buffer.readableBytes());
                //标记当前处于丢弃状态
                discardingTooLongFrame = true;
                if (failFast) {
                    fail(tooLongFrameLength);
                }
            }
        } else {
            tooLongFrameLength += buffer.readableBytes();
            buffer.skipBytes(buffer.readableBytes());
        }
        return null;
    }
}

这里的方法也比较长, 这里也通过拆分进行剖析

(1). 行处理器

(2). 找到最小长度分隔符

(3). 解码

首先看第一步行处理器:

if (lineBasedDecoder != null) { 
    return lineBasedDecoder.decode(ctx, buffer);
}

这里首先判断成员变量lineBasedDecoder是否为空, 如果不为空则直接调用lineBasedDecoder的decode的方法进行解码, lineBasedDecoder实际上就是上一小节剖析的LineBasedFrameDecoder解码器

这个成员变量, 会在分隔符是\n和\r\n的时候进行初始化

我们看初始化该属性的构造方法

public DelimiterBasedFrameDecoder(
        int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) {
    //代码省略
    //如果是基于行的分隔
    if (isLineBased(delimiters) && !isSubclass()) {
        //初始化行处理器
        lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
        this.delimiters = null;
    } else {
        //代码省略
    }
    //代码省略
}

这里isLineBased(delimiters)会判断是否是基于行的分隔, 跟到isLineBased方法中:

private static boolean isLineBased(final ByteBuf[] delimiters) {
    //分隔符长度不为2
    if (delimiters.length != 2) {
        return false;
    }
    //拿到第一个分隔符
    ByteBuf a = delimiters[0];
    //拿到第二个分隔符
    ByteBuf b = delimiters[1];
    if (a.capacity() < b.capacity()) {
        a = delimiters[1];
        b = delimiters[0];
    }
    //确保a是/r/n分隔符, 确保b是/n分隔符
    return a.capacity() == 2 && b.capacity() == 1
            && a.getByte(0) == '\r' && a.getByte(1) == '\n'
            && b.getByte(0) == '\n';
}

首先判断长度等于2, 直接返回false

然后拿到第一个分隔符a和第二个分隔符b, 然后判断a的第一个分隔符是不是\r, a的第二个分隔符是不是\n, b的第一个分隔符是不是\n, 如果都为true, 则条件成立

我们回到decode方法中, 看步骤2, 找到最小长度的分隔符:

这里最小长度的分隔符, 意思就是从读指针开始, 找到最近的分隔符

for (ByteBuf delim: delimiters) {
    //每个分隔符分隔的数据包长度
    int frameLength = indexOf(buffer, delim);
    if (frameLength >= 0 && frameLength < minFrameLength) {
        minFrameLength = frameLength;
        minDelim = delim;
    }
}

这里会遍历所有的分隔符, 然后找到每个分隔符到读指针到数据包长度

然后通过if判断, 找到长度最小的数据包的长度, 然后保存当前数据包的的分隔符, 如下图:

6-4-1

这里假设A和B同为分隔符, A分隔符到读指针的长度小于B分隔符到读指针的长度, 这里会找到最小的分隔符A, 分隔符的最小长度, 就readIndex到A的长度

我们继续看第3步, 解码:

 if (minDelim != null) 表示已经找到最小长度分隔符, 我们继续看if块中的逻辑:

int minDelimLength = minDelim.capacity();
ByteBuf frame; 
if (discardingTooLongFrame) { 
    discardingTooLongFrame = false; 
    buffer.skipBytes(minFrameLength + minDelimLength); 
    int tooLongFrameLength = this.tooLongFrameLength;
    this.tooLongFrameLength = 0;
    if (!failFast) {
        fail(tooLongFrameLength);
    }
    return null;
} 
if (minFrameLength > maxFrameLength) { 
    buffer.skipBytes(minFrameLength + minDelimLength); 
    fail(minFrameLength);
    return null;
} 
if (stripDelimiter) { 
    frame = buffer.readRetainedSlice(minFrameLength); 
    buffer.skipBytes(minDelimLength);
} else { 
    frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
}
return frame;

 if (discardingTooLongFrame) 表示当前是否处于非丢弃模式, 如果是丢弃模式, 则进入if块

因为第一个不是丢弃模式, 所以这里先分析if块后面的逻辑

 if (minFrameLength > maxFrameLength) 这里是判断当前找到的数据包长度大于最大长度, 这里的最大长度使我们创建解码器的时候设置的, 如果超过了最大长度, 就通过 buffer.skipBytes(minFrameLength + minDelimLength) 方式, 跳过数据包+分隔符的长度, 也就是将这部分数据进行完全丢弃

继续往下看, 如果长度不大最大允许长度, 则通过 if (stripDelimiter) 判断解析的出来的数据包是否包含分隔符, 如果不包含分隔符, 则截取数据包的长度之后, 跳过分隔符

我们再回头看 if (discardingTooLongFrame) 中的if块中的逻辑, 也就是丢弃模式:

首先将discardingTooLongFrame设置为false, 标记非丢弃模式

然后通过 buffer.skipBytes(minFrameLength + minDelimLength) 将数据包+分隔符长度的字节数跳过, 也就是进行丢弃, 之后再进行抛出异常

分析完成了找到分隔符之后的丢弃模式非丢弃模式的逻辑处理, 我们在分析没找到分隔符的逻辑处理, 也就是 if (minDelim != null) 中的else块:

if (!discardingTooLongFrame) { 
    if (buffer.readableBytes() > maxFrameLength) { 
        tooLongFrameLength = buffer.readableBytes();
        buffer.skipBytes(buffer.readableBytes());
        discardingTooLongFrame = true;
        if (failFast) {
            fail(tooLongFrameLength);
        }
    }
} else {
    tooLongFrameLength += buffer.readableBytes();
    buffer.skipBytes(buffer.readableBytes());
}
return null;

首先通过 if (!discardingTooLongFrame) 判断是否为非丢弃模式, 如果是, 则进入if块:

在if块中, 首先通过 if (buffer.readableBytes() > maxFrameLength) 判断当前可读字节数是否大于最大允许的长度, 如果大于最大允许的长度, 则将可读字节数设置到tooLongFrameLength的属性中, 代表丢弃的字节数

然后通过 buffer.skipBytes(buffer.readableBytes()) 将累计器中所有的可读字节进行丢弃

最后将discardingTooLongFrame设置为true, 也就是丢弃模式, 之后抛出异常

如果 if (!discardingTooLongFrame) 为false, 也就是当前处于丢弃模式, 则追加tooLongFrameLength也就是丢弃的字节数的长度, 并通过 buffer.skipBytes(buffer.readableBytes()) 将所有的字节继续进行丢弃

以上就是分隔符解码器的相关逻辑

章节总结

本章介绍了抽象解码器ByteToMessageDecoder, 和其他几个实现了ByteToMessageDecoder类的解码器, 这个几个解码器逻辑都比较简单, 同学们可以根据其中的思想剖析其他的比较复杂的解码器, 或者根据其规则实现自己的自定义解码器

更多关于Nett分布式分隔符解码器的资料请关注编程网其它相关文章!

--结束END--

本文标题: Nett分布式分隔符解码器逻辑源码剖析

本文链接: https://lsjlt.com/news/144257.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Nett分布式分隔符解码器逻辑源码剖析
    目录分隔符解码器我们看其中的一个构造方法我们跟到重载decode方法中我们看初始化该属性的构造方法章节总结前文传送门:Netty分布式行解码器逻辑源码解析 分隔符解码器 基于分隔符解...
    99+
    2024-04-02
  • Netty分布式行解码器逻辑源码解析
    目录行解码器LineBasedFrameDecoder首先看其参数我们跟到重载的decode方法中我们看findEndOfLine(buffer)方法前文传送门:Netty分布式固定...
    99+
    2024-04-02
  • Netty分布式编码器写buffer队列逻辑剖析
    目录写buffer队列我们跟到AbstractUnsafe的write方法中回到write方法中我们跟到setUnwritable(invokeLater)方法中前文传送门:抽象编码...
    99+
    2024-04-02
  • Netty分布式解码器读取数据不完整的逻辑剖析
    目录概述第一节: ByteToMessageDecoder我们看他的定义我们看其channelRead方法我们看cumulator属性我们回到channRead方法中概述 在我们上一...
    99+
    2024-04-02
  • Netty分布式抽象编码器MessageToByteEncoder逻辑分析
    目录MessageToByteEncoder首先看MessageToByteEncoder的类声明跟到allocateBuffer方法中前文回顾:Netty分布式编码器及写数据事件处...
    99+
    2024-04-02
  • Netty分布式抽象编码器MessageToByteEncoder逻辑的示例分析
    小编给大家分享一下Netty分布式抽象编码器MessageToByteEncoder逻辑的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!MessageTo...
    99+
    2023-06-29
  • Netty分布式ByteBuf使用的回收逻辑剖析
    目录ByteBuf回收这里调用了release0, 跟进去我们首先分析free方法我们跟到cache中回到add方法中我们回到free方法中前文传送门:ByteBuf使用subPag...
    99+
    2024-04-02
  • 分布式Netty源码分析
    这篇文章主要介绍了分布式Netty源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇分布式Netty源码分析文章都会有所收获,下面我们一起来看看吧。服务器端demo看下一个简单的Netty服务器端的例子pu...
    99+
    2023-06-29
  • kafka核心消费逻辑源码分析
    本篇内容主要讲解“kafka核心消费逻辑源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“kafka核心消费逻辑源码分析”吧!消费逻辑框架搭建好之后着手开发下kafka的核心消费逻辑,流式图...
    99+
    2023-07-06
  • Netty分布式FastThreadLocal的set方法实现逻辑剖析
    目录FastThreadLocal的set方法实现线程set对象我们跟到setIndexedVariable中我们跟进removeIndexedVariable方法上一小节我们学习了...
    99+
    2024-04-02
  • 分布式Netty源码EventLoopGroup分析
    这篇文章主要介绍“分布式Netty源码EventLoopGroup分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“分布式Netty源码EventLoopGroup分析”文章能帮助大家解决问题。Ev...
    99+
    2023-06-29
  • 分布式Netty源码分析概览
    目录服务器端demoEventLoopGroup介绍功能1:先来看看注册Channel功能2:执行一些Runnable任务ChannelPipeline介绍bind过程sync介绍误...
    99+
    2024-04-02
  • Netty分布式Future与Promise执行回调相关逻辑剖析
    目录Future和Promise执行回调首先我们看一段写在handler中的业务代码这里关注newPromise()方法, 跟进去我们继续跟write方法跟进tryFailure方法...
    99+
    2024-04-02
  • Netty分布式ByteBuf的分类方式源码解析
    目录ByteBuf根据不同的分类方式 会有不同的分类结果1.Pooled和Unpooled2.基于直接内存的ByteBuf和基于堆内存的ByteBuf3.safe和unsafe上一小...
    99+
    2024-04-02
  • Redisson分布式锁之加解锁源码分析
    这篇文章主要介绍“Redisson分布式锁之加解锁源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Redisson分布式锁之加解锁源码分析”文章能帮助大家解决问题。锁的可重入性我们都知道,Ja...
    99+
    2023-07-05
  • Netty分布式ByteBuf缓冲区分配器源码解析
    目录缓冲区分配器以其中的分配ByteBuf的方法为例, 对其做简单的介绍跟到directBuffer()方法中我们回到缓冲区分配的方法然后通过validate方法进行参数验...
    99+
    2024-04-02
  • redisson分布式限流RRateLimiter源码解析
    目录分布式限流-单位时间多实例多线程访问次数限制1.简单使用2. 实现限流redisson使用了哪些redis数据结构3. 超过10s,我再次获取一个令牌,数据结构发生的变化4. 源...
    99+
    2022-11-13
    redisson分布式限流RRateLimiter redisson RRateLimiter
  • Netty分布式flush方法刷新buffer队列源码剖析
    目录flush方法这里最终会调用AbstractUnsafe的flush方法跟进addFlush方法回到addFlush方法回到AbstractUnsafe的flush方法我们重点关...
    99+
    2024-04-02
  • go语言分布式id生成器及分布式锁源码分析
    本文小编为大家详细介绍“go语言分布式id生成器及分布式锁源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“go语言分布式id生成器及分布式锁源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。分布式 i...
    99+
    2023-07-05
  • Java Zookeeper分布式分片算法源码分析
    这篇文章主要介绍了Java Zookeeper分布式分片算法源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java Zookeeper分布式分片算法源码分析文章都会有所收获,下面我们...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作