Seriál gRPC - Jak fungují Protocol Buffers
Protocol Buffers představují multiplatformní a jazykově neutrální mechanismus pro binární serializaci dat. Protobuf je jednoduchý předpis, který popisuje webovou službu a zprávy, které odesílá a přijímá. Bohatá nabídka nástrojů pro různé platformy umožňuje na základě specifikace Protobuf generovat například klientské knihovny nebo dokumentaci. Serializace pomocí Protobuf je výrazně rychlejší než v případě jiných formátů (JSON, XML) a zároveň úspornější z hlediska přenosu dat.
Použití Protobuf formátu umožňuje použití širokého spektra nástrojů, které umožňují vygenerovat klientský a serverový kód v různých programovacích jazycích. Od počátku se jedná o výhodu, která eliminuje zbytečné debaty ohledně formátu dat a šetří tak vývojářům čas.
Co jsou Protocol Buffers
Protocol Buffers zastávají mnoho funkcí. Ve svém základu se jedná o jazykově neutrální formát dat, kterým lze popsat název služeb, procedur a zpráv. Protocol Buffers se obvykle zkracují na Protobuf a jedná se o soubory s příponou .proto
.
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
repeated Product products = 4;
}
První řádek specifikuje verzi syntaxe. V současné době se používá verze 3, která přinesla oproti verzi 2 mnoho drobných změn. V ukázce je vidět definice zprávy, která se skládá ze tří vlastností. Každá vlastnost obsahuje datový typ, název a pořadí. Právě pořadí prvků je při serializaci klíčové a každý prvek musí být označen unikátním číslem. Rozsah čísel je velký, použít však nelze rozsah 19000 až 19999.
Jednotlivé prvky se mohou vyskytovat jako samostatné vlastnosti (např.: page_number) a nebo jako kolekce. Kolekce jsou označeny klíčovým slovem repeated
. Vedle primitivních datových typů lze odkazovat i na jiné další Protobuf kontrakty.
Protobuf je možné pomocí mnoha nástrojů generovat do podoby tříd v různých jazycích. Na platformě .NET se používá Protobuf Compiler, který umí překompilovat proto soubory do C# tříd. Snaží se přitom odhadnout vhodný primitivní typ mezi proto a CLR typem. Mezi nejčastěji používané primitivní typy a jejich ekvivalent v CLR patří:
- double -> double
- float -> float
- int32 -> int
- int64 -> long
- uint32 -> uint
- uint64 -> ulong
- sint32 -> int
- sint64 -> long
- fixed32 -> uint
- fixed64 -> ulong
- sfixed32 -> int
- sfixed64 -> long
- bool -> bool
- string -> string
- bytes -> ByteString
Na první pohled nabízí gRPC mnoho různých číselných typů. Některé jsou výhodnější pro kladná čísla, jiná pro čísla s fixní délkou. Jednotlivé typy nejsou nikdy NULL, čili vždy musí mít hodnotu. Výchozí hodnoty jsou stejné, jako je tomu v .NETu. Například int32 má výchozí hodnotu 0. Protobuf podporuje také enumy, u kterých je výchozí první hodnota, která musí mít zástupné číslo 0.
gRPC + JSON
Ačkoliv to nepovažuji za nejlepší praxi, v mnoha případech lze string vlastnost použít jako nositele jiného formátu dat. V zásadě tak lze vytvořit jednoduchou zprávu se stringovou vlastností, jejíž hodnotou je například JSON. V takovém případě zavádíme 2nd level serializaci, jelikož před serializací Protobuf ještě potřebujeme serializovat určitý objekt do JSON formátu. V případě streamování však není zpomalení tak výrazné a tento přístup může některým týmům vyhovovat.
Boj s datovými typy
Použití Protobuf může být ze začátku nezvyk. Zejména matoucí je fakt, že nemáme klasické nullable types a chybí některé datové typy jako DateTime nebo decimal. Místo klasického použití C# tříd musíme vždy definovat nejprve Protobuf message. Tento princip je často označován jako contract-first a usnadňuje návrh webových služeb. Protobuf je minimalistický zejména proto, aby svou jednoduchostí pokryl všechny běžné platformy. Proto neobsahuje specifika jednotlivých programovacích jazyků.
Naštěstí je možné vytvářet si i vlastní proto messages, takže si takové jednodušší suplementy decimal typu lze napsat ručně. Nejčastější běžně používané typy dokonce nachystal Google, autor formátu Protocol Buffers. Tyto známé typy se označují jako tzv. well-known types a lze si je zapojit do projektu jako NuGet balíček. Zpřístupní nám například:
- Timestamp (pro uchování data a času)
- BoolValue, Int32Value... (a další ekvivalenty pro nullable typy)
Jde to i bez Protobuf
Protocol Buffers oceníme zejména při návrhu multiplatformních gRPC služeb. V případě služeb, které používáme výhradně v .NET ekosystému existuje pohodlnější cesta tvorby kontraktů zcela bez proto souborů. Podívejte se na stránku návrh gRPC služeb, kde se dozvíte více.