УДК 621.396.4; 519.876.5
О современных форматах обмена данными между компонентами распределенной
вычислительной системы
Шаптала В.С., Солнцев Д.В.
Аннотация. В статье ставится задача обосновать выбор способа обмена данными в распределенной вычислительной системе. Целью работы является исследование ключевых технологий, которые реализуют основные способы обмена данными и находятся в открытом доступе. При моделировании используются методы кроссплатформенного программирования. Новизна обзора состоит в обобщении особенностей использования различных форматов обмена данными, которые используются при построении распределенных вычислительных систем. К результатам работы следует отнести сопоставление двух подходов взаимодействия между распределенными компонентами: создание собственных проприетарных решений и использование открытых реализаций форматов обмена данными, а также разработку тестового приложения с использованием кроссплатформенного фреймворка Qt для анализа основных способов обмена данными. Результаты работы приложения приведены на целевых операционных системах: Windows (Windows 10) и Linux (Astra Linux 8.2 и Alt Linux 1.6). Практическая значимость работы заключается в оценке характеристик, позволяющих осуществить выбор того или иного формата обмена данными: быстродействие, размер используемой памяти и особенности интеграции. Полученные оценки позволяют выбрать технологию обмена данными в распределенной вычислительной среде в зависимости от требований, предъявляемых прикладной областью. Доступность реализаций основных форматов обмена данными в виде библиотек, распространяемых по свободным лицензиям и/или исходного кода, находящегося в открытом доступе, позволяют применять соответствующие технологии в распределенных системах, компоненты которых могут находиться под управлением распространенных операционных систем и аппаратных платформ.
Ключевые слова: распределенные вычислительные системы, XML, JSON, PROTOBUF, CBOR.
Введение
Многие современные телекоммуникационные системы строятся на основе распределенных вычислительных систем. Часто это связано с тем, что составные части системы делаются различными производителями или различными подразделениями в составе одной организации. Например, протокол передачи данных, модулятор и возбудитель можно реализовать в одной программе, а можно в трех разных, которые могут выполняться на одном или разных вычислительных комплексах и, возможно, под управлением различных операционных систем. Распределенный подход упрощает разработку и поддержку системы, но требует использования эффективных форматов обмена данными между компонентами.
В статье будут рассматриваться вычислительные системы, компоненты которых взаимодействуют между собой только через сеть Ethernet.
Проприетарные стеки протоколов
В этом случае создается стек протоколов, где описывается всё информационно-логическое взаимодействие. В наихудшем случае это приводит к полному отказу от устоявшихся протоколов сетевого и транспортного уровней (например, TCP/IP) и к созданию одного протокола с поддержкой функций прикладного уровня. Этот путь очень дорогой как с точки зрения программирования, так и с точки зрения разработки и тестирования. При подобном подходе очень сильно усложняется построение аппаратуры и возникает необходимость создания анализаторов проприетарных протоколов, что в конечном итоге приводит к существенному увеличению стоимости и длительности разработки. В случае, когда время необходимое для разработки, тестирования и документирования стека протоколов превышает время существования команды разработчиков, задействованной в этой работе, то результат работы может стать непредсказуемым. Дополнительно к вышеперечисленным недостаткам следует обратить внимание на две проблемы:
очень часто при работе в таких условиях приходится прибегать к методам реверс-инжиниринга;
составители протокола часто не предусматривают возможность введения дополнительных полей в произвольном месте структуры данных без необходимости обновления всего программного обеспечения в распределенной вычислительной среде.
Единственным достоинством этого подхода является закрытие рынка, поскольку для работы в этих условиях сторонним компаниям нужен доступ к логически завершенной конструкторской документации, получение которой может быть затруднительным, а иногда и невозможным.
В противовес этому подходу можно противопоставить использование многократно апробированных открытых и платформенно независимых протоколов обмена данными прикладного уровня, использование которых может существенно упростить построение и сопровождение программного обеспечения. Важным условием для выбора анализируемых технологий является то, что структуры данных в разных компонентах могут меняться, и это не должно приводить к непредсказуемому поведению распределенной вычислительной среды.
Форматы представления данных в таких протоколах могут быть текстовыми и бинарными. Данная статья посвящена анализу наиболее актуальных и широко используемых представителей данных типов форматов.
Extensible markup language
XML - язык разметки, определяющий правила представления данных в текстовом виде. В ходе разработки языка к нему предъявлялись следующие требования: читаемость человеком и машиной, простота использования, расширяемость и способность быть использованным в широком спектре задач по организации обмена информацией в сети Internet.
XML появился в 1998 г. как спецификация W3C [1] и сразу приобрел популярность: его XML появился в 1998 г. как спецификация W3C [1] и сразу приобрел популярность: его стали широко использовать при написании API для WEB приложений, а также для работы приложений с БД. Впоследствии, консорциумом W3C было создано несколько рабочих групп по развитию языка: было выпущено несколько сопутствующих стандартов (напр. RFC 7303 [2]), официальная версия спецификации XML 1.0 от 26.11.2008 г.
Библиотеки для работы с данными в формате XML существуют для всех распространенных языков программирования. Они включают в себя классы и методы для работы с XML документами: формирование, разбор и преобразование в иные типы данных.
На основе и с использованием XML стали появляться и другие технологии: SOAP, XML-RPC и пр., однако с 2009 г. все рабочие группы W3C, связанные с данной технологией, были ликвидированы и новых разработок по развитию и поддержке языка не ведется. В настоящий момент XML в значительной степени утратил свои позиции, уступив более легковесному формату JSON.
Исходное сообщение в формате XML представляет из себя разметку, схожую с HTML, где пользовательские данные заключены между открывающим и закрывающими тэгами:
<tag> value </tag>.
После сериализации сообщение представлено в виде байтового массива с исходным сообщением, каждый символ которого закодирован в соответствии со стандартом Unicode.
Java script object notation
JSON - популярный текстовый формат обмена данными, появившийся, примерно, в 2002 году [3]. Пришел из мира WEB, где в том время стояла задача разработки простого, более легковесного, чем XML-формата в условиях общения между распределенными клиентом и сервером. В WEB сфере сервером является http сервер, который реализует API
(как правило, REST) для доступа клиентов - либо получение html-странички, либо для получения данных от удаленного сервиса поверх http протокола. Так как протокол http является stateless протоколом (сервер отвечает на текущий запрос клиента и не знает о предыдущей истории общения), то формат JSON оказался удачным решением поставленной задачи. С тех пор, JSON получил признание и распространение не только в WEB сфере, но и в качестве внутреннего формата обмена данными между компонентами распределенных систем более широкого спектра, в том числе при организации обмена данными между приложениями и базами данных.
В настоящий момент существует два документа, регламентирующих обмен сообщениями в JSON-формате: RFC 8259 [4] и ECMA-404 [5]. Реализации стандарта, в силу большой популярности формата, существуют для большинства языков программирования и включают в себя классы и методы для работы с JSON: формирование сообщения, разбор и преобразование в иные типы данных.
Формат представляет закодированное сообщение в читаемом для человека виде, состоящее из пар «ключ-значение». Ключом, при этом, является текстовая строка, а значением может быть строка, число, переменная типа bool, и некоторые другие.
Сериализованное сообщение представлено в виде массива байт, где каждый символ исходного сообщения кодируется соответствующим ему символом стандарта UTF-8. В зависимости от реализации, могут быть предоставлены дополнительные возможности по сериализации JSON сообщения. Например, во фреймворке Qt имеется возможность сформировать байтовый массив в бинарном виде. Такое представление фреймворк использует для своих внутренних нужд.
Google protocol buffers
Google Protocol Buffers (Protobuf) - технология/протокол сериализации данных в бинарном формате, разрабатывается и поддерживается корпорацией Google [6]. Продукт с открытым кодом распространяется по лицензии BSD, появившись в 2008 г., успел зарекомендовать себя как простое, удобное решение для описания формата передаваемых данных, обладающее высокой производительностью при сериализации/десериализации сообщений. В 2010 году компания Twitter объявила о переходе на protobuf для хранения своих коротких сообщений (т.н. «твитов»).
Структура сообщений описывается в отдельных файлах с расширением «proto». Синтаксис proto файлов C-подобный включает описание всех полей сообщения с указанием их типов, названий, значений по умолчанию (опционально). Формируемые proto файлы платформенно-независимые, легко читаемые человеком и редактируемые при необходимости введением/удалением полей сообщения. В процессе развития технологии синтаксис описания, proto файлы разделились на 2 независимых версии: proto2 и proto3. Основными отличиями proto3 от proto2 являются: отсутствие квалификаторов required/optional перед типом данных и отсутствие возможности задавать значение полей по умолчанию (определены заранее самим protobuf).
Сформированные proto файлы компилируются отдельным компилятором protoc. На выходе компилятор protoc генерирует файлы, которые подключаются к проекту и используются для заполнения полей данными, сериализации и десериализации сообщения. Кроме того, сгенерированные файлы предоставляют некоторые служебные методы для работы с сообщением.
Присутствуют имплементации под разные языки программирования: C++, C#, Java, Go, Python. Для языка C существует отдельная версия protobuf-c со своим компилятором «proto» файлов protoc-c и рантаймом, написанных на языке C. Особенности распространения google protobuf и подключение необходимых файлов к проекту будут описаны далее в статье.
Concise binary object representation
CBOR - бинарный формат представления данных [7], несмотря на свою относительную молодость (стандарт, описывающий данный формат, появился в 2013 году), уже успел завоевать определенную популярность и имеет множество имплементаций на разных языках программирования: C/C++, Java, Python, JavaScript и другие. Библиотеки по работе с CBOR включают в себя классы/методы для формирования, разбора (парсинга) принятого сообщения.
К CBOR проявляют интерес и некоторые крупные IT компании. В частности, одна из реализаций стандарта, под названием tinyCBOR, разрабатывается и поддерживается корпорацией Intel.
Как и все бинарные форматы, сериализованное сообщение в формате CBOR представляет из себя закодированный, в соответствии со стандартом RFC 7049 [8] массив байт в нечитаемом для человека виде, но содержащий в себе информацию о типах добавленных данных в качестве дополнительной информации, чем и обусловлены накладные расходы на формирование такого сообщения. Тем не менее, данные накладные расходы оказываются почти всегда ниже, чем накладные расходы при передаче информации в текстовом виде, что будет продемонстрировано далее в тексте статьи.
Особенностью формата является его ориентация на схожую с JSON логику формирования сообщения в виде массива пар «ключ-значение». Ключ, при этом, может задаваться как строкой (тип Text string), так и в виде тега, который пользователь (прикладной разработчик) может определить сам.
Описание тестового окружения
Тестирование происходит на виртуальных операционных системах, запущенных с помощью кроссплатформенной системы управления виртуализацией oVirt версии 4.3.2.1. Параметры гостевых операционных систем сведены в табл. 1. Состав серверного оборудования: IntelE5-2650; 20 MB; 2,0 GHz (2,8 GHz) / 12*8 GB; DDR3 1066 MHz. Дисковая подсистема серверного оборудования не имеет большого значения, поскольку для рассматриваемых объемов данных она практически не используется.
Таблица 1 - Целевые операционные системы
Guest OS, x64 Windows 10 Alt Linux 8.2 Astra Linux 1.6
Compiler, x64 MinGW 7.3 GCC5 GCC5
CPU E312xx; 2,0 GHz E312xx; 2,0 GHz E312xx; 2,0 GHz
RAM, GB 2 2 2
Тестовое приложение написано на языке программирования С++ с использованием кроссплатформенного фреймворка Qt версии 5.12.1 в интегрированной среде разработки QtCreator версии 4.8.1. Все результаты получены для сборки приложений в режиме «Release». Далее рассмотрим возможности, предоставляемые фреймворком Qt и сторонними библиотеками, которые были использованы в тестовом приложении для работы со сравниваемыми в данной статье форматами представления данных.
В Qt реализовано два подхода для работы с XML документами. В первом, существует модуль xml, в котором XML документ представлен в виде дерева DOM. В данный модуль входят классы, обеспечивающие работу с DOM - добавление, удаление, модификация поддеревьев/листьев (теги и данные). Во втором подходе, используются классы QXmlStreamWriter и QXmlStreamReader, входящие в модуль Qt core. Данные классы позволяют реализовать простую последовательную запись и разбор элементов (тегов и их значений), составляющих XML документ. Этот подход и был использован в тестовом приложении.
Для работы с форматом JSON в Qt существуют целый набор вспомогательных классов, основными из которых являются: QJsonDocument, QJsonObject, QJsonValue,
QJsonArray. У класса QJsonDocument имеются методы, используемые для сериализации/десериализации сообщений. В тестовом приложении поля структуры заранее известны, поэтому значения полей извлекаются непосредственно из сериализованного сообщения посредством задания нужного ключа. Например: QJsonObject obj = doc.object(); msg->id = obj["MSG_ID"]. toVariant().toUInt().
Существуют и иные способы построения парсеров, включая последовательный обход байтового массива сериализованного сообщения.
Google Protobuf является внешней библиотекой, поэтому ее необходимо либо скомпилировать из исходных кодов (распространяются бесплатно по лицензии BSD), либо использовать уже скомпилированную версию.
Для тестового приложения, запускаемом под ОС Windows скомпилированная библиотека версии 3.7.1 была скачана через утилиту MSYS2, для ОС Linux были скачаны исходные коды библиотеки той же версии 3.7.1, затем скомпилированы стандартными для языка программирования C++ средствами сборки приложений: configure, make, make install.
Кроме самой библиотеки необходимо сгенерировать компилятором protoc (входит в комплект поставки google protobuf) подключаемые файлы со структурой сообщения. Сериализация/десериализация происходит путем вызова функций библиотеки protobuf.
Начиная с версии 5.12.1. в Qt входит библиотека для работы с CBOR под названием tynyCBOR. Для облегчения работы с библиотекой разработчиками Qt были написаны оберточные классы, скрывающие низкоуровневые интерфейсы tinyCBOR. Этими классами являются классы QCborStreamReader и QCborStreamWriter. Подключаются с модулем core. Класс QCborStreamReader был использован в тестовом приложении для формирования сообщения в формате CBOR, а при помощи класса QCborStreamReader был написан парсер для восстановления исходного сообщения из сериализованного в формате CBOR. Парсинг происходит в виде последовательного прохода по массиву байт сериализованного сообщения в целях поиска нужных идентификаторов, обозначающих поля структуры (ими могут выступать строки или пользовательские теги).
Сравнение форматов обмена данными Сравнение рассматриваемых протоколов будет проводиться на структуре данных c длиной информационного блока 16, 512 или 2048 значений:
struct Msg {
quint32 id = 11;
quint32 len;
quint32 time;
quint32 service = 500;
float data[16, 512 или 2048];
quint32 crc = 12345678;
};
Для тестирования работы приложения с использованием технологии google protobuf необходимо предварительно описать структуру сообщения в файле «proto». Для корректного сравнения форматов между собой структура сообщения в «proto» файле должна повторять поля структуры Msg: syntax = "proto3"; message ProtoMsg { uint32 id = 1; uint32 len = 2; uint32 time = 3; uint32 service = 4; repeated float data = 5;
uint32 crc = 6;
}
Часть полей структур данных Msg и ProtoMsg заполняется статически, а часть динамически:
поле time заполняется текущим временем на момент тестирования;
поле len равно размеру структуры с учетом длины информационного блока;
поля data заполняются как случайные данные в формате с плавающей точкой в диапазоне от 0.0 до 1000.0.
Начальное заполнение полей очень важно для корректного сравнения полученных результатов, поскольку в различных форматах обмена данными в сериализованном потоке для представления информации может потребоваться различное количество байт. Важно обратить внимание, что для хранения одного элемента информационного блока всегда требуется 32 бита. Размеры входных и выходных данных рассматриваемых форматов представлены в табл. 2.
В связи с тем, что анализируемые форматы обмена данными имеют текстовое и бинарное представление сравнение проводилось с использованием компрессии данных. Для всех сравниваемых форматов получаемый после сериализации исходного сообщения массив байт был сжат при помощи функции qCompress() с уровнем сжатия, заданным по умолчанию (ZLIB DEFAULT COMPRESSION), и восстановлен вызовом функции qUncompress(). Данные функции являются обертками над функциями библиотеки zlib 1.2.11, входящей в состав Qt 5.12.1.
Таблица 2 - Эффективность представления данных
Формат Длина Исходный Размер сообщения после Накладные расходы по отношению к
блока размер, байт сериализации, байт исходному сообщению, %
данных Компрессия Компрессия
XML 16 84 294 200 250,00 138,10
512 2068 4760 1776 130,17 -14,12
2048 8212 18584 6494 126,30 -20,92
JSON 16 84 242 157 188,10 86,90
512 2068 4708 1721 127,66 -16,78
2048 8212 18532 6433 125,67 -21,66
Google 16 84 84 97 0,00 15,48
protobuf 512 2068 2070 1538 0,10 -25,63
2048 8212 8214 5757 0,02 -29,90
CBOR 16 84 118 115 40,48 36,90
512 2068 2599 1641 25,68 -20,65
2048 8212 10279 6065 25,17 -26,14
Скорость работы для выбранных технологий указана в табл. 3. Быстродействие замеряется для всех целевых операционных систем. В связи с тем, что после компрессии размер сообщения может немного изменяться, в таблице указано среднее значение для 10 запусков. Необходимо обратить внимание на то, что измерения проводятся в многозадачных операционных системах, поэтому из результатов экспериментов исключались пиковые значения, которые могли быть вызваны, например, работой антивирусного программного обеспечения или процессов с высоким приоритетом.
Полученные результаты можно усреднить для всех рассматриваемых операционных систем (см. табл. 4), поскольку эксперименты выполняется на одинаковой аппаратной платформе. Эта оценка является нижней границей для оценки быстродействия рассматриваемых алгоритмов. Для более точных оценок надо настраивать операционные системы так, чтобы получить похожую загрузку фоновыми службами, что в рамках этой работы не делалось.
В табл. 5 приведен размер кода, необходимого для сериализации и десериализации. Эти оценки применимы только для рассматриваемой структуры данных и получены при
программировании на языке С++. Дополнительно в этой таблице указана степень интеграции с фреймворком Qt.
Таблица 3 - Быстродействие технологий Windows 10/Alt Linux 8.2/Astra Linux 1.6
Формат Длина блока Время работы Время работы Время работы Время работы
данных кодера, мкс декодера, мкс компрессора, мкс декомпрессора, мкс
XML 16 33/25/26 45/34/43 33/61/89 15/10/11
512 240/220/198 330/279/287 365/319/327 51/53/49
2048 770/780/785 1070/1084/1079 1290/1450/1387 127/153/146
JSON 16 30/19/25 21/19/24 30/70/96 12/10/12
512 160/157/168 175/195/197 290/157/350 40/51/54
2048 631/636/665 780/760/795 1191/1430/1390 121/156/161
Google 16 2/1/1 2/1/1 24/59/81 5/1/3
protobuf 512 4/2/2 3/1/2 135/145/177 34/25/25
2048 6/3/3 4/3/3 452/326/337 88/68/64
CBOR 16 7/4/5 76/16/75 31/61/85 4/2/3
512 27/20/22 119/107/125 250/200/235 38/34/34
2048 81/73/85 185/199/310 1280/634/740 96/94/110
Таблица 4 - Усредненное быстродействие технологий
Формат Длина блока Время работы Время работы Время работы Время работы
данных кодера, мкс декодера, мкс компрессора, мкс декомпрессора, мкс
XML 16 28 41 61 12
512 219 299 337 51
2048 778 1078 1376 142
JSON 16 25 21 65 11
512 162 189 266 48
2048 644 778 1337 146
Google 16 1 1 55 3
protobuf 512 3 2 152 20
2048 4 3 372 73
CBOR 16 5 56 59 3
512 23 117 228 35
2048 80 231 885 100
Таблица 5 - Размер кода для сериализации и десериализации данных
Формат Степень интеграции с Qt Количество строк кода
Кодер Декодер
XML Входит в модуль Qt core 38 33
JSON Входит в модуль Qt core 25 19
Google Protobuf Отдельная библиотека 1 1
CBOR Входит в модуль Qt core 26 61
Выводы
В распределенных вычислительных системах существует гораздо большее количество форматов обмена данными, чем рассмотрено в этой статье. Их многообразие вызвано широким спектром требований, которые к ним предъявляются. Такими требованиями являются как рассмотренные выше быстродействие, эффективность представления данных, степень интеграции со средой разработки, так и дополнительные требования, например, патентная чистота, распространенность, необходимость поддержки старого парка программного обеспечения и многое другое, поэтому рекомендовать формат вне контекста прикладного процесса практически невозможно.
При проектировании современных систем целесообразно использовать бинарные форматы представления данных. Если на прикладном уровне в них инкапсулируется
большое количество повторяемых бинарных данных или текстовых сообщений и требуется минимизировать объем трафика, то рекомендуется использовать компрессию данных.
Допустимы ситуации, когда в качестве исходной точки берется общеизвестный формат, который потом масштабируется исходя из текущих потребностей, но это целесообразно только в том случае, когда у компании достаточно ресурсов для поддержания сложных и теперь уже проприетарных решений.
Литература
1. Extensible Markup Language (XML). URL: https://www.w3.org/XML/ (accessed 01 Apr. 2019).
2. RFC 7303 URL: https://tools.ietf.org/html/rfc7303 (accessed 08 Apr. 2019).
3. Java Script Object Notation. URL: https://www.json.org (accessed 01 Apr. 2019).
4. RFC 8259 URL: https://tools.ietf.org/html/rfc8259 (дата обращения 01.04.2019).
5. ECMA-404. The JSON Data Interchange Syntax URL: https://www.ecma-international.org/publications/standards/Ecma-404.htm (accessed 08 Apr. 2019).
6. Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data. URL: https://developers.google.com/protocol-buffers/ (accessed 01 Apr. 2019).
7. Concise Binary Object Representation. URL: https://cbor.io/ (accessed 01 Apr. 2019).
8. RFC 7049 URL: https://tools.ietf.org/html/rfc7049 (accessed 01 Apr. 2019).
Статья поступила 22 июля 2019 г.
Информация об авторах
Шаптала Василий Сергеевич - Начальник лаборатории ПАО «Интелтех». Кандидат технических наук.
Солнцев Денис Викторович - Инженер-программист.
Тел. 8(812)448-19-01 (доб.12-15). E-mail: shaptalavs@inteltech.ru.
Адрес: 197342, г. Санкт-Петербург, Кантемировская ул., д.8.
On modern data exchange formats between components of distributed computing system
Abstract. The article aims to justify the choice of data exchange format in distributed computing system. The purpose of the work is the study of key technologies that implement the main methods of data exchange and are publicly available. The simulation uses cross-platform programming methods. The novelty of this review is in summarizing the features of using various data exchange formats that are used in the construction of distributed computing systems. The results of the work should include a comparison of two approaches of interaction between distributed components: creating your own proprietary solutions and using open implementations of data exchange formats, also creating a test application using cross-platform framework Qt to analyze the main methods to exchange data between components of distributed system. The results of the work of test application are given on target operating systems: Windows (Windows 10) и Linux (Astra Linux 8.2 и Alt Linux 1.6). The practical benefit of the work is in assessing the characteristics that allow the choice of a particular data exchange format: speed, size of memory used and integration features. The estimates obtained allow us to choose a data exchange technology in a distributed computing environment depending on the requirements of the application area. The availability of implementations of main data exchange formats in the form of libraries distributed under free licenses and/or open source code allows the use of such technologies in distributed systems whose components could be run on most common operating systems and hardware platforms.
Keywords: distributed computing, XML, JSON, PROTOBUF, CBOR.
Information about Authors
Shaptala Vasilij Sergeevich - Head of laboratory PJSC "Inteltech". Ph.D.
Solntsev Denis Viktorovich - Software engineer of PJSC "Inteltech".
Tel. 8(812)448-19-01 (12-15). E-mail: shaptalavs@inteltech.ru. Address: Russia, 197342, Saint-Petersburg, Kantemirovskaya street 8.
Для цитирования: Шаптала В.С., Солнцев Д.В. О современных форматах обмена данными между компонентами распределенной вычислительной системы // Техника средств связи. 2019. № 4 (148). С. 51-58.
For citation: Shaptala V.S., Solntsev D.V. On modern data exchange formats between components of distributed computing system // Means of communication equipment. 2019. NO 4 (148). P. 51-58. (In Russian).