Protokollpufferobjekte, die zur Laufzeit generiert werden

8

Ein Kollege von mir kam mit der Idee, Protokollpufferklassen zur Laufzeit zu erzeugen. Bedeutung:

  • Es gibt eine C ++ - Serveranwendung und eine Java-Client-Anwendung, die über TCP / IP über Protokollpuffer-Nachrichten kommunizieren.
  • Die C ++ - Anwendung kann in verschiedenen Versionen ein anderes Schema haben und dies ist nicht unbedingt abwärtskompatibel
  • Es gibt eine Java-Anwendung, die mit diesem Server kommuniziert und alle möglichen Serverversionen unterstützt.

Die Idee ist, dass der Server die Definition des Protokollpuffers als Teil des ersten Handshakes sendet und die Java-Anwendung die Klasse zur Laufzeit generiert und sie für die Kommunikation mit dem Server verwendet.

Ich frage mich, ob das überhaupt eine wichtige Idee ist und ob es möglicherweise einen Nutzen für einen solchen Anwendungsfall gibt.

Danke

    
Jan Zyka 16.09.2013, 20:29
quelle

2 Antworten

19

Was Sie beschreiben, wird tatsächlich bereits von den Protocol Buffers-Implementierungen in C ++ und Java unterstützt. Sie müssen lediglich ein FileDescriptorSet (wie in google/protobuf/descriptor.proto ), die FileDescriptorProto s für jede relevante .proto -Datei enthalten, dann DynamicMessage , um die Nachrichten am empfangenden Ende zu interpretieren.

Um ein FileDescriptorProto in C ++ zu erhalten, geben Sie den Nachrichtentyp Foo , der in dieser Datei definiert ist, wie folgt vor:

%Vor%

Legen Sie alle FileDescriptorProto s, die die benötigten Typen definieren, sowie alle importierten Dateien in ein FileDescriptorSet proto. Beachten Sie, dass Sie google::protobuf::FileDescriptor (das zurückgegebene Objekt) verwenden können by Foo::descriptor().file() ), um über Abhängigkeiten zu iterieren anstatt sie explizit zu benennen.

Senden Sie nun FileDescriptorSet an den Client.

Verwenden Sie auf dem Client FileDescriptor.buildFrom() konvertieren jedes FileDescriptorProto auf einen Live- Descriptors.FileDescriptor . Sie müssen sicherstellen, dass Abhängigkeiten vor Abhängigkeiten erstellt werden, da Sie die bereits erstellten Abhängigkeiten zu buildFrom() wenn die Unterhaltsberechtigten zu bauen.

Von dort können Sie die FileDescriptor 's findMessageTypeByName() , um die Descriptor für den spezifischen Nachrichtentyp, der Ihnen wichtig ist.

Schließlich können Sie rufen Sie DynamicMessage.newBuilder(descriptor) , um eine neue Builder-Instanz für den betreffenden Typ zu erstellen. DynamicMessage.Builder implementiert die Message.Builder Schnittstelle, die Felder wie getField() und < a href="https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/Message.Builder#setField(com.google.protobuf.Descriptors.FieldDescriptor,%20java.lang .Object) "> setField() , um die Felder der Nachricht dynamisch zu bearbeiten (durch Angabe der entsprechenden FieldDescriptor s).

Sie können auch DynamicMessage.parseFrom(descriptor,input) um die vom Server empfangenen Nachrichten zu analysieren.

Beachten Sie, dass ein Nachteil von DynamicMessage ist dass es relativ langsam ist. Im Grunde ist es wie eine interpretierte Sprache. Generierter Code ist schneller, da der Compiler für den bestimmten Typ optimiert werden kann, während DynamicMessage muss mit jedem Typ umgehen können.

Allerdings gibt es wirklich keinen Weg dazu. Selbst wenn Sie den Code-Generator ausführen und die Klasse zur Laufzeit kompilieren, wäre der Code, der die neue Klasse verwendet, immer noch Code, den Sie zuvor geschrieben haben, bevor Sie wussten, welchen Typ Sie verwenden würden. Daher muss es immer noch eine reflektions- oder reflexionsähnliche Schnittstelle verwenden, um auf die Nachricht zuzugreifen, und das wird langsamer sein, als wenn der Code für den spezifischen Typ handgeschrieben wäre.

Aber ist es eine gute Idee?

Nun, das hängt davon ab. Was wird der Client eigentlich tun, um mit diesem Schema, das er vom Server erhält, zu tun ?Das Übertragen eines Schemas über die Leitung macht den Client nicht mit der Version des Protokolls kompatibel - der Client muss verstehen , was das Protokoll bedeutet. Wenn das Protokoll rückwärtskompatibel geändert wurde, bedeutet dies fast sicher, dass sich die Bedeutung des Protokolls geändert hat und der Clientcode aktualisiert werden muss, Schemaübertragung oder nicht. Die einzige Zeit, in der Sie erwarten können, dass der Client ohne Aktualisierung weiterarbeitet, ist, wenn der Client nur eine generische Operation ausführt, die nur vom Nachrichteninhalt, nicht aber von der Nachrichtenbedeutung abhängt - beispielsweise könnte der Client die Nachricht in JSON konvertieren ohne zu wissen, was es bedeutet. Dies ist jedoch relativ ungewöhnlich, insbesondere am Client-Ende einer Anwendung. Aus diesem Grund sendet Protobufs standardmäßig keine Typinformationen - weil es normalerweise nutzlos ist, denn wenn der Empfänger die Bedeutung nicht kennt, ist das Schema irrelevant.

Wenn das Problem darin besteht, dass der Server Nachrichten an den Client sendet, die überhaupt nicht interpretiert werden sollen, sondern nur zu einem späteren Zeitpunkt an den Server zurückgeschickt werden, benötigt der Client das Schema überhaupt nicht . Übermitteln Sie die Nachricht einfach als bytes und analysieren Sie sie nicht. Beachten Sie, dass ein bytes -Feld, das eine codierte Nachricht vom Typ Foo enthält, auf dem Draht genauso aussieht wie ein Feld, dessen Typ tatsächlich als Foo deklariert ist. Sie könnten den Client und den Server gegen geringfügig unterschiedliche Versionen der Datei .proto kompilieren, wobei der Client ein bestimmtes Feld als bytes sieht, während der Server es als Unternachricht ansieht, um die Notwendigkeit für den Client zu vermeiden sich der Definition dieser Unternachricht bewusst sein.       ''

    
Kenton Varda 17.09.2013, 21:01
quelle
5

Für Java ist die folgende Wrapper-API ("protobuf-dynamic") möglicherweise einfacher zu verwenden als die ursprüngliche protobuf-API:

Ссылка

Zum Beispiel:

%Vor%

Dynamische Schemas können in einigen Anwendungen nützlich sein, um Änderungen zu verteilen, ohne Code neu zu kompilieren (etwa in einem dynamisch typisierten System). Sie können auch sehr nützlich für "dumme" Anwendungen sein, die kein semantisches Verständnis erfordern (sagen wir ein Datenbrowser-Tool)

    
osuciu 02.02.2016 06:39
quelle

Tags und Links