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

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

Очень часто мы не вправе выбирать схему сериализации при работе со сторонними сервисами. Многие сервисы предоставляют схему с использованием JSON или XML, и если вам приходилось сталкиваться с некоторыми финансовыми решениями, вы могли встретить FIX. В этих случаях мы должны соблюдать схемы, которые нам даны. Однако когда дело доходит до решений, которые мы выбираем для разработки и поддержки внутри наших организаций, у нас есть большая степень свободы в выборе способа обмена данными между нашими программами и службами. Это как раз то время, когда мы должны мыслить немного шире, чем обычные решения, с которыми мы столкнулись при работе с внешними API.

Проблемы, связанные с игнорированием сериализации

Предположим, вы - бэкэнд-инженер, работающий над каким-то программным проектом, написанным на C #, в среде микросервисов, где вам нужно где-то предоставить API некоторым клиентам. Вы решили реализовать свой бэкэнд как веб-службу, которая получает и отправляет JSON, поскольку, похоже, это то, чем все остальные делают в наши дни. Аргумент, который вы также находите убедительным, заключается в том, что у вас есть почти полная свобода моделирования своих доменных объектов, поскольку и Newtonsoft.JSON, и встроенный System.Text.Json очень хороши в сериализации большинства структур данных и объектов, которые мы им бросаем. . Это позволит вам легко добавлять и удалять новые поля в процессе разработки, не нарушая при этом слишком большого количества ошибок в коде сериализации. Если производительность станет проблемой, вы всегда можете переключиться на более эффективную схему позже.

Так в чем проблема? Что ж, вы сделали несколько неявных дизайнерских решений, которые также имеют некоторые недостатки. Если не тратить время на указание схемы, все вызывающие вашу службу на этапе разработки будут получать данные, которые быстро меняются в формате без какого-либо уведомления, что затрудняет их процесс разработки. Кроме того, у вас могут возникнуть проблемы, если вы решите использовать такие типы данных, как decimal в C #. Как это будет переведено в JSON, если этот формат не имеет реальной поддержки повышенной точности, которую обеспечивает decimal? Скорее всего, они будут преобразованы в двойные с эквивалентной потерей точности.

Есть способы обойти подобные проблемы, например, убедиться, что строковое представление decimal передается по сети, а не полагаться на преобразование по умолчанию. Но внезапно вы должны активно делать такой выбор, и сериализация перестала быть такой тривиальной, как раньше. При дальнейшем решении проблемы с производительностью это легко сделать, только если она никогда не возникает или у вашего решения нет пользователей. Если вам нужно изменить схему сериализации, у вас может быть много разных пользователей вашего API, и не все из них будут рады обновиться до вашей новой схемы двоичной сериализации, поэтому вам, возможно, придется прибегнуть к запуску и поддержке двух версий ваш API на время. Разумно помнить, что в вашей системе никогда не было меньше и более счастливых пользователей, чем когда вы планировали ее дизайн, поэтому затраты на изменение сериализации практически бесплатны.

Некоторые вопросы по выбору сериализации

К счастью, есть несколько вопросов, которые мы можем задать себе, чтобы найти разумное решение. Вот некоторые из них:

  • Кто участники общения?
  • Каковы их ограничения?
  • Схема или без схемы?
  • Текстовый или двоичный?
  • Вам нужна версия?

Кто участники общения?

В большинстве программных систем у вас будет два типа участников коммуникации: люди и машины. Несмотря на то, что в наши дни вы редко можете представить данные напрямую людям, поскольку программы командной строки становятся все менее и менее распространенными. Пользователи могут по-прежнему предъявлять требования, например, им нужны данные, которые вы предоставляете, которые легко обрабатывать в Excel, браузере или в другом месте. Это, очевидно, накладывает некоторые ограничения на то, как вы можете форматировать данные, поскольку некоторые сверхкомпактные двоичные форматы могут оказаться трудными для импорта в Excel, тогда как текстовый формат, такой как CSV, может оказаться намного проще. Когда связь осуществляется между двумя программными системами, вам нужно задать вопрос, действительно ли необходимо читать сообщения между ними в текстовом формате, или будет достаточно более компактного двоичного формата. Очень часто бывает так.

Каковы ограничения участников?

Очень часто вам придется учитывать требования вызывающих абонентов вашей системы. Если вас в первую очередь вызывает интерфейс React, выбор высокопроизводительной схемы двоичной сериализации, такой как SBE, может быть не оптимальным, поскольку для поддержки этого формата из javascript доступно мало инструментов и библиотек. Если вызывающими являются другие системы с большим количеством библиотек, и вы можете ожидать много вызовов или отправки больших объемов данных по сети, вы можете получить выгоду от выбора схемы двоичной сериализации, такой как Protocol Buffers, Cap'n. Proto, или SBE, с улучшенными характеристиками, вытекающими из этих схем. В общем, разница в производительности между текстовыми и двоичными форматами может быть на несколько порядков, поэтому в зависимости от количества запросов, с которыми сталкивается ваша служба, лучше определять это на ранней стадии процесса разработки.

