做为熟悉Kafka的读者,确定知道Kafka的消息中的不少长度字段均采用了可变长度的编码格式,那么这种可变的编码格式是什么呢,没错,就是咱们今天要讲的varint编码格式。下面咱们会有两个知识点须要讲解:java
-
字节序 -
varint编码
字节序
计算机在信息传输的过程当中都是采用必定的编码格式将数据编码为二进制,当数据接收方收到数据之后也会进行相应的解码将二进制数据转换成响应的格式。在计算机中,一个二进制数字(0或1)表明1Bit,8个Bit又称为1个字节(byte)。web
字节序指的就是多个字节在通讯中的排列顺序,字节序目前有两种格式:数组
-
大端序:整数的最高字节在存储时在最低字节的前面则称为大端序,通俗一点说就是按照数字的书写顺序进行二进制的转换 -
小端序:整数的最低字节在存储时在最高字节的前面则称为小端序,通俗一点说就是按照数字书写的颠倒顺序进行二进制的转换
下图就是数字123456的断续和小端序的二进制格式:微信

首先int类型的数据占据4个字节,以大端序为例咱们能够看到123456的高位的第一个字节都是无用的,咱们可使用三个字节就能表明123456,因为Kafka的长度字段的数值都会远远小于123456,甚至1个字节也能够表示,若是咱们依旧使用int来表示长度的话将会浪费大量的空间,所以基于这个缘由,Kafka在本身的v2消息格式中的长度字段具采用了可变长度的表示,这种表示方式就是经过varint编码。编辑器
varint
varint其实并不单单在kafka中有所使用,大名鼎鼎的Protocol Buffers也使用varint编码。flex
varint是使用一个或多个字节序列化整数的方式,他能够把一个固定字节的整数编码成变长字节。编码
varint编码中每个字节的最高位都不用来存储数字的真正表示,而是表示当前字节是否还属于当前数据,1表明是,0表明不是(也就是该字节是当前数据的最后一个字节数据)。每个字节的低7位用于以7位为一组存储数字的二进制补码表示,最低有效数组在前,这也就代表varint编码是按照小端序来排列的。spa
图中对数字123456进行varint编码,123456用二进制表示为1 11100010 01000000,每次低从向高取7位再加上最高有效位变成11000000 11000100 00000111。.net

下面咱们经过一段Java代码实现varint编码和解码,其中只实现了无符号Interger类型的数据。code
public class VarInt {
public static void writeUnsignedVarint(int value, DataOutput output) throws IOException { // value & 0xffffff80 当前字节是否为最后一个字节,不是则执行while while ((value & 0xffffff80) != 0) { // value & 0x7f保证能够取到整数最低7位 // | 0x80 填充字节最高位为1,由于当前字节还不是数据的最后一个字节 byte b = (byte) ((value & 0x7f) | 0x80); output.writeByte(b); System.out.println(b); value >>>= 7 ; } // 写入最后一个字节 System.out.println(value); output.writeByte(value); }
public static int readUnsignedVarint(ByteBuffer buffer) { int value = 0; int b; int i = 0; while (((b = buffer.get()) & 0x80) != 0) { value |= (b & 0x7f) << i; i += 7; if (i >= 28) { throw new IllegalArgumentException("illegal varint"); } } value |= b << i; return value; }
public static void main(String[] args) throws IOException { DataOutputStream output = new DataOutputStream(new ByteArrayOutputStream(2)); writeUnsignedVarint(123456, output); System.out.println("encode size:" + output.size()); System.out.println("-----------"); byte[] bytes = new byte[]{-64, -60, 7}; ByteBuffer wrap = ByteBuffer.wrap(bytes); System.out.println("decode:" + readUnsignedVarint(wrap)); System.out.println("-----------"); }}
读者能够本身尝试一下Long类型和有符号Interger的实现(Zig-Zag编码)。
本文分享自微信公众号 - shysh95(shysh95)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。