kafka 高吞吐量

kafka 高吞吐量介绍
  • 零拷贝

    将数据直接从磁盘文件复制到网卡设备中,而不需要经过应用程序,减少了内核和用户模式之间的上下文切换,依赖于底层的senffile()方法。

    内核态是对系统硬件资源的控制,用户态是应用程序,上层应用。在系统调用的时候,会发生用户从用户态到内核态的上下文切换。

    如果我们需要将数据展示给用户,我们就需要先将数据拷贝到内存,在从内存把数据放到socket,展示给用户。

    通过sendfile系统调用,提供了零拷贝。数据通过DMA(Direct Memory Access)拷贝到内核态Buffer后,直接通过DMA拷贝到NIC Buffer,无需CPU拷贝。

    PlaintextTransportLayer.class
    @Override
    public long transferFrom(FileChannel fileChannel, long position, long count) throws IOException {
        return fileChannel.transferTo(position, count, socketChannel);
    }
  • 磁盘顺序写 和 页缓存

    kafka设计采用文件追加的方式,只能在文件的尾部追加新的消息,不允许修改已经写入消息。当broker接收到producer发送过来的消息的时候,根据消息的主题和分区情况,将该消息写入到该分区的最后的一个segment文件中。采用这种追加的方式避免了磁盘随机写入磁盘寻道的开销。

    如果每条消息都执行一次磁盘写入,也还是会造成很大的I/O开销,kafka使用了大量的页缓存,消息先被写入到页缓存,然后由操作系统负责将数据刷到磁盘,kafka提供了同步刷盘和间歇性刷盘的功能。除了页缓存批量刷盘消息,kafka服务重启,页缓存的数据还是保持有效。

    image

  • 消息批量写入 和 消息压缩

    kafka消息压缩方式是将多条消息一起压缩,这样的压缩效果比较好;compression .type来配置压缩方式,有GZIP、SNAPPY、LS4,默认是不压缩的。

    kafka 消息格式

    public static int writeTo(DataOutputStream out,
                              int offsetDelta,
                              long timestampDelta,
                              ByteBuffer key,
                              ByteBuffer value,
                              Header[] headers) throws IOException {
        int sizeInBytes = sizeOfBodyInBytes(offsetDelta, timestampDelta, key, value, headers);
        ByteUtils.writeVarint(sizeInBytes, out);
        byte attributes = 0; // there are no used record attributes at the moment
        out.write(attributes); //属性
        ByteUtils.writeVarlong(timestampDelta, out); // 时间增量
        ByteUtils.writeVarint(offsetDelta, out); // 偏移增量
        if (key == null) {
            ByteUtils.writeVarint(-1, out);
        } else {
            int keySize = key.remaining();
            ByteUtils.writeVarint(keySize, out);
            Utils.writeTo(out, key, keySize); //key keySize
        }
        if (value == null) {
            ByteUtils.writeVarint(-1, out);
        } else {
            int valueSize = value.remaining();
            ByteUtils.writeVarint(valueSize, out);
            Utils.writeTo(out, value, valueSize); // value valueSize
        }
        if (headers == null)
            throw new IllegalArgumentException("Headers cannot be null");
        ByteUtils.writeVarint(headers.length, out);
        for (Header header : headers) {
            String headerKey = header.key();
            if (headerKey == null)
                throw new IllegalArgumentException("Invalid null header key found in headers");
            byte[] utf8Bytes = Utils.utf8(headerKey);
            ByteUtils.writeVarint(utf8Bytes.length, out);
            out.write(utf8Bytes);
            byte[] headerValue = header.value();
            if (headerValue == null) {
                ByteUtils.writeVarint(-1, out);
            } else {
                ByteUtils.writeVarint(headerValue.length, out); // headerValue.length
                out.write(headerValue); // headerValue
            }
        }
        return ByteUtils.sizeOfVarint(sizeInBytes) + sizeInBytes;
    }

    image

    上图是一条消息的所有数据信息,采用很多的varInt、varLong等变长整型和ZigZag编码。会把消息追加到batch里面去。

  • 总结

    零拷贝减少了上下文的切换;磁盘顺序写加快了写入时间 ,页缓存减缓I/O开销;消息批量写入减少网络I/O,消息压缩减少传输量。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值