Практическое руководство по сжатию HTTP-запросов веб-клиента

Если вы оказались в ситуации, когда вы отправляете большие полезные данные HTTP-запроса из веб-браузера в свой API, вы, вероятно, допустили архитектурную или дизайнерскую ошибку. Тем не менее, наша команда недавно оказалась в ситуации, когда регулярная отправка 200–300 КБ запросов давала нам наименьшее количество компромиссов.

Как следствие, мы наблюдали длительное время передачи запросов. В отличие от того, как браузеры автоматически распаковывают тела HTTP-ответов с помощью механизма согласования содержимого HTTP (посредством заголовка Accept-Encoding), браузер не сжимал автоматически тела наших запросов. Неужто был подобный механизм, который мы могли бы задействовать в этом случае?

Мы ошибались

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

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

Прежде чем мы пришли к сжатию GZIP на стороне клиента в качестве нашего решения, мы изучили ряд альтернатив:

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

На ум пришел gRPC, поскольку мы успешно использовали его в качестве протокола связи микросервисов; однако он пока не поддерживается в браузере, поэтому нам пришлось двигаться дальше.

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

GZIP как наш G6

Учитывая, что алгоритм GZIP существует уже некоторое время, мы надеялись, что кто-то реализовал его в Javascript. Мы осмелились мечтать, что это будет еще и Javascript, который тоже можно будет запускать в браузере. К счастью, наш поиск привел нас на страницу pako npm, и, учитывая ее тесты и тесты, казалось, что это был именно тот пакет, который мы искали.

Что касается интеграции с вашей базой кода на стороне клиента, если вы используете axios, обычную библиотеку HTTP-запросов Javascript, лучше всего, вероятно, интегрировать ее в качестве глобального преобразователя запросов:

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

Для принимающей стороны Axiom Zen обычно записывает внутренний код на Go (golang). Это оказалось отличной демонстрацией мощности и элегантности интерфейсов в Go, как видно из этого игрушечного примера:

Обратите внимание на то, что функции json.NewDecoder() все равно, что io.Reader она отправляет.

Еще одна вещь, на которую следует обратить внимание (не показано здесь), заключается в том, что если вам нужно сделать совместное использование ресурсов между источниками (CORS), вам нужно добавить заголовок Content-Encoding в свой список Access-Control-Allow-Headers при обработке предварительных -flightOPTIONS requests.

Результаты, достижения

Мы увидели 10-кратное уменьшение размера (данные JSON по своей природе довольно часто повторяются) с крошечным штрафом ЦП на стороне клиента. Хотя наш подход может потребовать настройки с вашей стороны, мы остались довольны результатами, которые были особенно заметны при разговоре с удаленными серверами.

Дайте нам знать, что вы думаете о нашем подходе, в комментариях ниже!

Написано Крисом Скоттом и командой инженеров Axiom Zen.
Под редакцией Ясмин Надери и Брайс Бладон

Смотрите больше подобных историй в блоге Axiom Zen.