Warum Protobuf das Datenformat-Ökosystem dominieren sollte
James Reed
Infrastructure Engineer · Leapcell

Tiefes Verständnis von Protobuf
Was ist Protobuf
Protobuf (Google Protocol Buffers), wie in der offiziellen Dokumentation definiert: Protocol Buffers sind eine sprachunabhängige, plattformunabhängige und erweiterbare Methode zur Serialisierung strukturierter Daten, die in Szenarien wie Datenkommunikationsprotokollen und Datenspeicherung breit angewendet werden kann. Es ist eine von Google bereitgestellte Tool-Bibliothek mit einem effizienten Protokoll-Datenaustauschformat, das sich durch flexible, effiziente und automatisierte Serialisierungsmechanismen für strukturierte Daten auszeichnet.
Im Vergleich zu XML ist die Größe der von Protobuf codierten Daten kleiner und die Codierungs- und Decodierungsgeschwindigkeit ist höher. Im Vergleich zu JSON ist Protobuf in Bezug auf die Konvertierungseffizienz hervorragender, wobei sowohl seine Zeiteffizienz als auch seine Raumeffizienz das 3- bis 5-fache von JSON erreichen.
Wie die offizielle Beschreibung besagt: „Protocol Buffers sind Googles sprachneutraler, plattformneutraler, erweiterbarer Mechanismus zur Serialisierung strukturierter Daten – denken Sie an XML, aber kleiner, schneller und einfacher. Sie definieren einmal, wie Ihre Daten strukturiert sein sollen, und können dann speziellen generierten Quellcode verwenden, um Ihre strukturierten Daten einfach in eine Vielzahl von Datenströmen und aus diesen zu schreiben und eine Vielzahl von Sprachen zu verwenden.“
Vergleich von Datenformaten
Angenommen, wir haben ein person
-Objekt, das durch JSON, XML bzw. Protobuf dargestellt wird, und sehen wir uns ihre Unterschiede an.
XML-Format
<person> <name>John</name> <age>24</age> </person>
JSON-Format
{ "name":"John", "age":24 }
Protobuf-Format
Protobuf stellt Daten direkt im Binärformat dar, das nicht so intuitiv ist wie XML- und JSON-Formate. Zum Beispiel:
[10 6 69 108 108 122 111 116 16 24]
Vorteile von Protobuf
Gute Leistung/Hohe Effizienz
- Zeitlicher Mehraufwand: Der Mehraufwand für die XML-Formatierung (Serialisierung) ist akzeptabel, aber der Mehraufwand für das XML-Parsen (Deserialisierung) ist relativ groß. Protobuf hat diesen Aspekt optimiert und kann den zeitlichen Mehraufwand für die Serialisierung und Deserialisierung erheblich reduzieren.
- Platzbedarf: Protobuf reduziert auch den Platzbedarf erheblich.
Code-Generierungsmechanismus
Schreiben Sie beispielsweise den folgenden Inhalt, der einer Struktur ähnelt:
message testA { required int32 m_testA = 1; }
Protobuf kann automatisch die entsprechenden .h
- und .cpp
-Dateien generieren und die Operationen an der Struktur testA
in einer Klasse kapseln.
Unterstützung für Abwärtskompatibilität und Vorwärtskompatibilität
Wenn der Client und der Server gleichzeitig ein Protokoll verwenden und der Client ein Byte im Protokoll hinzufügt, hat dies keine Auswirkungen auf die normale Verwendung des Clients.
Unterstützung für mehrere Programmiersprachen
Im von Google offiziell veröffentlichten Quellcode ist die Unterstützung für mehrere Programmiersprachen enthalten, z. B.:
- C++
- C#
- Dart
- Go
- Java
- Kotlin
- Python
Nachteile von Protobuf
Schlechte Lesbarkeit aufgrund des Binärformats
Um die Leistung zu verbessern, verwendet Protobuf ein Binärformat für die Codierung, wodurch die Daten weniger lesbar sind und die Effizienz während der Entwicklungs- und Testphase beeinträchtigt wird. Unter normalen Umständen arbeitet Protobuf jedoch sehr zuverlässig, und es treten im Allgemeinen keine schwerwiegenden Probleme auf.
Fehlende Selbstbeschreibung
Im Allgemeinen ist XML selbstbeschreibend, während das Protobuf-Format dies nicht ist. Es handelt sich um ein binäres Protokoll, und es ist schwierig, seine Funktion zu kennen, ohne es mit einer zuvor geschriebenen Struktur abzugleichen.
Schlechte Universalität
Obwohl Protobuf die Serialisierung und Deserialisierung in mehreren Sprachen unterstützt, ist es kein universeller Übertragungsstandard über Plattformen und Sprachen hinweg. In Szenarien der plattformübergreifenden Nachrichtenübermittlung ist die Kompatibilität mit anderen Projekten nicht gut, und es sind häufig entsprechende Anpassungs- und Transformationsarbeiten erforderlich. Im Vergleich zu JSON und XML ist seine Universalität etwas unzureichend.
Nutzungsanleitung
Definition von Nachrichtentypen
Proto-Nachrichtentypdateien enden im Allgemeinen mit .proto
. In einer .proto
-Datei können ein oder mehrere Nachrichtentypen definiert werden.
Das Folgende ist ein Beispiel für die Definition eines Nachrichtentyps für eine Suchabfrage. Die syntax
am Anfang der Datei wird verwendet, um die Versionsinformationen zu beschreiben. Derzeit gibt es zwei Proto-Versionen, Proto2 und Proto3.
syntax="proto3";
Syntaxformat explizit auf Proto3 setzen. Wenn die syntax
nicht festgelegt ist, wird standardmäßig Proto2 verwendet. query
steht für den abzufragenden Inhalt, page_number
für die Seitenzahl der Abfrage und result_per_page
für die Anzahl der Elemente pro Seite. syntax = "proto3"
muss sich in der ersten Zeile der .proto
-Datei (mit Ausnahme von Kommentaren und Leerzeilen) befinden.
Die folgende Nachricht enthält 3 Felder (query
, page_number
, result_per_page
), und jedes Feld hat einen entsprechenden Typ, Feldnamen und Feldnummer. Der Feldtyp kann string
, int32
, enum
oder ein zusammengesetzter Typ sein.
syntax = "proto3"; message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; }
Feldnummern
Jedes Feld im Nachrichtentyp muss mit einer eindeutigen Nummer definiert werden, und diese Nummer wird verwendet, um das Feld in den Binärdaten zu identifizieren. Zahlen im Bereich von [1,15] können mit einem Byte codiert und dargestellt werden; im Bereich von [16,2047] müssen sie mit zwei Bytes codiert und dargestellt werden. Daher kann das Freilassen der Zahlen innerhalb von 15 für häufig vorkommende Felder Platz sparen. Der Mindestwert der Zahl ist 1, und der Höchstwert ist 2^29 - 1 = 536870911. Zahlen im Bereich von [19000, 19999] können nicht verwendet werden, da diese Zahlen intern vom Proto-Compiler verwendet werden. Ebenso können auch andere vorreservierte Zahlen nicht verwendet werden.
Feldregeln
Jedes Feld kann durch singular
oder repeated
geändert werden. In der Proto3-Syntax wird standardmäßig der Wert singular
verwendet, wenn der Änderungstyp nicht angegeben ist.
singular
: Dies bedeutet, dass das geänderte Feld maximal einmal vorkommt, d. h. es wird 0 oder 1 Mal angezeigt.repeated
: Dies bedeutet, dass das geänderte Feld beliebig oft vorkommen kann, auch 0 Mal. In der Proto3-Syntax verwenden Felder, die durchrepeated
geändert werden, standardmäßig diepacked
-Codierung.
Kommentare
Sie können der .proto
-Datei Kommentare hinzufügen. Die Kommentarsyntax ist die gleiche wie im C/C++-Stil und verwendet //
oder /* ... */
.
/* SearchRequest represents a search query, with pagination options to * indicate which results to include in the response. */ message SearchRequest { string query = 1; int32 page_number = 2; // Which page number do we want? int32 result_per_page = 3; // Number of results to return per page. }
Reservierte Felder
Beim Löschen oder Auskommentieren eines Felds in einer message
können andere Entwickler in Zukunft die vorherige Feldnummer wiederverwenden, wenn sie die message
-Definition aktualisieren. Wenn sie versehentlich die alte Version der .proto
-Datei laden, kann dies zu schwerwiegenden Problemen wie Datenbeschädigung führen. Um solche Probleme zu vermeiden, können Sie die reservierten Feldnummern und Feldnamen angeben. Wenn jemand diese Feldnummern in Zukunft verwendet, wird beim Kompilieren des Proto ein Fehler erzeugt, der daran erinnert, dass es ein Problem mit dem Proto gibt.
Hinweis: Verwenden Sie Feldnamen und Feldnummern nicht für dasselbe Feld.
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }
Zuordnung zwischen Feldtypen und Sprachypen
Die definierte .proto
-Datei kann über einen Generator Go-Sprachcode generieren. Beispielsweise ist die aus der a.proto
-Datei generierte Go-Datei die Datei a.pb.go
.
Die Zuordnung zwischen grundlegenden Typen in Proto und Go-Sprachypen ist in der folgenden Tabelle dargestellt (hier wird nur die Typzuordnung zwischen Go und C/C++ aufgeführt. Informationen zu anderen Sprachen finden Sie unter https://developers.google.com/protocol-buffers/docs/proto3):
.proto Type | Go Type | C++ Type |
---|---|---|
double | float64 | double |
float | float32 | float |
int32 | int32 | int32 |
int64 | int64 | int64 |
uint32 | uint32 | uint32 |
uint64 | uint64 | uint64 |
sint32 | int32 | int32 |
sint64 | int64 | int64 |
fixed32 | uint32 | uint32 |
fixed64 | uint64 | uint64 |
sfixed32 | int32 | int32 |
sfixed64 | int64 | int64 |
bool | bool | bool |
string | string | string |
bytes | []byte | string |
Standardwerte
.proto Type | default value |
---|---|
string | "" |
bytes | []byte |
bool | false |
numeric types | 0 |
enums | first defined enum value |
Enum-Typen
Wenn Sie eine Nachricht definieren und möchten, dass der Wert eines Felds nur einer der erwarteten Werte ist, können Sie den Enum-Typ verwenden.
Fügen Sie beispielsweise jetzt das Feld corpus
zu SearchRequest
hinzu, und sein Wert kann nur einer von UNIVERSAL
, WEB
, IMAGES
, LOCAL
, NEWS
, PRODUCTS
und VIDEO
sein. Dies kann erreicht werden, indem Sie der Nachrichtendefinition eine Enum hinzufügen und eine Konstante für jeden möglichen Enum-Wert hinzufügen.
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 4; }
Die erste Konstante der Corpus
-Enum muss 0 zugeordnet werden, und alle Enum-Definitionen müssen eine Konstante enthalten, die 0 zugeordnet ist, und dieser Wert ist der Inhalt der ersten Zeile der Enum-Definition. Dies liegt daran, dass 0 als Standardwert der Enum verwendet wird. In der Proto2-Syntax ist der Enum-Wert in der ersten Zeile immer der Standardwert. Aus Gründen der Kompatibilität muss der Wert 0 die erste Zeile der Definition sein.
Importieren anderer Protos
Andere .proto
-Dateien können in eine .proto
-Datei importiert werden, um die in der importierten Datei definierten Nachrichtentypen zu verwenden.
import "myproject/other_protos.proto";
Standardmäßig können nur die in der direkt importierten .proto
-Datei definierten Nachrichtentypen verwendet werden. Manchmal kann es jedoch erforderlich sein, die .proto
-Datei an einen neuen Speicherort zu verschieben. Zu diesem Zeitpunkt kann eine virtuelle .proto
-Datei am alten Speicherort platziert werden, und die Syntax import public
kann verwendet werden, um alle Importe an den neuen Speicherort weiterzuleiten, anstatt die .proto
-Datei direkt zu verschieben und alle Aufrufstellen auf einmal zu aktualisieren. Jeder Ort, der eine Proto-Datei importiert, die die Anweisung import public
enthält, kann die öffentlichen Abhängigkeiten der importierten Abhängigkeiten weitergeben.
Angenommen, es gibt die Dateien a.proto
und b.proto
im aktuellen Ordner, und b.proto
wird in der Datei a.proto
importiert, d. h. die Datei a.proto
hat den folgenden Inhalt:
import "b.proto";
Nehmen wir an, wir möchten jetzt die Nachrichten in b.proto
zur Verwendung an anderen Orten in die Datei common/com.proto
einfügen. Wir können b.proto
ändern und com.proto
darin importieren. Beachten Sie, dass wir import public
verwenden müssen, da ein einzelner import
nur die in b.proto
definierten Nachrichten verwenden und die Nachrichtentypen in der in b.proto
importierten Proto-Datei nicht verwenden kann.
// b.proto file, move the message definitions inside to the common/com.proto file, // add the following import statement inside import public "common/com.proto"
Bei Verwendung von protoc
zur Kompilierung muss die Option -I
oder --proto_path
verwendet werden, um protoc
mitzuteilen, wo die importierten Dateien zu finden sind. Wenn der Suchpfad nicht angegeben wird, sucht protoc
im aktuellen Verzeichnis (dem Pfad, in dem protoc
aufgerufen wird).
Nachrichtentypen in der Proto2-Version können zur Verwendung in eine Proto3-Datei importiert werden, und Nachrichtentypen in der Proto3-Version können auch in eine Proto2-Datei importiert werden. Die Enum-Typen in Proto2 können jedoch nicht direkt auf die Proto3-Syntax angewendet werden.
Verschachtelte Nachrichten
Nachrichtentypen können innerhalb eines anderen Nachrichtentyps definiert werden, d. h. verschachtelte Definitionen. Beispielsweise ist der Typ Result
innerhalb von SearchResponse
definiert und unterstützt mehrere Verschachtelungsebenen.
message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; }
Wenn ein äußerer Nachrichtentyp eine Nachricht innerhalb einer anderen Nachricht verwendet, z. B. der Typ SomeOtherMessage
, der Result
verwendet, kann er SearchResponse.Result
verwenden.
message SomeOtherMessage { SearchResponse.Result result = 1; }
Unbekannte Felder
Unbekannte Felder sind Felder, die der Proto-Compiler nicht erkennen kann. Wenn beispielsweise eine alte Binärdatei die von einer neuen Binärdatei mit neuen Feldern gesendeten Daten parst, werden diese neuen Felder in der alten Binärdatei zu unbekannten Feldern. In der ersten Version von Proto3 wurden unbekannte Felder beim Parsen der Nachricht verworfen, aber in Version 3.5 wurde die Beibehaltung unbekannter Felder wieder eingeführt. Unbekannte Felder werden während des Parsens beibehalten und sind in der serialisierten Ausgabe enthalten.
Codierungsprinzip
TLV-Codierungsformat
Der Schlüssel zur hohen Effizienz von Protobuf liegt in seinem TLV-Codierungsformat (Tag-Length-Value). Jedes Feld hat einen eindeutigen tag
-Wert als Kennung, length
stellt die Länge der value
-Daten dar (für einen value
mit fester Länge gibt es keine length
) und value
ist der Inhalt der Daten selbst.
Für den tag
-Wert besteht er aus zwei Teilen: field_number
und wire_type
. field_number
ist die Nummer, die jedem Feld in der message
zuvor zugewiesen wurde, und wire_type
stellt den Typ dar (feste Länge oder variable Länge). Der wire_type
hat derzeit 6 Werte von 0 bis 5, und diese 6 Werte können durch 3 Bits dargestellt werden.
Die Werte von wire_type
sind in der folgenden Tabelle dargestellt, wobei 3 und 4 als veraltet gelten und wir nur auf die verbleibenden 4 Typen achten müssen. Für mit Varint codierte Daten ist es nicht erforderlich, die Bytelänge length
zu speichern, und zu diesem Zeitpunkt degeneriert das TLV-Codierungsformat zu TV-Codierung. Für 64-Bit- und 32-Bit-Daten ist length
ebenfalls nicht erforderlich, da der type
-Wert bereits angibt, ob die Länge 8 Byte oder 4 Byte beträgt.
wire_type | Encoding Method | Encoding Length | Storage Method | Data Type |
---|---|---|---|---|
0 | Varint | Variable length | T - V | int32 int64 uint32 uint64 bool enum |
0 | Zigzag + Varint | Variable length | T - V | sint32 sint64 |
1 | 64-bit | Fixed 8 bytes | T - V | fixed64 sfixed64 double |
2 | length-delimi | Variable length | T - L - V | string bytes packed repeated fields embedded |
3 | start group | Deprecated | Deprecated | |
4 | end group | Deprecated | Deprecated | |
5 | 32-bit | Fixed 4 bytes | T - V | fixed32 sfixed32 float |
Varint-Codierungsprinzip
Varint ist ein Int mit variabler Länge, d. h. eine Codierungsmethode mit variabler Länge. Dadurch können kleinere Zahlen weniger Bytes zur Darstellung verwenden und die Datenkomprimierung erreicht werden, indem die Anzahl der zur Darstellung von Zahlen verwendeten Bytes reduziert wird. Für eine Zahl vom Typ Int32 sind normalerweise 4 Byte zur Darstellung erforderlich, aber mit der Varint-Codierung kann eine Zahl vom Typ Int32, die kleiner als 128 ist, mit 1 Byte dargestellt werden. Für größere Zahlen sind möglicherweise 5 Byte zur Darstellung erforderlich, aber in den meisten Nachrichten kommen sehr große Zahlen normalerweise nicht vor, sodass die Verwendung der Varint-Codierung weniger Byte zur Darstellung von Zahlen verwenden kann.
Varint ist eine Codierung mit variabler Länge, und sie unterscheidet jedes Feld durch das höchstwertige Bit jedes Bytes. Wenn das höchstwertige Bit eines Bytes 1 ist, bedeutet dies, dass das nachfolgende Byte auch Teil der Zahl ist; wenn es 0 ist, bedeutet dies, dass es sich um das letzte Byte handelt und die verbleibenden 7 Bits alle zur Darstellung der Zahl verwendet werden. Obwohl jedes Byte 1 Bit Speicherplatz verschwendet (d. h. 1/8 = 12,5 % Verschwendung), kann dennoch viel Speicherplatz gespart werden, wenn es viele Zahlen gibt, die nicht als 4 Byte zur Darstellung festgelegt werden müssen.
Beispielsweise sieht der Varint-Codierungsprozess für eine Zahl vom Typ Int32 65 wie folgt aus, und die 65, die ursprünglich 4 Byte belegten, belegen nach der Codierung nur 1 Byte.
Für eine Zahl vom Typ Int32 128 belegen 2 Byte nach der Codierung.
Die Varint-Decodierung ist der umgekehrte Prozess der Codierung, der relativ einfach ist, und hier wird kein Beispiel gegeben.
Zigzag-Codierung
Zahlen in nicht signierte Zahlen und verwenden dann die Varint-Codierung, um die Anzahl der Bytes nach der Codierung zu reduzieren.
Zigzag verwendet nicht signierte Zahlen, um signierte Zahlen darzustellen, sodass Zahlen mit kleineren absoluten Werten mit weniger Bytes dargestellt werden können. Bevor wir die Zigzag-Codierung verstehen, wollen wir zunächst einige Konzepte verstehen:
- Original Code: Das höchstwertige Bit ist das Vorzeichenbit, und die verbleibenden Bits stellen den absoluten Wert dar.
- Einerkomplement: Mit Ausnahme des Vorzeichenbits werden die verbleibenden Bits des ursprünglichen Codes einzeln invertiert.
- Zweierkomplement: Für positive Zahlen ist das Zweierkomplement sich selbst; für negative Zahlen werden mit Ausnahme des Vorzeichenbits die verbleibenden Bits des Originalcodes einzeln invertiert und dann 1 addiert.
Nehmen wir die Zahl vom Typ Int32 -2 als Beispiel, und ihr Codierungsprozess sieht wie folgt aus.
Zusammenfassend lässt sich sagen, dass für negative Zahlen arithmetische Operationen für ihr Zweierkomplement durchgeführt werden. Für eine Zahl n
führen Sie, wenn sie vom Typ sint32
ist, die Operation (n<<1) ^ (n>>31)
aus; wenn sie vom Typ sint64
ist, führen Sie die Operation (n<<1) ^ (n>>63)
aus. Durch diese Operation wird die negative Zahl in eine positive Zahl geändert, und dieser Prozess ist die Zigzag-Codierung. Verwenden Sie abschließend die Varint-Codierung.
Da die Varint- und Zigzag-Codierung die Inhaltslänge selbst parsen können, kann das Längenelement weggelassen werden und die TLV-Speicherung wird zu TV-Speicherung vereinfacht, ohne dass das length
-Element erforderlich ist.
Berechnungsmethoden für tag- und value-Werte
tag
Der tag
speichert die Identifikationsinformationen und Datentypinformationen des Felds, d. h. tag = wire_type
(Felddatentyp) + field_number
(Identifikationsnummer). Die Feldnummer kann über den tag
abgerufen werden, der dem definierten Nachrichtenfeld entspricht. Die Berechnungsformel lautet tag = field_number<<3 | wire_type
und führen Sie dann die Varint-Codierung darauf aus.
value
Der value
ist der Wert des Nachrichtenfelds nach der Varint- und Zigzag-Codierung.
string Codierung (fortgesetzt)
Wenn der Feldtyp der Typ string
ist, wird der Feldwert in UTF-8 codiert. Beispielsweise gibt es die folgende Nachrichtendefinition:
message stringEncodeTest { string test = 1; }
In der Go-Sprache lautet der Beispielcode zum Codieren dieser Nachricht wie folgt:
func stringEncodeTest(){ vs:=&api.StringEncodeTest{ Test:"English", } data,err:=proto.Marshal(vs) if err!=nil{ fmt.Println(err) return } fmt.Printf("%v\n",data) }
Der Binärinhalt nach der Codierung lautet wie folgt:
[10 14 67 104 105 110 97 228 184 173 144 155 189 228 120 186]
Codierung verschachtelte Typen
Verschachtelte Nachrichten bedeuten, dass der value
eine andere Feldnachricht ist. Die äußere Nachricht wird mit TLV-Speicherung gespeichert, und ihr value
ist auch eine TLV-Speicherstruktur. Das schematische Diagramm der gesamten Codierungsstruktur sieht wie folgt aus (es kann als Baumstruktur vorgestellt werden, wobei die äußere Nachricht der Stammknoten ist und die darin verschachtelte Nachricht als untergeordneter Knoten verwendet wird, und jeder Knoten folgt der TLV-Codierungsregel):
- Die äußerste Nachricht hat ihren entsprechenden
tag
,length
(falls vorhanden) undvalue
. - Wenn der
value
eine verschachtelte Nachricht ist, hat diese verschachtelte Nachricht ihren eigenen unabhängigentag
,length
(falls vorhanden) undvalue
. - In Analogie dazu fahren Sie, wenn es verschachtelte Nachrichten innerhalb der verschachtelten Nachricht gibt, mit der Codierung gemäß der TLV-Regel fort.
repeated Felder mit packed
Die durch repeated
geänderten Felder können mit packed
oder ohne sie versehen sein. Für mehrere Feldwerte desselben repeated
-Felds sind ihre tag
-Werte alle gleich, d. h. der Datentyp und die Feldsequenznummer sind gleich. Wenn mehrere TV
-Speicher verwendet werden, liegt eine Redundanz des tag
vor.
Wenn packed = true
festgelegt ist, wird die Speichermethode des repeated
-Felds optimiert. Das heißt, derselbe tag
wird nur einmal gespeichert, und dann wird die Gesamtlänge length
aller Werte unter dem repeated
-Feld hinzugefügt, um eine TLVV...
-Speicherstruktur zu bilden. Diese Methode kann die Länge der serialisierten Daten effektiv komprimieren und Übertragungsaufwand sparen. Zum Beispiel:
message repeatedEncodeTest{ // Method 1, without packed repeated int32 cat = 1; // Method 2, with packed repeated int32 dog = 2 [packed=true]; }
Im obigen Beispiel verwendet das Feld cat
nicht packed
, und jeder cat
-Wert verfügt über eine unabhängige tag
- und value
-Speicherung; während das Feld dog
packed
verwendet und der tag
nur einmal gespeichert wird, gefolgt von der Gesamtlänge length
aller dog
-Werte, und dann werden alle dog
-Werte der Reihe nach angeordnet. Auf diese Weise kann das repeated
-Feld, das packed
verwendet, bei großen Datenmengen den von den Daten belegten Speicherplatz und den Bandbreitenverbrauch während der Übertragung erheblich reduzieren.
Fazit
Mit seiner Effizienz (Größe) und Professionalität (professionelle Typen) sollte Protobuf in Zukunft eine höhere Abdeckung im Bereich der Datenübertragung haben.
Leapcell: Die Serverless-Plattform der nächsten Generation für Webhosting, asynchrone Aufgaben und Redis
Abschließend möchte ich Ihnen die am besten geeignete Plattform für die Bereitstellung von Diensten vorstellen: Leapcell
1. Unterstützung mehrerer Sprachen
- Entwickeln Sie mit JavaScript, Python, Go oder Rust.
2. Stellen Sie unbegrenzt viele Projekte kostenlos bereit
- Sie zahlen nur für die Nutzung – keine Anfragen, keine Gebühren.
3. Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 $ unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
4. Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
5. Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Kein Betriebsaufwand – konzentrieren Sie sich einfach auf den Aufbau.
Erfahren Sie mehr in der Dokumentation!
Leapcell Twitter: https://x.com/LeapcellHQ