Как стать автором
Обновить

Комментарии 52

Веб в своё время расцвёл именно благодаря текстовым протоколам. Сейчас же многие "закручивают гайки" экономя на спичках, тормозя инновации под предлогом оптимизации.


Не хотиnе ли попробовать реализовать сериализатор в формат tree? Он с одной стороны читаем человеком, с другой — весьма гибок, а с третьей позволяет передавать бинарные данные без экранирования (только сплит по сепаратору). В сериализованном виде может получиться что-то типа такого:


schema Bean Bean id int64

schema InnerBean Bean
    value1 int64
    value2 int64
    value3 int64
    value4 int64
    value5 int64
    value6 InnerBean id

schema OuterBean Bean
    descr string
    bean1 InnerBean id    
    bean2 InnerBean id    
    bean3 InnerBean id    

InnerBean
    id \1
    value1 \3lmt8r
    value2 \i23dbi
    value3 \eui2f3b
    value4 \ewviubi4
    value5 \342
    value4 \2

InnerBean
    id \2
    value1 \3lmt8r
    value2 \i23dbi
    value3 \eui2f3b
    value4 \ewviubi4
    value5 \7vvvvvvvvvv
    value4 \2

InnerBean
    id \3
    value1 \3lmt8r
    value2 \i23dbi
    value3 \eui2f3b
    value4 \ewviubi4
    value5 \342
    value4 \1

OuterBean
    id \4
    descr
        \this is multiline description
        \of bean #4
    bean1 \1
    bean2 \2
    bean2 \3

Разбирается это тривиальным парсером. Избыточность нивелируется простейшим алгоритмом сжатия. Числа я тут записал в base32, но можно и сырыми данными.

В данный момент мне нужно решить проблемы с производительностью в том, что уже есть и работает. Насчет tree — честно говоря я конформист в техническом плане и предпочел бы более распространенные форматы.
При чем здесь Веб? Есть много других областей где применяется передача данных по сети.

При чём здесь передача данных по сети? Есть много других областей, где применяется сериализация объектов.


Чем проще и гибче протокол, тем проще сделать реализацию и прикрутить в нужном месте, тем больше будет реализаций и применений, тем выше будет конкуренция и распространённость, тем выше будет качество исполнения наиболее конкурентноспособных реализаций, стабильность и скорость работы.

Часто встречаю в своей работе что не нужен гибкий протокол и ему не нужна куча различных применений и распространенности и такие протоколы используем в С++ коде. А как можно говорить о стабильности у универсального протокола типа JSON? Что даёт ему +10 к стабильности или скорости работы?
Часто встречаю в своей работе что не нужен гибкий протокол и ему не нужна куча различных применений и распространенности и такие протоколы используем в С++ коде.

То, что гибкость в конкретный момент не востребована, не означает, что она не потребуется в будущем (пример из статьи — внезапно выясняется, что из 100 возможных полей заполнены лишь 10 и требуется "адаптивная схема") и уж тем более не означает, что нужно намеренно выбирать наиболее дубовый протокол имеющий лишь один вариант применения.


А как можно говорить о стабильности у универсального протокола типа JSON?

Tree — это формат представления AST. Так что он ближе к LISP и XML, чем к JSON. Впрочем, гибкость протокола не исключает использования схем. В примере выше, схема данных прилагается к самим данным.


Что даёт ему +10 к стабильности или скорости работы?

Предельная простота, поддержка потоковой обработки и отсутствие необходимости декодировать строки, например.

«дубовый протокол» — быстр, чрезвычайно прост, занимает минимум места, не принуждает копировать/декодировать строки, применяется только там где он необходим.

Было бы странно, если бы бинарный протокол не обладал этими характеристиками. Однако, вы забыли упомянуть:


  1. Требует кодогенерации.
  2. Ограниченный набор поддерживаемых языков.
  3. Ограниченная система типов.
  4. Ограниченные возможности отладки.
к стабильности

Наличие RFC(формализованный стандарт, исключающий разночтения, несовместимость => меньше проблем с фантазиями авторов библиотек) и over 9000 пользователей, которые успели пробежаться по всем потенциальнам проблемам.


скорости работы?

