предисловие
Thrift
служба поддержкибинарный,Формат сжатия,а такжеjson
форматировать данныеСериализацияидесериализовать. Разработчики могут более гибко выбирать конкретную форму протокола. возможен договорБесплатное расширениеДа, новая версия протокола, полностьюсовместимыйстарая версия!
текст
Введение в форматы обмена данными
в настоящее время популярныйформат обмена даннымиЕго можно разделить на следующие категории:
(1) Самоаналитический тип
Сериализованные данные содержатвсеструктура, в том числеfield
названиеиvalue
ценность. Напримерxml/json/java serizable
, Байдуmcpack/compack
, относятся к этой категории. которые регулируют различные свойстваприказправильносериализовать/десериализоватьБез влияния.
(2) Полуаналитический тип
Сериализованные данные, отбрасывая некоторую информацию, такую какfield
имя, но представилindex
(довольно частоid
+type
способ) соответствовать конкретномуАтрибутыиценность. Представители по этому поводуgoogle protobuf/thrift
также попадают в эту категорию.
(3) Нет аналитического типа
Легендарный большой Baiduinfpack
Реализация достигается таким образом, отбрасывая многодостоверная информация,производительность/сжатиеЛучше, чем лучшее, но обратная совместимость требует определенной работы, и подробности неизвестны.
обменный формат | тип | преимущество | недостаток |
---|---|---|---|
Xml | текст | легко читать | Раздутый, не поддерживает бинарные типы данных |
JSON | текст | легко читать | Отброшенная информация о типе, такая как «оценка»: 100, неоднозначно, чтобы тип оценки был синтаксическим анализом int/double, двоичный тип данных не поддерживается. |
Java serizable | бинарный | Простой в использовании | Раздутый, ограниченный только полем JAVA |
Thrift | бинарный | эффективный | Нелегко читать, обратная совместимость имеет определенные ограничения по соглашению. |
Google Protobuf | бинарный | эффективный | Нелегко читать, обратная совместимость имеет определенные ограничения по соглашению. |
Бережливые типы данных
- основной тип: bool: логическое значение byte: 8-битное целое число со знаком i16: 16-битное целое число со знаком i32: 32-битное целое число со знаком i64: 64-битное целое число со знаком double: 64-битное число с плавающей запятой string: строка в кодировке UTF-8 binary: двоичная строка
- Тип структуры: struct: объект определенной структуры
- Тип контейнера: list: упорядоченный список элементов set: неупорядоченный набор неповторяющихся элементов map: упорядоченный набор ключей/значений
- Тип исключения: exception: тип исключения
- Тип Обслуживания: service: класс, соответствующий конкретной службе
Экономичный протокол сериализации
Thrift
позволяет пользователям выбиратьклиентиСервермеждутранспортный протокол связикатегория, вПротокол передачиВ целом делится натекст(text
)ибинарный(binary
) Протокол передачи. заэкономить пропускную способность,Повышение эффективности передачи, обычно используетсябинарныйТип транспортного протокола является большинством, а иногда и основанным натип текстаСоглашение, которое должно быть основано на реальных потребностях в проекте/продукте. Общие протоколы следующие:
- TBinaryПротокол:бинарныйФормат кодирования для передачи данных
- Компактный протокол:высокая эффективностьиз,плотныйизбинарныйФормат кодирования для передачи данных
- TJSONПротокол: использовать
JSON
текстпротокол кодирования данных для передачи данных - TSimpleJSONProtocol: предоставляет только
JSON
просто пишисоглашение, применимое черезРазбор скриптового языка
Тесты сериализации для Thrift
а) Сначала напишите простуюthrift
документpair.thrift
:
struct Pair {
1: required string key
2: required string value
}
отмечено здесь
required
Поля , которые должны быть правильно назначены при использовании, иначе среда выполнения выдастTProtocolException
аномальный. по умолчанию и указан какoptional
, поле не проверяется во время выполнения.
(б) Скомпилируйте и сгенерируйтеjava
Исходный код:
thrift -gen java pair.thrift
(c) Напишите тестовый код сериализации и десериализации:
- Тест сериализации, будет
Pair
объект записывается в файл
private static void writeData() throws IOException, TException {
Pair pair = new Pair();
pair.setKey("key1").setValue("value1");
FileOutputStream fos = new FileOutputStream(new File("pair.txt"));
pair.write(new TBinaryProtocol(new TIOStreamTransport(fos)));
fos.close();
}
- Тест десериализации, синтаксический анализ сгенерирован из файла
Pair
объект
private static void readData() throws TException, IOException {
Pair pair = new Pair();
FileInputStream fis = new FileInputStream(new File("pair.txt"));
pair.read(new TBinaryProtocol(new TIOStreamTransport(fis)));
System.out.println("key => " + pair.getKey());
System.out.println("value => " + pair.getValue());
fis.close();
}
(d) Наблюдайте за текущими результатами, нормальный вывод указывает, чтоСериализацияидесериализоватьПроцесс завершается нормально.
Исходный код протокола Thrift
(1) анализ writeData()
Выезд первымthrift
измеханизм сериализации, то есть реализация записи данных, которая принята здесьбинарный протоколTBinaryProtocol
, точка входа естьpair.write(TProtocol)
:
Проверятьscheme()
метод, принять решение о принятииПлан кортежа(TupleScheme
)все ещеСтандартный план(StandardScheme
) для сериализации по умолчанию используетсяСтандартный планStandardScheme
.
Стандартный план(StandardScheme
) подwrite()
метод:
Вот несколько шагов сделано:
(а) СогласноThrift IDL
определено в файлеrequired
Поле проверяет правильность назначения поля.
public void validate() throws org.apache.thrift.TException {
// check for required fields
if (key == null) {
throw new org.apache.thrift.protocol.TProtocolException("Required field 'key' was not present! Struct: " + toString());
}
if (value == null) {
throw new org.apache.thrift.protocol.TProtocolException("Required field 'value' was not present! Struct: " + toString());
}
}
б) пройтиwriteStructBegin()
записыватьнаписать структуруизначальный тег.
public void writeStructBegin(TStruct struct) {}
(c) Пишите один за другимPair
различные поля объекта, в том числе поляначальный тег поля,значение поляиконечный тег поля.
if (struct.key != null) {
oprot.writeFieldBegin(KEY_FIELD_DESC);
oprot.writeString(struct.key);
oprot.writeFieldEnd();
}
// 省略...
(1) Первыйначальный тег поля,включаютtype
иfield-id
.type
полеИдентификационный номер типа данных,field-id
даThrift IDL
ОпределенныйПорядок полей, сказатьkey
1,value
это 2.
public void writeFieldBegin(TField field) throws TException {
writeByte(field.type);
writeI16(field.id);
}
Thrift
при условииTType
, для разныхтип данных(type
) предоставляет уникальный идентификаторtypeID
.
public final class TType {
public static final byte STOP = 0; // 数据读写完成
public static final byte VOID = 1; // 空值
public static final byte BOOL = 2; // 布尔值
public static final byte BYTE = 3; // 字节
public static final byte DOUBLE = 4; // 双精度浮点型
public static final byte I16 = 6; // 短整型
public static final byte I32 = 8; // 整型
public static final byte I64 = 10; // 长整型
public static final byte STRING = 11; // 字符串类型
public static final byte STRUCT = 12; // 引用类型
public static final byte MAP = 13; // Map
public static final byte SET = 14; // 集合
public static final byte LIST = 15; // 列表
public static final byte ENUM = 16; // 枚举
}
(2) Тогда напишитезначение поля, в зависимости от типа данных поля, его можно обобщить в следующую реализацию:writeByte()
,writeBool()
,writeI32()
,writeI64()
,writeDouble()
,writeString()
иwriteBinary()
метод.
TBinaryProtocol
через длину8
изbyte
кеш массива байтовнаписатьиличитатьвременные байтовые данные.
private final byte[] inoutTemp = new byte[8];
**Здравый смысл 1: **Введение в шестнадцатеричный формат. Данные, начинающиеся с 0x, представляют собой шестнадцатеричный формат, а 0xff заменяется на 255 в десятичном формате. В шестнадцатеричном формате пять букв A, B, C, D, E и F представляют собой 10, 11, 12, 13, 14 и 15 соответственно.
16
базаИзменятьдесятичный: f означает 15. Вес n-го бита равен n-й степени числа 16, справа налево, начиная с бита 0: 0xff = 1516^1 + 1516^0 = 255
16
базаИзменятьбинарныйизменить сновадесятичный: 0xff = 1111 1111 = 2^8 - 1 = 255
**Здравый смысл 2:** Использование побитовых операторов. >> Представляет символ сдвига вправо, например: int i=15; результат i>>2 равен 3, и сдвинутая часть будет отброшена. А >.
Преобразовать вбинарныйФорма 0000 1111(15), сдвинутая вправо на 2 бита, будет 0000 0011(3), а результат 0001 1010(18), сдвинутый на 3 бита, будет 0000 0011(3).
- writeByte(): написатьодин байтданные.
public void writeByte(byte b) throws TException {
inoutTemp[0] = b;
trans_.write(inoutTemp, 0, 1);
}
- writeBool(): написатьЛогическое значениеданные.
public void writeBool(boolean b) throws TException {
writeByte(b ? (byte)1 : (byte)0);
}
-
writeI16(): написатькороткое целое
short
тип данных.
public void writeI16(short i16) throws TException {
inoutTemp[0] = (byte)(0xff & (i16 >> 8));
inoutTemp[1] = (byte)(0xff & (i16));
trans_.write(inoutTemp, 0, 2);
}
-
writeI32(): написатьЦелое число
int
тип данных.
public void writeI32(int i32) throws TException {
inoutTemp[0] = (byte)(0xff & (i32 >> 24));
inoutTemp[1] = (byte)(0xff & (i32 >> 16));
inoutTemp[2] = (byte)(0xff & (i32 >> 8));
inoutTemp[3] = (byte)(0xff & (i32));
trans_.write(inoutTemp, 0, 4);
}
-
writeI64(): написатьдлинное целое
long
тип данных.
public void writeI64(long i64) throws TException {
inoutTemp[0] = (byte)(0xff & (i64 >> 56));
inoutTemp[1] = (byte)(0xff & (i64 >> 48));
inoutTemp[2] = (byte)(0xff & (i64 >> 40));
inoutTemp[3] = (byte)(0xff & (i64 >> 32));
inoutTemp[4] = (byte)(0xff & (i64 >> 24));
inoutTemp[5] = (byte)(0xff & (i64 >> 16));
inoutTemp[6] = (byte)(0xff & (i64 >> 8));
inoutTemp[7] = (byte)(0xff & (i64));
trans_.write(inoutTemp, 0, 8);
}
-
writeDouble(): написатьдвойной поплавок
double
тип данных.
public void writeDouble(double dub) throws TException {
writeI64(Double.doubleToLongBits(dub));
}
- writeString(): написатьТип строки, сначала напиши сюдадлина строки, затем написатьсодержимое строки.
public void writeString(String str) throws TException {
try {
byte[] dat = str.getBytes("UTF-8");
writeI32(dat.length);
trans_.write(dat, 0, dat.length);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
-
writeBinary: написатьдвоичный массивтип данных, где ввод данных
NIO
серединаByteBuffer
тип.
public void writeBinary(ByteBuffer bin) throws TException {
int length = bin.limit() - bin.position();
writeI32(length);
trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length);
}
(3) После того, как каждое поле написано, его необходимо записатьконечный тег поля.
public void writeFieldEnd() {}
(d).Когда все поля заполнены, необходимо записатьмаркер остановки поля.
public void writeFieldStop() throws TException {
writeByte(TType.STOP);
}
(e).После завершения записи всех данныхwriteStructEnd()
записыватьнаписать структуруиззнак завершения.
public void writeStructEnd() {}
(2) анализ readData()
Проверятьthrift
измеханизм десериализации,Сейчасчтение данныхреализовано, также используябинарный протоколTBinaryProtocol
, точка входа естьpair.read(TProtocol)
:
чтение данныхизапись данныхто же самое, также используетсяСтандартный планStandardScheme
.Стандартный план(StandardScheme
) подread()
метод:
Вот несколько шагов для завершения:
(пропускreadStructBegin
читатьструктураизначальный тег.
iprot.readStructBegin();
(б) Циклическое чтениеструктурасерединаВсе полевые данныеприбытьPair
объект до прочтенияorg.apache.thrift.protocol.TType.STOP
до того как.iprot.readFieldBegin()
начать читатьследующее поленужно прочитать передначальный тег поля.
while (true) {
schemeField = iprot.readFieldBegin();
if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
break;
}
// 字段的读取,省略...
}
(с). СогласноThrift IDL
Определенныйfield-id
Прочтите соответствующее поле и назначьте егоPair
объект и установитьPair
Соответствующие поля объектастатус чтения(посылка: поле находится вIDL
определяется какrequired
).
switch (schemeField.id) {
case 1: // KEY
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.key = iprot.readString();
struct.setKeyIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // VALUE
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.value = iprot.readString();
struct.setValueIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
default:
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
ополе чтениязначение в соответствии с полемтип данныхТакже делится на следующие реализации:readByte()
,readBool()
,readI32()
,readI64()
,readDouble()
,readString()
иreadBinary()
метод.
- readByte(): читатьодин байтданные.
public byte readByte() throws TException {
if (trans_.getBytesRemainingInBuffer() >= 1) {
byte b = trans_.getBuffer()[trans_.getBufferPosition()];
trans_.consumeBuffer(1);
return b;
}
readAll(inoutTemp, 0, 1);
return inoutTemp[0];
}
- readBool(): читатьЛогическое значениеданные.
public boolean readBool() throws TException {
return (readByte() == 1);
}
-
readI16(): читатькороткое целое
short
тип данных.
public short readI16() throws TException {
byte[] buf = inoutTemp;
int off = 0;
if (trans_.getBytesRemainingInBuffer() >= 2) {
buf = trans_.getBuffer();
off = trans_.getBufferPosition();
trans_.consumeBuffer(2);
} else {
readAll(inoutTemp, 0, 2);
}
return (short) (((buf[off] & 0xff) << 8) |
((buf[off+1] & 0xff)));
}
-
readI32(): читатьЦелое число
int
тип данных.
public int readI32() throws TException {
byte[] buf = inoutTemp;
int off = 0;
if (trans_.getBytesRemainingInBuffer() >= 4) {
buf = trans_.getBuffer();
off = trans_.getBufferPosition();
trans_.consumeBuffer(4);
} else {
readAll(inoutTemp, 0, 4);
}
return ((buf[off] & 0xff) << 24) |
((buf[off+1] & 0xff) << 16) |
((buf[off+2] & 0xff) << 8) |
((buf[off+3] & 0xff));
}
-
readI64(): читатьдлинное целое
long
тип данных.
public long readI64() throws TException {
byte[] buf = inoutTemp;
int off = 0;
if (trans_.getBytesRemainingInBuffer() >= 8) {
buf = trans_.getBuffer();
off = trans_.getBufferPosition();
trans_.consumeBuffer(8);
} else {
readAll(inoutTemp, 0, 8);
}
return ((long)(buf[off] & 0xff) << 56) |
((long)(buf[off+1] & 0xff) << 48) |
((long)(buf[off+2] & 0xff) << 40) |
((long)(buf[off+3] & 0xff) << 32) |
((long)(buf[off+4] & 0xff) << 24) |
((long)(buf[off+5] & 0xff) << 16) |
((long)(buf[off+6] & 0xff) << 8) |
((long)(buf[off+7] & 0xff));
}
-
readDouble(): читатьс плавающей запятой двойной точности
double
тип данных.
public double readDouble() throws TException {
return Double.longBitsToDouble(readI64());
}
-
readString(): читатьТип строкиданные, сначала прочитайте и проверьте
4
байтдлина строки, затем проверьтеNIO
буферЕсть ли байты соответствующей длины вне потребляется. Если да, то прямо избуферчитать из, иначе изканал передачичитать данные в.
public String readString() throws TException {
int size = readI32();
checkStringReadLength(size);
if (trans_.getBytesRemainingInBuffer() >= size) {
try {
String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), size, "UTF-8");
trans_.consumeBuffer(size);
return s;
} catch (UnsupportedEncodingException e) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
return readStringBody(size);
}
если изканал передачичитать данные в , просматриватьreadStringBody()
метод:
public String readStringBody(int size) throws TException {
try {
byte[] buf = new byte[size];
trans_.readAll(buf, 0, size);
return new String(buf, "UTF-8");
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
-
readBinary(): читатьдвоичный массиввведите данные истрока прочитанааналогично, возвращает
ByteBuffer
Объект байтового буфера.
public ByteBuffer readBinary() throws TException {
int size = readI32();
checkStringReadLength(size);
if (trans_.getBytesRemainingInBuffer() >= size) {
ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), size);
trans_.consumeBuffer(size);
return bb;
}
byte[] buf = new byte[size];
trans_.readAll(buf, 0, size);
return ByteBuffer.wrap(buf);
}
(d) После того, как данные каждого поля будут прочитаны, необходимо прочитать еще одинконечный тег поля.
public void readFieldEnd() {}
(e) Когда все поля прочитаны, нужно пройтиreadStructEnd()
прочитать еще одинМаркер завершения строительства.
public void readStructEnd() {}
(f) После прочтения также необходимо проверитьThrift IDL
определяется какrequired
Является ли поле пустым и законным.
public void validate() throws org.apache.thrift.TException {
// check for required fields
if (key == null) {
throw new org.apache.thrift.protocol.TProtocolException("Required field 'key' was not present! Struct: " + toString());
}
if (value == null) {
throw new org.apache.thrift.protocol.TProtocolException("Required field 'value' was not present! Struct: " + toString());
}
}
Суммировать
Собственно, здесь дляThrift
измеханизм сериализацииимеханизм десериализацииизВыполнениеиЭффективность, Я считаю, что у вас уже есть более глубокое понимание!
Ссылки по теме
-
Подробное объяснение серии Apache Thrift (1) — обзор и начало работы
-
Подробное объяснение Apache Thrift Series (2) — Модель сетевых служб
-
Подробное объяснение серии Apache Thrift (3) — механизм сериализации
Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack
Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.