Таблица 1. Сравнение существующих технологий
Выводы из обзора
В данный момент реализовано множество решений: как кодирования структурированной информации, так и удаленного вызова процедур. Данный обзор позволяет сформулировать требования к реализации, набору возможностей, дизайну сообщений и сервисов.
Как было видно из обзора, синтаксис языка для сообщений и сервисов должен быть C-подобным, чтобы быть легковоспринимаемым для разработчика. Требуется возможность делать иерархии объектов. Наличие исключений также обязательно.
Возможности технологии не должны быть слишком обширными, чтобы не получилась тяжеловесная система. Со стороны проекта существует требование, чтобы решение было максимально простым и понятным для стороннего разработчика.
Также требуется высокий уровень абстракции, вся низкоуровневая работа должна быть скрыта от разработчика. Разработчик должен иметь возможность задавать иерархию объектов и не задумываться о технических деталях. Сервисы должны быть такими, чтобы внешне вызов метода выглядел как вызов локального метода.
Глава 2. Алгоритм работы Основными характеристиками производительности RPC является скорость сжатия и качество сжатия. Для достижения лучших результатов были проделаны некоторые оптимизации, которые позволили уменьшить объём потребляемого трафика: объединение запросов, использование идентификаторов, жесткая структура сообщений, пулы объектов, оптимальное создание объектов.
Чаще всего клиент реализован так, что делает разнообразные запросы к серверу достаточно часто. Поэтому возможна некоторая оптимизация, которая позволит сократить количество физических запросов к серверу. Для этого необходимо объединять запросы в один и делать отправку на сервер. Сервер вернет ответ сразу на все запросы, а клиент далее вызовет необходимые методы соответствующие каждому запросу. Данная оптимизация позволяет не делать вызовы по несколько раз в секунду, а делать, например, один вызов в секунду.
Использование метапрограммирования, а конкретно системы MPS, позволяет сохранять и получать идентификаторы узлов сущностей, узлов сервисов, узлов методов. Поэтому правильным решением является идентифицировать метод сервиса не по его названию или порядковому номеру, а по идентификатору узла этого метода. Это даёт возможность переименовывать методы, менять их порядок, сигнатуру, при этом всё будет работать точно также. Кроме того, это дает возможность называть методы одинаково и определять различные типы или наборы параметров, поддерживать полиморфизм-перегрузку.
В обзоре литературы было указано несколько методов кодирования структурированной информации. Основой для алгоритма кодирования был выбран protobuf, поскольку он предлагает простую реализацию и хорошие результаты работы. Сжатие чисел происходит посредством отсечения ненужных септетов. Далее информация приводится в base64 вариант, чтобы иметь возможность передавать работать с веб-клиентами. Существовал вариант сделать несколько вариантов кодирования, но на данном этапе было принято решение ограничиться одним, поскольку в противном случае клиенту и серверу пришлось бы обмениваться дополнительной информацией, хранящей в себе необходимый формат.
Данное ограничение увеличивают размер по сравнению с protobuf, однако были произведены улучшения, которые помогли улучшить сжатие. Был произведен отказ от хранения номеров полей, что позволило добиться лучшего сжатия. Это стало возможно благодаря тому, что нет требований к такой жесткой поддержке версионирования. Если поле удалено из сообщения, то клиент, который несет в себе старую версию сообщений, стал нерабочим в силу того, что не может выполнить какую-то функциональность, т.к. получает от сервера или передает ему недостаточную информацию. Также это позволяет добиться некоторого ускорения: при раскодировании нет нужды определять, какое именно поле раскодируется, его тип и прочее, всё это известно заранее и может быть сразу же сгенерировано.
Однако все методы кодирования никак не основываются на знаниях о том, какие данные передаются в сообщениях. Например, при передаче объединенных сообщений, скорее всего, будут похожие запросы. При написании веб-сервиса почти всегда присутствует база данных на сервере, а значит, присутствуют идентификаторы сущностей. Также было указано, что присутствуют идентификаторы методов.
В данной работе была использована оптимизация, которая не может быть применена на любых структурных данных, но даёт заметный выигрыш при использовании описанных выше данных: имеющих одинаковые идентификаторы. Было решено, что программист может определить каждому полю в сообщении дескриптор “usepool”, описание которого дано дальше.
В язык были включены пулы объектов. Данный алгоритм работает следующим образом: при кодировании сообщения обработчик кладёт объект в пул и заменяет его в иерархии на порядковый идентификатор в пуле. Если объект уже находится там, то второй раз он не добавляется туда. Данный алгоритм применяется только для базовых типов, поскольку не требует больше никаких действий по кодированию структур.
Если в пуле уже много объектов, то новые идентификаторы будут большими, поэтому существует вариант создать несколько пулов. Было проведено тестирование и определено, что создание нового пула не дает существенного уменьшения объема информации. Однако поддержка нескольких пулов менее понятна для разработчика и тратит больше процессорного времени. Поэтому было решено отказаться от поддержки возможности иметь несколько пулов объектов и ограничиться одним.
Ниже (табл.2) приведены результаты замеров времени кодирования и результирующего объема сообщений.
| Время на кодирование
| Итоговый объём
| Без пулов
| 152034 нс
| 1467482 байта
| С одним пулом
| 176403 нс
| 1096588 байта
| С несколькими пулами
| 193698 нс
| 1081232 байта
| Таблица 2. Результаты тестирования трех вариантов кодирования
Тестирование производилось следующим образом: было выбрано около сотни реальных моделей данных различных размеров: от 1кб до 10мб (размеры в кодировании без пулов). Далее для каждого варианта была запущена 100000 раз цепочка кодирование-декодирование, взято среднее значение. После чего тестирование было произведено 500 раз и взято среднее значение.
Из таблицы видно, что итоговый объём без применения пулов уменьшился приблизительно на 26%, замедление же составило порядка 11%. Таким образом, данный алгоритм является выгодным. Применение же нескольких пулов практически не дало уменьшения объёма сообщений, вызвав при этом замедление и усложнение синтаксиса языка.
Технология protobuf использует паттерн Builder для создания сообщений. Это заметно замедляет время создания объектов, поскольку требует выполнение дополнительного кода. Поэтому в данной работе создание объектов происходит в стиле POJO, что ускоряет создание примерно в два раза, тем самым повышая скорость десиарилизации.
Из этой части работы становится видно, что алгоритм сжатия является оптимальным при условии осведомленности о кодируемых данных. Так как за основу был выбран protobuf, то кодирование производится быстро и качественно, не уступает другим алгоритмам по скорости и качеству сжатия: существует открытый проект, в рамках которого производится сравнение существующих сжимающих алгоритмов [24].
|