做爲熟悉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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。