SIMD-парсеры. Формат настолько популярен, что для него окупилось написание сильно оптимизированных библиотек и биндингов для них.

И что, для него есть production-ready реализации для Java, ObjC/Swift и какого-нибудь серверного языка?
Не говоря уже о том, что он текстовый и наверняка не имеет кодогенерации.

Пока только D и JS/TS.

0. https://www.xolstice.org/protobuf-maven-plugin/ уже давно все есть

1. начиная с protobuf v3 одной командой в протофайле option java_multiple_files = true; отключаем генерацию большого файла, получаем набор class=>file. Править полученные протофайлы вам не запрещается, при большом желании можно расширить компилятор и он будет генерить то что вам нужно и прописывать интерфейсы какие хотите

2. как вы предлагаете изменять byte[] в котором по определенным смещениям лежат значения изменяемой длинны? (ваш же пункт 4, чтобы что-то поменять нам нужно будет хвостик сдвинуть влево-вправо и изменить размер самого массива) Если объект в системе вроде как и готов, но возможно еще будет меняться, то перекидывается ссылка на билдер, а вот если нужно отправить в wire клиенту, то тут уже и build() вызывается и получаем готовый слепок.

3. сами же и сказали решение проблемы, dto не должно думать как упаковать граф, а потом его распаковывать, или может JSON уже научился такое делать? с учетом того что proto v3 еще ближе приблизился к json, то упаковка графа в руках того кто упаковывает, а совсем не стандартная операция.

4. [PROTOBUF+ZIP] vs [JSON+GZIP] это спор на уровне: у нас есть SQL и у нас есть NoSQL, в одном схема прописана и если говно пришло то оно сразу отбросится, во втором мы что-то как-то запишем-прочитаем. И далеко не у всех текста гоняются, зачастую только ID нужных элементов. К тому же сами признали увеличенную нагрузку на CPU, что для мобильных приложений очень критично. Хотите еще быстрее, с меньшей нагрузкой на CPU и без схемы, то добро пожаловать в MessagePack, только потом не жалуйтесь, что клиенты прислали очередную кашу.

В общем у вас пожелания: я хочу бинарный формат, который работает быстро, является компактным, сохраняет и проверяет схему, сохраняет ссылочность, позволяет без копирования изменять поля прямо в byte[] и т.д.

Лично я не знаю таких форматов и вижу противоречия в требованиях: компактный vs изменения сразу упакованного массива, компактный-быстрый vs сохраняем целиком ссылки-граф.
0. Спасибо. Но я вижу что ему надо указывать путь к protoc, то сам он его не содержит, поэтому лично мне проще protoc запустить встроенными средстваи ant/maven

1. Да, я про версию 3 немного написал. К сожалению править сгенерированные файлы конечно нельзя — максимум положить рядом diff.patch и применять его после генерации.

2. Нене. Проблема излишнего копирования в текущей реализации заключается в копировании данных между Bean.Builder и Bean. Такое копирование можно исключить, так как это сделал protostuff и javanano@protobuf.v3. А что еще более сильной оптимизации, там есть варианты, поскольку формат не очень сложный. Например наши инженеры написали конвертор byte[] -> byte[], который позволяет поменять список значений полей без необходимости полной десериализации.

В целом меня все устраивает. За исключением проблемы #2, которую сейчас пофиксим пул-реквестом. Цель статьи — напомнить, что серебрянной пули не существует :)
0. да, можно указать, но он по умолчанию из окружения вытягивается, а в окружение почти все знакомые ставят apt-get/yum/port install protobuf-java

Я пока не видел людей которые считают protobuf серебрянной пулей =) чаще встречаю которые считают, что json это верх совершенства и пихают его везде, а потом ловят несогласованность форматов в рантайме. В свое время лично для меня proto решил много проблем, так как позволяет высунуть контракт протокола на сервисы и гарантировать его соблюдения.

