Простая минификация Json тел запросов / ответов с Kotlin Serialization

от автора

Привет!

Недавно в рамках одного из проектов на стеке KMP, Ktor и Kotlin Serialization мы с командой решили провести эксперимент и определить возможность и целесобразность минификации тел запросов / ответов на Json.

Да, мы знаем про GraphQL, Protobuf и др., но в нашем случае имел место необузданный интерес наколхозить такое решение. И при всей его наивности удалось сократить средний размер итоговых джсонов (после всех внутренних оптимизаций) на 15–20%.

Вводные данные:

  1. Большое приложение на KMP с таргетами iOS, Android, Web и Desktop;

  2. Фронтенд и бэкенд написаны на Ktor и швыряются Json’ами по HTTP;

  3. Монорепа и по сути один KMP проект.

Структура проекта:

frontend‑app — модуль с реализацией фронтенда на KMP;

backend‑app — модуль с реализацией бэкенда на Kotlin + Ktor.

http‑model — модуль с моделями, общими между бэкендом и фронтендом; чистый Kotlin и Kotlin Serialization.

Задача

У нас есть списочный Json, который весит 4 754 байт. По сути единственной сущностью списка является следующий объект:

{   "interestId": "f5092d67-1ba7-4e7a-8eed-75ba2726c242",   "title": "Антенны дальнего действия",   "imageUrl": null,   "category": {     "interestCategoryId": "6ac16b9f-9d2b-4bd4-b2aa-a5d35d727ecd",     "title": "Аналог",     "interestCategoryOrder": 0   },   "interestOrder": 0 }

Объект и его структура (включая ключи) дублируются n кол‑во раз, где n — размер списка, что логично для Json формата.

А вот наша модель Interest, общая между бэкендом и фронтендом, лежащая в модуле common‑model:

@Serializable data class Interest(   val interestId: Uuid,   val title: String,   val imageUrl: String?,   val category: InterestCategory,   val interestOrder: Int )

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

Рождается предположение: «А зачем нам сохранять читаемость Json? Для кого?»

Соответственно, обновленная модель:

@Serializable data class Interest(   @SerialName("iid")   val interestId: Uuid,    @SerialName("t")   val title: String,    @SerialName("iu")   val imageUrl: String?,    @SerialName("c")   val category: InterestCategory, // В InterestCategory аналогично    @SerialName("io")   val interestOrder: Int )

Обновленный объект Json:

{   "iid": "f5092d67-1ba7-4e7a-8eed-75ba2726c242",   "t": "Антенны дальнего действия",   "iu": null,   "c": {     "ici": "6ac16b9f-9d2b-4bd4-b2aa-a5d35d727ecd",     "t": "Аналог",     "ico": 0   },   "io": 0 }

Итоговый вес изначального Json’а с минифицированным объектом — 3 890 байт, т. е. ~ 80% от исходного. 20% веса банально занимал нейминг ключей. А ведь зависимость ~ O(nk), где n — размер списка, а k — вложенность объекта списка.

Какие могут быть проблемы?

Единственное за чем необходимо было следить, чтобы в рамках одной модели не было двух идентичных значений @SerialName.

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

Использовали бы мы это в проде?

Уже использовали в рамках того же проекта. Работает хорошо, на метрики повлияло в позитивном ключе. В каких‑то местах менее существенно, в каких‑то ощутимо.

Для нас это был безболезненный и дешёвый вариант: на всё про всё ушла ~ 1 человеко‑ночь под пивом, в то время как банальная миграция на Protobuf или GraphQL была бы на порядок сложнее, запутаннее и менее привлекательной для заказчиков.

Внимание!

С нашим мультиплатформенным стеком это действительно было неплохим решеним, однако если, скажем, Ваш проект состоит из нескольких команд: веба на JS, отдельно нативных мобилок и отдельно бэка на, например, Java, нужно серьёзно задуматься.

Стоят ли эти 15–20% прироста производительности написания очень качественной документации и спецификации и постоянного синка нечитаемого нейминга в случае каких‑то изменений?

Моё мнение — не стоит. Зависит от масштаба проекта, команды и радикальности руководства.


ссылка на оригинал статьи https://habr.com/ru/articles/933474/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *