Introduction au tampon de protocole Google

Introduction à Google Protocol Buffer

1. Vue d'ensemble

Dans cet article, nous examinerons leGoogle Protocol Buffer (protobuf) - un format de données binaire indépendant du langage bien connu. Nous pouvons définir un fichier avec un protocole et ensuite, en utilisant ce protocole, nous pouvons générer du code dans des langages tels que Java, C ++, C #, Go ou Python.

Ceci est un article d'introduction au format lui-même; si vous voulez savoir comment utiliser le format avec une application Web Spring, jetez un œil àthis article.

2. Définition des dépendances Maven

Pour utiliser les tampons de protocole est Java, nous devons ajouter une dépendance Maven à unprotobuf-java:


    com.google.protobuf
    protobuf-java
    ${protobuf.version}



    3.2.0

3. Définition d'un protocole

Commençons par un exemple. Nous pouvons définir un protocole très simple dans un format protobuf:

message Person {
    required string name = 1;
}

Il s'agit d'un protocole d'un simple message de typePerson qui n'a qu'un seul champ obligatoire - nom qui a un typestring.

Examinons l'exemple plus complexe de la définition d'un protocole. Supposons que nous ayons besoin de stocker les détails de la personne dans un format protobuf:

l'emballage protobuf;

package protobuf;

option java_package = "com.example.protobuf";
option java_outer_classname = "AddressBookProtos";

message Person {
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;

    repeated string numbers = 4;
}

message AddressBook {
    repeated Person people = 1;
}

Notre protocole se compose de deux types de données: unPerson et unAddressBook. Après avoir généré le code (plus à ce sujet dans la section ultérieure), ces classes seront les classes internes à l'intérieur desAddressBookProtos classe.

Lorsque nous voulons définir un champ obligatoire - ce qui signifie que la création d'un objet sans un tel champ provoquera unException, nous devons utiliser un mot clérequired.

La création d'un champ avec le mot-cléoptional signifie que ce champ n'a pas besoin d'être défini. Le mot clérepeated est un type de tableau de taille variable.

Tous les champs sont indexés - le champ désigné par le numéro 1 sera enregistré en tant que premier champ dans un fichier binaire. Le champ marqué d'un 2 sera sauvegardé ensuite et ainsi de suite. Cela nous donne un meilleur contrôle sur la manière dont les champs sont disposés dans la mémoire.

4. Génération de code Java à partir d'un fichier Protobuf

Une fois que nous avons défini un fichier, nous pouvons générer du code à partir de celui-ci.

Tout d'abord, nous avons besoin deinstall protobuf sur notre machine. Une fois que nous faisons cela, nous pouvons générer du code en exécutant une commandeprotoc:

protoc -I=. --java_out=. addressbook.proto

La commandeprotoc générera un fichier de sortie Java à partir de notre fichieraddressbook.proto. L'option-I spécifie un répertoire dans lequel réside un fichierproto. Lejava-out spécifie un répertoire dans lequel la classe générée sera créée. __

La classe générée aura des setters, des getters, des constructeurs et des générateurs pour nos messages définis. Il disposera également de méthodes utiles pour enregistrer les fichiers protobuf et les désérialiser du format binaire à la classe Java.

5. Création d'une instance de messages définis par Protobuf

Nous pouvons facilement utiliser un code généré pour créer une instance Java d'une classePerson:

String email = "[email protected]";
int id = new Random().nextInt();
String name = "Michael Program";
String number = "01234567890";
AddressBookProtos.Person person =
  AddressBookProtos.Person.newBuilder()
    .setId(id)
    .setName(name)
    .setEmail(email)
    .addNumbers(number)
    .build();

assertEquals(person.getEmail(), email);
assertEquals(person.getId(), id);
assertEquals(person.getName(), name);
assertEquals(person.getNumbers(0), number);

Nous pouvons créer un générateur fluide en utilisant une méthodenewBuilder() sur le type de message souhaité. Après avoir configuré tous les champs obligatoires, nous pouvons appeler une méthodebuild() pour créer une instance d'une classePerson.

6. Sérialisation et désérialisation de Protobuf

Une fois que nous avons créé une instance de notre classePerson, nous voulons la sauvegarder sur disque dans un format binaire compatible avec un protocole créé. Disons que nous voulons créer une instance de la classeAddressBook et ajouter une personne à cet objet.

Ensuite, nous voulons enregistrer ce fichier sur le disque - il existe une méthode utilwriteTo() dans le code généré automatiquement que nous pouvons utiliser:

AddressBookProtos.AddressBook addressBook
  = AddressBookProtos.AddressBook.newBuilder().addPeople(person).build();
FileOutputStream fos = new FileOutputStream(filePath);
addressBook.writeTo(fos);

Après avoir exécuté cette méthode, notre objet sera sérialisé au format binaire et enregistré sur disque. Pour charger ces données à partir d'un disque et les désérialiser vers l'objetAddressBook, nous pouvons utiliser une méthodemergeFrom():

AddressBookProtos.AddressBook deserialized
  = AddressBookProtos.AddressBook.newBuilder()
    .mergeFrom(new FileInputStream(filePath)).build();

assertEquals(deserialized.getPeople(0).getEmail(), email);
assertEquals(deserialized.getPeople(0).getId(), id);
assertEquals(deserialized.getPeople(0).getName(), name);
assertEquals(deserialized.getPeople(0).getNumbers(0), number);

7. Conclusion

Dans cet article rapide, nous avons introduit une norme de description et de stockage des données dans un format binaire: le tampon de protocole Google.

Nous avons créé un protocole simple et créé une instance Java conforme au protocole défini. Ensuite, nous avons vu comment sérialiser et désérialiser des objets en utilisant protobuf.

L'implémentation de tous ces exemples et extraits de code peut être trouvée dans leGitHub project - il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.