Со строками чаще проблемы не в том что копируются туда-сюда, а в самом кодировании-декодировании в UTF8, так как в java локальное представление Unicode, вот тут CPU и проседает частенько =(
>«В среде разработчиков часто бытует мнение, что протокол сериализации protobuf и его реализация — это особая, выдающаяся технология, способная решить все реальные и потенциальные проблемы»

Ни один нормальный разработчик никогда полностью не доверяет какой-то технологии и всегда относится к ней с сомнением.
Если это не так — присмотритесь к такому разработчику: не пора ли его уволить
А лучше сразу расстрелять, ведь разработчик всегда должен сомневаться и относиться с недоверием к технологиям. Пчёлы против мёда, ага.

Формат/протокол сам по себе мало о чем говорит, все чаще решает конкретная реализация и распространенность. Все писали, что на андроиде не надо использовать встроенную джавовскую сериализацию, строго говоря не так уж и медленно она работала, однако попалась либа https://github.com/RuedigerMoeller/fast-serialization, которая для меня упростила жизнь многократно. Долгое время на флеше использовал AMF, прекрасный протокол: сжатие, упаковка, циклические ссылки, бинарный, потом на одном проекте надо было json, флешовый нативный json парсил строки мгновенно, меньше миллисекунды, надо было только потом замапить на модель, чуть мелденнее AMF, но не критично. А потом soap на мобильном устройстве и в принципе ничего страшного, немного оптимизаций :) Один раз попробовали заменить RMI отправкой данных (фактически байт массивов) руками по сокету, и никакого выигрыша не удалось достичь сходу, не все так просто. CORBA ну тот же ваш протобуф :) ничем не запомнился, кроме мучительного его изучения. Снова андроид — AIDL… А можно, между прочим, и парсеры XML повыбирать и подходы разные попробовать.

Главное преимущество protobuf что вы пишите один код для всех языков программирования. Если вам легче написать json parser на пяти языках для пяти различных бэкенд то пожалуйста. Для меня мое время важнее. Также не стоит рассматривать protobuf messages как бины — это прежде всего обертка для передаваемых данных между серверами. Ну и конечно большим плюсом protobuf — недавний релиз grpc.
Я вот не знаю ни одного языка/платформы для которой не существовал бы готовый парсер JSON, поэтому максимум, что нужно сделать — это нарисовать мэппинг между полями бина и полями JSON, и то как правило, это требуется не всегда.

Но идея, конечно, верная. Это не сущности, а чистые DTO, только вот иногда хочется эти данные быстро перегнать в другой формат и с текущей реализацией это требует дополнительных усилий. Ну или сразу использовать protostuff, который умеет все из коробки.
Простите, я другое имел в виду. Вам для каждой платформы нужно создать свой класс обертку и настроить умный маппинг, который не будет фейлиться когда в один бэкенд разработчик добавляет новое поле. А это все возможности появления новых багов, а новые баги это время разработчика + большие деньги.
Во-первых, класс-обертку не обязательно создавать. Маппинг в существующие классы натыкал и нормально. Можно ассоциативные массивы использовать. Во-вторых, из XML, JSON, CSV их можно нагенерировать спокойно. В-третьих, а с protobuf'ом будет не так разве?
1. Вот именно. А в случае protobuf вам не нужны существующие классы, они создаються с protobuf message. Можно и ассоциативные массивы, а можно и в строке все держать. Только для чего?

2. Какая технология позволяет сгенерироват класс для большинства платформ, с поддержкой сериализации с XML, JSON, CSV? (кроме клонов protobuf).

3. Смотрите п.1.
1. Если у меня есть существующие классы, зачем мне что-то ещё? Не понятно.
2. Ну JAXB там для Java. xsd.exe для C#. На ум сразу приходят.
3. С протобуфом всё аналогично.
1. Если вы написали классы для своих данных + написали мэппинг для XML или json то конечно вам уже ничего не нужно. Но если вы только начали писать проект, то вы описываете только как выглядят ваши данные и все! Protobuf сам позаботится о создании классов и их сериализации.

2. Вы так и не ответили на вопрос. У вас для каждой платформы другая технология. Protobuf — это одна технология. Вы можете переслать объект с бэкенд с++ или go до Java. И все будет работать из коробки. Вам нужно только заполнит обьект значениями.

PS У меня ощущение что вы не совсем понимаете как работает protobuf и какие задачи стояли перед его разработчиками.
1. Если у меня проект начинается с нуля. Я пишу класс, помечаю его аннотацией @XmlElement. И всё. Кроме того. со своими классами я могу делать все что захочу. Захотел — транзитивные поля добавил. Захотел — методы написал. С классами протобуфа, я так понимаю, ничего подобного не выйдет.

2. Ну если вы хотите жести жестокой, то есть такая технология WSDL. Так вот по этой самой WSDL вы можете на любой практически платформе (думаю что на гораздо большем количеств платформ, чем протобуф умеет) нагенерировать классов и интерфейсов. Вообще слабо понимаю зачем, начиная проект с нуля, начинать его на нескольких платформах генерировать классы для разных платформ. модель данных поменяли — перегенерируем классы для всех модулей на разных платформах, разрабатываемые разными командами? Единственный сценарий — это к существующему сервису цепляться. Тут WSDL в плане генерации фору даст всем. Так что совсем не аргумент.
Не могу спорить о WSDL поскольку никогда с ним не работал, но насколько успел понять это только описание сервиса (без генерации кода). На мой взгляд gRPC помощнее будет. Protobuf генерирует только классы для передачи между сервисами. Они и не должны иметь какой-то внутренние методы.

И вы в своем разговоре привязаны к xml, но это не панацея. xml данные слишком велики по сравнению с protobuf. И конечно парсинг xml тоже в несколько раз медленнее. Для компаний типа Facebook, Microsoft, Google эти оптимизации помогают сэкономить миллионы.

Каждой технологии своё применение. Не существует идеального решения. И если вам удобнее передавать xml между сервисами то передавайте. Предлагаю закрыть это обсуждение.
Нагенерит вам протобуф классов, а вы потом ещё маппинг этих классов на свои делаете?
Спасибо. Теперь знаю что не стоит тратить на него время.
Я такого не говорил :) Вообще это очень неплохая технология. Просто есть отдельные проблемы, плюс не надо ее сильно переоценивать.
Ну проблема с циклическими ссылками и, насколько я понял, дополнительная сложность в разработке (т.е. нельзя просто настаивть аннотации и сериализовать бин) нивелируют для меня все возможные преимущества.
Аннотированные POJO в формат protobuf умеет сериализовать библиотека protostuff. Со ссылочностью в protobuf, да, все непросто — как минимум придется все запихивать в отдельные словари, а ссылочность обеспечивать через идентификаторы. Или городить даже что-то более серьезное.
Должна быть очень веская причина, чтобы все это городить.
Как правильно заметил сам автор, большая часть «описанных» проблем — это проблемы именно реализации под JAVA. Конечно, в других реализациях есть свои подводные камни.
Что касается ссылок друг на друга — весьма легко обходится простым изменением структуры хранения в протобафе, вводом дополнительного контейнера, где идут объекты ссылающиеся друг на друга, ну или вводом ID на ссылающийся объект.

