Protobuf it!
Александр Мокров
Serialization
/
Deserialization
Python
Go
C++
C#
JavaScript
W3C
Annotea
CC/PP
Compound Document
Formats
CSS
DOM
HTML
HTTP
InkML
MathML
OWL
PICS
PNG
P3P
RDF
SMIL
SPARQL
Style
SVG
TAG
Timed Text
URI/URL
Voice Browser
WAI
WAI-ARIA
WebCGM
Web Services
XML
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
XML
XPath XSLT XQuery
XInclude
XSDXLink
XML Schema
DTD
XKMS
XML
Encryption
XPointer
XHTML
XForms
Google
XML but smaller,
faster and simpler
Protocol Buffers
C++
Java
Python
Objective-C
C#
JavaNano
JavaScript
Ruby
Go
PHP
Dart
Twitter
Protobuf vs XML
are simpler
are 3 to 10 times smaller
are 20 to 100 times faster
are less ambiguous
generate data access classes that are easier to
use programmatically
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
}
protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/person.proto
↓
person_pb2.py
>>> from person_pb2 import Person
Compiling
Person = _reflection.GeneratedProtocolMessageType('Person',
(_message.Message,), dict(
PhoneNumber =
_reflection.GeneratedProtocolMessageType('PhoneNumber',
(_message.Message,), dict(
DESCRIPTOR = _PERSON_PHONENUMBER,
__module__ = 'person_pb2'
# @@protoc_insertion_point(class_scope:Person.PhoneNumber)
)),
DESCRIPTOR = _PERSON,
__module__ = 'person_pb2'
# @@protoc_insertion_point(class_scope:Person)
))
_PERSON = _descriptor.Descriptor(
name='Person',
full_name='Person',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='Person.name', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, options=None),
from person_pb2 import Person
person = Person()
person.id = 1
person.name = "Vasya Pupkin"
person.email = "vasya@example.com"
phone = person.phones.add()
phone.number = "+7 499 270-27-27"
phone.type = Person.HOME
>>> person
name: "Vasya Pupkin"
id: 1234
email: "vasya@example.com"
phones {
number: "+7 499 270-27-27"
type: HOME
}
phones {
number: "2702727"
type: WORK
}
>>> data = person.SerializeToString()
>>> data
b'nx0cVasya
Pupkinx10xd2tx1ax11vasya@example.com"x14nx10
+7 499 270-27-27x10x01"x0bnx072702727x10x02'
>>> person = Person.FromString(data)
>>> person = Person()
>>> person.ParseFromString(data)
Any
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
OneOf
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
Map
map<string, uint32> projects = 5;
>>> person.projects['one'] = 1
>>> person
projects {
key: "one"
value: 1
}
>>> person.projects['one']
1
Library
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
message MyMessage {
google.protobuf.Timestamp my_field = 1;
}
service Foo {
rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
}
JSON Mapping
>>> from google.protobuf.json_format import MessageToJson
>>> json_string = MessageToJson(person)
>>> json_string
'{n "name": "Vasya Pupkin",n "email":
"vasya@example.com",n "phones": [n {n "number":
"+7 499 270-27-27",n "type": "HOME"n },n {n
"number": "2702727",n "type": "WORK"n }n ],n "id":
1234n}'
>>> from google.protobuf.json_format import Parse
>>> person = Person()
>>> Parse(json_string, person)
>>> person
name: "Vasya Pupkin"
id: 1234
email: "vasya@example.com"
...
Unknown Fields
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
RPC
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
gRPC
● Быстрый
● Компактный
● Позволяет формализовать данные
● Бинарный
● Лаконичный
● Поддерживается всеми распространенными
языками
● Позволяет обеспечить совместимость со
Ссылки
https://github.com/google/protobuf
https://developers.google.com/protocol-buffers
http://www.computerworld.com/article/2513433/app-development/twitter-solves-
its-data-formatting-challenge.html
Спасибо за внимание!
Вопросы?

Protobuf it!

