ThriftTBinaryProtocolTCompactProtocolTransport LayerDelta EncodingWire Format
Apache Thrift: Protocols
Обзор Thrift
Apache Thrift — фреймворк для кросс-языковой сериализации и RPC, созданный в Facebook (2007), Apache с 2010. Текущая версия — 0.22.0 (May 2025). Ключевое отличие от Protobuf: Thrift — полный RPC-фреймворк со встроенным transport layer, protocol layer и server runtime. Protobuf — только формат сериализации (gRPC — отдельный проект).
Поддерживает 28 языков: C++, Java, Python, Go, Rust, C#, JavaScript, Ruby, PHP, Erlang, Haskell, Perl, Dart, и другие.
Thrift Architecture Stack
Application CodeПользовательский код: вызывает сгенерированные методы клиента/сервера. IDL → thrift compiler → client stubs + server handlers.
Protocol LayerОпределяет wire format: как типы кодируются в байты. TBinaryProtocol (простой, быстрый), TCompactProtocol (компактный, varint+zigzag), TJSONProtocol (human-readable).
Transport LayerОпределяет как байты доставляются: TCP socket, HTTP, memory buffer, file. TSocket (TCP), THttpTransport (HTTP), TMemoryBuffer (in-memory), TFramedTransport (length-prefixed frames).
Server RuntimeМодель обработки запросов: TSimpleServer (один поток), TThreadPoolServer (thread pool), TNonblockingServer (NIO). Thrift предоставляет ready-to-use сервера.
NOTE
В отличие от Protobuf (где gRPC — отдельный проект на HTTP/2), Thrift включает transport и server runtime в коробке. Это упрощает начальную настройку, но ограничивает гибкость: привязка к Thrift-specific транспортам вместо стандартных HTTP/2 load balancers.
Thrift IDL
Thrift использует собственный IDL (Interface Definition Language):
Thrift IDLThrift IDL: struct (message), service (RPC), enum, typedef, exception, union, const. namespace per language. required/optional explicit. Service = full RPC definition.
Protobuf IDLProtobuf IDL: message, service, enum, oneof, map. package + option for language. Все поля implicit в proto3. Service для gRPC — отдельная генерация.
TBinaryProtocol
Простейший wire format Thrift — fixed-size headers, без компрессии:
TBinaryProtocol: Field Encoding
Field = Type (1B) + Field ID (2B) + Value
Каждое поле в TBinaryProtocol: type byte (1 байт) + field ID (2 байта big-endian) + value. Fixed-size header = 3 байта per field.
Type Byte1 байт: тип данных. BOOL=2, BYTE=3, I16=6, I32=8, I64=10, DOUBLE=4, STRING=11, STRUCT=12, MAP=13, SET=14, LIST=15. Фиксированный набор.
Field ID2 байта big-endian: номер поля из IDL (аналог field number в Protobuf). Позволяет пропускать неизвестные поля.
ValueФормат зависит от type byte. i32: 4 байта big-endian. i64: 8 байта. string: 4-byte length + UTF-8 data. bool: 1 байт.
Stop Byte: 0x00 (конец struct)
Struct завершается stop byte = 0x00. Ридер читает поля пока не встретит type byte = 0. Порядок полей не гарантирован (как в Protobuf).
Stop byte: 0x00. Общий размер: 7 + 9 + 1 = 17 байт. Для сравнения: Protobuf кодирует тот же User(id=150, name='Al') в 7 байт — в 2.4 раза компактнее.
WARNING
TBinaryProtocol не компактный: integers всегда 4/8 байт (fixed-size big-endian), field header — 3 байта. Тот же User(id=150, name="Al") занимает 17 байт vs 7 байт в Protobuf. Для высоконагруженных систем используйте TCompactProtocol.
TCompactProtocol
Компактный формат, использующий varint + zigzag + delta encoding для field IDs:
Каждый struct (и nested struct) завершается stop byte 0x00. Ридер читает поля в цикле: прочитать header → если type = 0 → конец struct.
Stop Byte и вложенные struct
Outer Struct StartOuter struct начинается. Ридер читает field headers один за другим.
Field 1: id (i32)Обычное скалярное поле. Header + value.
Field 2: address (struct)Вложенный struct. После header — рекурсивное чтение полей inner struct до его stop byte.
Field 3: name (string)Продолжение outer struct после inner stop byte.
0x00 — Outer Stop ByteStop byte outer struct: 0x00. Каждый уровень вложенности имеет свой stop byte.
Transport Layer
Thrift отделяет сериализацию (protocol) от доставки (transport):
Thrift Transports
TSocketПростой TCP socket. Blocking I/O. Подходит для TSimpleServer (один клиент) и TThreadPoolServer (thread per connection).
TFramedTransportLength-prefixed frames: 4-byte big-endian length + payload. Обязателен для TNonblockingServer (NIO). Позволяет читать полное сообщение за один read.
THttpTransportHTTP POST transport. Payload в body. Проходит через HTTP proxies и load balancers. Совместим с REST инфраструктурой.
TMemoryBufferIn-memory byte buffer. Для тестирования и in-process сериализации. Без сетевого overhead.
TIP
TFramedTransport — ключевой транспорт для production: TNonblockingServer требует framed transport, чтобы определить границы сообщений в non-blocking I/O. Если клиент использует TFramedTransport, сервер тоже обязан. Несовпадение → corrupted data.
Thrift vs Protobuf: Wire Size
Сравним на одних данных — User(id=150, name="Al"):
Wire Size: TBinary vs TCompact vs Protobuf
КомпонентЧасть сообщения.
TBinaryTBinaryProtocol: fixed-size headers.
TCompactTCompactProtocol: varint + delta.
ProtobufProtocol Buffers: varint field tags.
id headerField header для id (int32, field 1).
type(1B) + field_id(2B) = 3 байта.
delta header: 1 байт.
field tag: 1 varint = 1 байт.
id valueValue: 150 (int32).
4 байта big-endian.
zigzag(150) = varint(300) = 2 байта.
varint(150) = 2 байта.
name headerField header для name (string, field 2).
type(1B) + field_id(2B) = 3 байта.
delta header: 1 байт.
field tag: 1 varint = 1 байт.
name valueValue: 'Al' (string).
length(4B) + 'Al'(2B) = 6 байт.
length(1B varint) + 'Al'(2B) = 3 байта.
length(1B varint) + 'Al'(2B) = 3 байта.
stop/totalStop byte / итого.
stop byte: 1 байт. Итого: 17 байт.
stop byte: 1 байт. Итого: 8 байт.
Нет stop byte. Итого: 7 байт.
TCompactProtocol почти равен Protobuf по компактности. Разница — 1 байт stop byte. При больших сообщениях overhead стремится к нулю.
Schema Evolution в Thrift
Thrift поддерживает эволюцию схемы аналогично Protobuf: field IDs на wire позволяют пропускать неизвестные поля.
Thrift Schema Evolution
Добавление поляНовый field ID + optional (или с default). Старые клиенты пропускают неизвестный field ID по type byte. Новые клиенты используют default если поле отсутствует.
Удаление поляПрекратить отправку. Нет аналога reserved — ответственность разработчика не переиспользовать field ID. Старые клиенты: поле отсутствует → default.
Thrift requiredThrift до сих пор поддерживает required — и это те же проблемы что в proto2. Required поле нельзя удалить без координированного обновления. Recommendation: не использовать required.
Нет reserved keywordThrift не имеет reserved keyword. Защита от переиспользования field IDs — только дисциплина команды и code review. Это слабее чем Protobuf reserved.
Thrift в современной экосистеме
Где Thrift используется сегодня
Активное использованиеКрупные компании со legacy Thrift инфраструктурой. Meta (Facebook) — основной пользователь, fbthrift. Apache ecosystem: HBase, Cassandra, Hive используют Thrift RPC.
Тренд: миграция на gRPCНовые проекты выбирают gRPC (Protobuf + HTTP/2). Причины: лучший tooling, HTTP/2 load balancing, streaming, interceptors, grpc-web для браузеров. Thrift — legacy choice.
NOTE
Meta поддерживает собственный форк — fbthrift — с улучшениями: Rocket (async server framework), streaming RPC, и интеграция с Folly (C++ library). Apache Thrift 0.22.0 и fbthrift — это разные, несовместимые реализации.
Доступ закрыт
Требуется вход
Для доступа к материалам курса необходимо войти через Telegram
Проверьте понимание
Результат: 0 из 0
Концептуальный
Закончили урок?
Отметьте его как пройденный, чтобы отслеживать свой прогресс