kafka是如何解决粘包拆包的

本文来说下kafka是如何解决粘包拆包的


概述

前面笔者写了一篇文章一文讲清粘包拆包 全面的讲解了TCP粘包拆包相关的问题。下面进行一个简单的总结:

TCP粘包拆包产生的原因是,应用层有意义的数据包,传输层的协议并不了解其含义,不会去根据你的业务内容去分包和发送,只会按照自己的协议格式传送数据

知道问题的本质后,解决问题就简单了。就需要在应用层收到数据后根据标识判断一下,数据是否完整,如果完整了我们再进行数据包解析,最后交给业务代码处理。

解决粘包拆包问题的方法:

(1)消息定长;
(2)增加特殊字符进行分割,比如每条数据末尾都添加一个换行符;
(3)自定义协议,例如 len + data,其中len是代表data的字节长度;


kakfa是如何解决粘包拆包问题的呢

首先看粘包,也就是接收到了多余的数据,该如何拆分数据包,读取到正确完整的数据包?

kafka使用到是上面的第三种解决方法,自定义协议格式。

kafka接收到数据包后,会进行这些操作:

  • 先读取前4字节,转换为一个int,即长度;
  • 根据长度申请内存buffer;
  • 最后读取指定大小的数据到申请好的buffer中;

具体代码实现在:KafkaChannel.read()

public NetworkReceive read() throws IOException {

    NetworkReceive result = null;
    if (receive == null) {
        receive = new NetworkReceive(maxReceiveSize, id);
    }
    // 读取数据
    receive(receive);
    // 判断是否读取完数据
    if (receive.complete()) {
    // 数据读取完后,rewind一下,准备读
        receive.payload().rewind();
        result = receive;
        receive = null;
    }
    // 
    return result;
}

private long receive(NetworkReceive receive) throws IOException {
    return receive.readFrom(transportLayer);
}

public long readFrom(ScatteringByteChannel channel) throws IOException {
    return readFromReadableChannel(channel);
}

public long readFromReadableChannel(ReadableByteChannel channel) throws IOException {

    int read = 0;
    // size就是存放len的缓冲区,大小4个字节,定义:size = ByteBuffer.allocate(4);
    if (size.hasRemaining()) {
        int bytesRead = channel.read(size);
        if (bytesRead < 0)
            throw new EOFException();
        read += bytesRead;
        // 读完了长度
        if (!size.hasRemaining()) {
            // rewind一下,准备获取具体的len值
            size.rewind();
            // size里的值就是接下来要读取的数据的长度
            int receiveSize = size.getInt();
            if (receiveSize < 0)
                throw new InvalidReceiveException("Invalid receive (size = " + receiveSize + ")");
            if (maxSize != UNLIMITED && receiveSize > maxSize)
                throw new InvalidReceiveException("Invalid receive (size = " + receiveSize + " larger than " + maxSize + ")");
            // 分配读取数据的缓冲区
            this.buffer = ByteBuffer.allocate(receiveSize);
        }
    }
    if (buffer != null) {
        int bytesRead = channel.read(buffer);
        if (bytesRead < 0)
            throw new EOFException();
        read += bytesRead;
    }
    // 返回读取的总长度
    return read;
}

接下来,再看看拆包代码。拆包也就是接收到数据不够组成一条完整的数据,该如何等待完整的数据包?

最主要的代码,在上面的receive.complete()方法中的判断逻辑。

public boolean complete() {
    return !size.hasRemaining() && !buffer.hasRemaining();
}
  • !size.hasRemaining():接收到的len数据已经读取完成;
  • !buffer.hasRemaining():接收到的data数据已经读取完成;

两个条件同时成立,也就是说既要读取完len,也要读取完data,才算读取了完整的一条数据。

只要一条数据没读完整,那么receive.complete()函数返回值就是false,那么最终返回的结果就是null,等待下一次OP_READ事件的时候再接着上次没读完的数据读取,直到读取一条完整的数据为止。

那么这次读取的数据就会暂存起来,存入stageReceives这个数据结构中等待下一次读取。

if (channel.ready() && key.isReadable() && !hasStagedReceive(channel)) {

    NetworkReceive networkReceive;
    while ((networkReceive = channel.read()) != null)
        addToStagedReceives(channel, networkReceive);
}

本文小结

本文详细介绍了kafka是如何解决粘包拆包问题的,关键是要掌握粘包拆包产生的根本原因。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值