Нет идеальных вещей, нужно уметь вещи использовать и адаптировать под себя.
Идеальных вещей не бывает, потому что всё есть результат компромиссов. Но.

Вот все же знают что делать с циклическими ссылками. Почему такое решение не применено в самом протоколе? Чем оно его так усложнит? На какие компромиссы придется пойти?
Извините, но, мне кажется, что ваши заявления слишком преувеличены, впрочем вы и сами это подтверждаете. И почему вы пишете бин, вместо привычного сообщение?

3. На мой взгляд, так и должно выглядеть очевидное поведение по умолчанию. Непонятно, почему вы ожидаете от просто бинарной сериализации каких-то хитрых оптимизаций.

4. Сами критикуете и сами же оправдываете. Все так, но здесь и нет никаких недостатков, одни очевидные плюсы. Нужно сжатие — используйте дополнительно сжатие.

По моим ощущениям у вас сложились неадекватно завышенные ожидания от простого бинарного сериализатора, разве они где-то обещали что-то большее? Protobuf это не панацея, а просто удобный инструмент, который уменьшает значительное количество ручной работы.
3. На мой взгляд так не кажется. Я сериализую объектную модель в бинарный формат. При десериализации я ожидаю получить аналогичную объектную модель.

Извините, но мне кажется, что вы преувеличиваете мои преувеличения. Я хотел обозначить важные и не всегда очевидные моменты, которые могут повлиять на принятие решения. Я не выношу вердиктов или оценок, просто стараюсь приводить факты.