Editor's Notes

  • #3 Вопрос сериализации и десериализации возникает практически в любом проекте. Зачастую можно обойтись и самописными инструментами. Но особо остро он встает при разработке систем состоящих из множества компонентов, написанных еще и на разных языках и разнесенных физически.
  • #4 Здесь можно увидеть такой пример. Отдельные части системы как-то обмениваются данными, и эти данные должны быть хорошо понимаемы всеми. Также сериализация важна для хранения данных.
  • #5 Решением проблем сериализации в ключе межъязыкового, межплатформенного и кроссплатформенного общения занимался и W3C. Консорциум всемирной паутины - этот консорциум был создан в 1994 году. Как консультативный орган для лидеров мировой компьютерной индустрии с целью обеспечения совместимости своих продуктов и разработки новых технологический стандартов. Стандартизация HTML - их первый крупный успех (в 1996 году).
  • #6 Здесь можно видеть список стандартов, утвержденных консорциумом. Здесь очень много важных стандартов.
  • #7 И конечно же всеми любимый XML. Здесь даже присутствует как минимум один человек, который его любит настолько, что даже ездил на XML конференцию!
  • #8 Вот пример этого удивительного языка, а вернее пример XSD (XML Schema Definition) - языка описания структуры XML-документов. Правда он прекрасен?
  • #9 Вообще экосистема XML внушительна. Это целый огромный мир. Он решал множество вопросов, в том числе и проблему сериализации и десериализации данных. Кто из вас знаком с XML и приходилось использовать или активно использует сейчас? Кому он нравится? А кому нет?
  • #10 Но видимо не все были от него в восторге. В их числе ребята из компании ныне уже несуществующей… под прежним названием. И что же они сделали? Независимый от языка и платформы, масштабируемый язык для сериализации структурированных данных, как, и, собственно,
  • #11 XML, только как они сами заявляют, он компактнее, быстрее и проще. Еще они его описывают как гибкий, эффективны, автоматизированный механизм сериализации.
  • #12 И имя ему - Protocol Buffers, или просто Protobuf. По замыслу разработчиков вы описываете свою структуру данных, компилируете в классы нужного вам языка и используете высокоуровневое представление. При этом закладывается, что новые версии ваших структур не будут ломать код, работающий со старыми версиями.
  • #13 Protobuf поддерживает множество языков программирования. Для каждого можно найти инструкцию по установке и использованию под различные платформы. Здесь языки, которые они заявляют у себя на гите, там же есть отдельная страничка с огромным количеством третьесторонних реализаций под эти и другие различные языки. Он был создан в 2008 году.
  • #14 И уже в 2010 году компания Twitter перешла на protobuf для хранения данных, отбросив XML, JSON и CSV. Под XML триллион петабайт твитов у них превращался в 10 триллионов петабайт. JSON тоже не был достаточно компактным, поскольку хранит заголовки. CSV отбросили из-за проблем обратной совместимости со старыми версиями данных, после изменения структуры.
  • #15 По заявлениям самих же разработчиков google protobuf от 3 до 10 раз меньше XML и от 20 до 100 раз быстрее.
  • #16 Как это работает? Вы описываете структуру в .proto-файле, пример которого вы можете видеть на экране. Это пример описания некой личности (взятый с сайта разработчика). Здесь message - некая базовая логическая сущность, содержащая пары ключ-значение. Т.е. мы описываем базовые параметры, указывая обязательное оно, опциональное или может быть несколько. Указываем тип и имя поля + последовательность. Это важно для сериализации в бинарный формат и последующей десериализации. Можно создавать свои внутренние типы через message, как тут показано на примере с PhoneNumber. Можно создавать перечисления для задания множества возможных значений у атрибутов. Указывать возможные значения по умолчанию. Но это вторая версия protobuf, сейчас актуальна третья и она стала еще лаконичнее.
  • #17 Теперь все поля опциональны. Значения по умолчанию строго регламентированы и для перечислений это значение с нулевым идентификатором.
  • #18 Для компиляции используется специальная программа protoc. Выполняем её в командной строке с указанием путей и получаем python модуль со статическим дескриптором для каждого типа сообщения в .proto файле, каждый из которых потом в рантайме через метаклассы генерится в python классы. Вообще если в .proto файле будет только та информация , то приведена на предыдущем слайде, то этого уже достаточно,чтобы скомпилировать рабочий код. Но будет выведено предупреждение, что не указана версия протокола в самом proto файле, и по умолчанию будет использоваться proto2. Поэтому в начале файла указываем syntax=”proto3”;
  • #19 Здесь можно видеть часть того, что в итоге генерится. С помощью метакласса GeneratedProtocolMessageType на основе дескрипторов создаются нужные классы.
  • #20 Собственно сам дескриптор, не полностью. Здесь name и full_name, но если задать namespace .proto файле, что, собственно делать нужно, то full_name дополнится на этот namespace. Там много еще чего есть, файл на почти 170 строк. Но не будем вдаваться в детали, я привел лишь для общего представления, c чем придется иметь дело.
  • #21 Ну и поехали. Импортируем класс, создаем экземпляр и заполняем. Множественные элементы добавляем через add. Значения заданные в перечисления попадают непосредственно в атрибуты класса, в котором задано перечисление. Если хотим задавать через Person.PhoneType.HOME, то придется обернуть перечисление в одноименное сообщение.
  • #22 Можно посмотреть что получилось - очень читаемый вывод.
  • #23 Механизм сериализации/десериализации достаточно прост и быстр.
  • #24 Что еще позволяет делать protobuf, помимо того. что есть в примере. Есть возможность указать для поля, что оно может быть произвольного типа
  • #25 Ограничение на использование только одного поля из нескольких возможных. Можно использовать поля разных типов, но не множественные
  • #27 Есть так же набор составных типов в стандартной библиотеки. Импорты
  • #30 Новая версия данных. Расширение. Совместимость у старых версий. Игнорирование старых полей