Схема или без схемы?

Выбор того, использовать схему или нет, вызвал немало споров. Если вы решите не торопиться и указать схему, вы упростите жизнь любому разработчику, вызывающему ваше приложение. Скорее всего, они даже смогут создавать модели данных с помощью инструментов. Если вы этого не сделаете, ваша жизнь станет проще, поскольку вам не придется поддерживать схему при изменении моделей данных, однако вызывающие абоненты могут быть удивлены, когда вы что-то измените, и их код волшебным образом сломается. Для определенных форматов данных, таких как XML и JSON, вы можете выбрать, хотите ли вы определять схему или нет, используя схему XML и схему JSON соответственно. Но для других форматов, таких как SBE, схемы обязательны. Я обычно предпочитаю создавать схемы, так как они помогают мне думать о том, какими будут мои модели данных, не слишком углубляясь в код. Но для небольших служебных приложений схемы могут оказаться излишними.

Текстовый или двоичный?

Выбор текстового формата для сериализации позволяет разработчикам просматривать данные, передаваемые по сети, с помощью базового текстового редактора, что часто связано с расходами в виде сообщений большего размера и более медленной десериализации, чем в большинстве двоичных форматов. Двоичные форматы часто приводят к противоположным компромиссам. Даже если некоторые люди будут утверждать, что текстовые форматы, такие как JSON, не читаются человеком, если вы не запомнили всю таблицу ASCII. Хотя технически это правда, это не меняет того факта, что существует множество инструментов, которые без особых проблем будут печатать как XML, так и JSON, в то время как вам может потребоваться немного времени на разработку собственного инструментария для отображения содержание сообщений, передаваемых по сети в схеме двоичной сериализации. Итак, какой из них выбрать? Опять же, это зависит от участников общения. Очевидно, можно утверждать, что, поскольку сообщениями в основном обмениваются машины, не имеет смысла делать их удобочитаемыми, так зачем вообще рассматривать текстовые форматы? Что ж, чтобы заставить другие машины взаимодействовать с машиной, на которой выполняется ваш код, вам нужны разработчики, чтобы сделать приложения именно для этого. Эти разработчики - люди, и им, возможно, будет легче читать некоторые JSON, чем полагаться на инструменты, которые вы предоставляете, или использовать собственные. Кроме того, как и в случае с редакторами, есть вероятность, что то же правило применимо и к языковым библиотекам. Чем более эзотерический или нишевый язык используют ваши клиенты, тем выше вероятность того, что они по-прежнему будут поддерживать JSON или XML через некоторые пакеты, а не некоторые из более надуманных двоичных форматов. Как правило, я обычно использую двоичную сериализацию со схемами, когда раскрываю их внутри организации, особенно если я знаю, что большая часть кодовой базы написана на .NET, C ++ или Java. Если возникнет необходимость раскрыть текстовый формат, я бы добавил шлюз перевода, таким образом, дополнительная сложность и стоимость перевода из двоичного в текстовый формат скрыты в одном месте.

Версионирование или нет?

Вам когда-нибудь нужно было развернуть функции по линии, которые потребовали бы добавления новых полей к объектам, проходящим по сети? Вот тут-то и пригодится управление версиями. Если у вас есть схемы, которые определяют версию объектов данных, передаваемых по сети, каждый клиент будет знать, какие сообщения он может обрабатывать, а какие нет. Во многих форматах добавление новых полей не является проблемой, большинство фреймворков JSON с радостью игнорируют поля, которые вы не указали в своих DTO, а некоторые даже принимают значения по умолчанию для полей в ваших DTO, которые десериализатор не может найти соответствующее значение для. Но добавление и удаление полей из ваших DTO - опасный путь, особенно если люди, звонящие в вашу службу, не участвуют в изменениях. При этом, если вы работаете в очень гибкой среде и запускаете веб-службу ASP.NET с автоматически созданной документацией Swagger, это может быть нормально. Другие форматы сериализации, такие как SBE, предлагают поддержку управления версиями, где вы можете добавить определенные поля, указать, из какой версии они применимы, и значение по умолчанию для сообщений из более ранних версий. Вопрос о том, нужно ли вам рассматривать управление версиями в формате сериализации, сильно зависит от вашей ситуации, но это определенно стоит рассмотреть на раннем этапе процесса проектирования.

Заключение

Сериализация - нетривиальная проблема, которая, если ее вовремя не решить, может привести к другим проблемам на этапе разработки. Я надеюсь, что эта статья помогла вам изменить ваш взгляд на сериализацию, от простого выбора разумного значения по умолчанию до активного выбора в архитектуре вашего приложения, независимо от того, решите ли вы придерживаться того, что вы знаете, или попробовать что-то новое, теперь вы должны быть вооружены некоторыми знаниями о компромиссах, которые может принести ваш выбор. Желаем удачи в ваших будущих начинаниях с дизайном и сериализацией API.