Я не понял претензию к строкам.
Предложение хранить строки в национальных кодировках а-ля CP-1251 порождает гораздо больше проблем, чем решает.

Претензий к строкам нет. Строки — это прекрасно. Прекраснее строк могут быть только Unicode-строки.

Суть изложенного в том, что при наличии в DTO большого количества строк все оптимизации примитивов, реализованные в protobuf на самом деле погоды не делают.
Вообще очень странные утверждения.
Аля: «При наличии колес у машины, присутствие магнитолы погоды не делает.»

Как вы вообще логически увязываете всю ту кашу которую описали?
Т.е. не надо оптимизаций никаких делать если в протоколе есть строки?
А т.к. в любом протоколе строки есть, то забейте на любые оптимизации?
Вы людям голову морочите фигней какой-то банальной…
Передайте своей Але, что, если она любит машины, то формулировка будет звучать так: «При наличии полного багажника бетонных плит, присутствие брызговиков Sparco на общие ТТХ машины не повлияет».

Передал, она сказала, что это все хорошо ровно в одном случае. Если мы говорим о грузовике для перевозки плит.
Но простобаф это библиотека общего назначения. У меня вот в нем 90% сообщений это идетнификаторы (Long). String летают редко.
И подозреваю, что я не один такой.
А вот вы далеко идущие выводы делаете только по одному юзкейсу. Непрофессионально это.

Проблему постоянного копирования данных решали в рамках реализации Cap’n Proto (https://capnproto.org).

НЛО прилетело и опубликовало эту надпись здесь
Cпасибо, отличная ссылка. Но это не совсем protobuf. Они реализуют байтовое представление с фиксированной шириной полей, в результате чего для изменения данных нужно знать только смещение. Данные с изменяемой длиной хранятся ссылками. Незаполненное место ликвидируется при пересылке при помощи своего легкого алгоритма компрессии. То есть можно придумать много отличных форматов, но статья про protobuf.

Ну и плюс они подтверждают:
When bandwidth really matters, you should apply general-purpose compression, like zlib or LZ4, regardless of your encoding format.
смотрели в свое время, но если брать список по топику:

те же c++, те же зависимости бинарного кодогенератора по схеме от os где запускаешь

честное признание на их же сайте: Currently it only beats Protobufs in realistic-ish end-to-end benchmarks by around 2x-5x. We can do better. То есть ни о какой разнице на порядки разговор не идет. К тому же учитывая следующий абзац сравнение было на c++ версии, что происходит в java неизвестно

для java нету оффициального сериализатора, а сторонний заброшен с февраля (Cap’n Proto’s reference implementation is in C++. Implementations in other languages are maintained by respective authors and have not been reviewed by me)

как результат: взять такой продукт в продакшен вместо протестированного protobuf лично я не решусь никогда
Потому что Cap'n Proto's — это формат хранения данных, а не передачи.

P.S. Лучше брать flatbuffers для этой цели.
Cap’n Proto is an insanely fast data interchange format and capability-based RPC system. Think JSON, except binary. Or think Protocol Buffers, except faster.

так что разработчики не согласны с вами, что это «формат хранения данных, а не передачи».

p.s. лучше брать то, что решает конкретную задачу
Решение для кроссплатформенной сборки можно подсмотреть тут: https://github.com/grpc/grpc-java (после строки «For protobuf-based codegen integrated with the Maven build system, you can use protobuf-maven-plugin»)

compile у protobuf-maven-plugin'а запускает protocArtifact, а compile-custom — pluginArtifact

ЗЫ: У меня os-maven-plugin работал только на maven 3.3 и выше.
Столкнулся с протобуфером пытаясь из PHP сним поработать, но нормального плагина не нашел и затею похоронил.
Вобще идея мне понравилась и если не пытаться приспособить протобуфер под свой проект, а построить проект под протобуфер то можно реально сэкноноить на хранении и ускорить обмен данными
Есть довольно интересные еще варианты flatbuffers и avro. Для каких то сценариев использования они могут подходить лучше.
Есть еще Thrift и отличная библиотека swift для него, которая генерирует IDL на основе объектной модели.
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории