×

Порівнюємо два формати серіалізації даних: Protobuf vs JSON

Привіт, мене звати Ярослав. Я займаюся розробкою в компанії Evrius. У цій статті ми порівняємо два формати серіалізації даних та ознайомимося з інструментами, які оптимізують її виконання. Інформація буде цікавою гоферам, які використовують серіалізацію для збереження та передачі даних.

Ця стаття є продовженням задачі, яку я розв’язував в офісі (тут ностальгійка, бо зараз працюю дистанційно).

Приклади коду доступні в репозиторії.

Історичні рішення, які треба переписати

На практиці це здається простим: з’явилася задача, її виконали швидко й легко, використовуючи стандартні інструменти, і всі задоволені. А з часом, хай за рік, змінились умови, збільшився трафік тощо, і те красиве рішення, що було спочатку, треба переписати. Знайомо?

JSON to Protobuf

У моєму робочому проєкті в одному з мікросервісів є операція, яка на кожен запит від користувачів зберігає JSON в key-value базу даних на три години. За рік користувачів стало більше, і ці операції збереження почали перевантажувати мережу (гарний початок для страшного оповідання).

Для зменшення трафіку і розміру БД ми вирішили замінити JSON на Protobuf. У результаті об’єм трафіку зменшився на третину, і це розв’язало проблему.

Але перед тим, як замінити, провели мікробенчмарки, якими й хочу поділитись далі.

JSON vs Protobuf, стандартна реалізація через рефлексію

У цьому мікробенчмарку Protobuf справді виграє у JSON. Але ми дамо JSON другий шанс у наступних порівняннях, щоб побачити можливості для розвитку у Protobuf.

Приклади структур буду скорочувати в ..., а повні можна глянути в репозиторії, де я проводив тести.

Для прикладу візьмемо GitHub API:

{
  "id": 23096959,
  "node_id": "MDEwOlJlcG9zaXRvcnkyMzA5Njk1OQ==",
  "name": "go",
  "full_name": "golang/go",
  "private": false,
  "owner": {
    // …
  },
  // …
  "license": {
    // …
  },
  // …
  "organization": {
    // …
  },
  "network_count": 10164,
  "subscribers_count": 3448
}

За допомогою онлайн-інструмента JSON to Go конвертуємо попередньо отримані дані в Go-структуру, яку будемо використовувати для серіалізації:

type Repository struct {
    ID               int          `json:"id"`
    // …
    Owner            Owner        `json:"owner"`
    // …
    License          License      `json:"license"`
    // …
    Organization     Organization `json:"organization"`
    // …
}

type Owner struct {
    Login             string `json:"login"`
    ID                int    `json:"id"`
    // …
}

type License struct {
    Key    string `json:"key"`
    // …
}

type Organization struct {
    Login             string `json:"login"`
    ID                int    `json:"id"`
    // …
}

Через інший, ще сирий інструмент JSON to Protobuf конверую в:

syntax = "proto3";

package protos;

message Repository {
  uint32 id = 1;
  // …
  Owner owner = 6;
  // …
  License license = 69;
  // …
  Organization organization = 75;
  // …
}

message Owner {
  string login = 1;
  uint32 id = 2;
  // …
}

message License {
  string key = 1;
  // …
}

message Organization {
  string login = 1;
  uint32 id = 2;
  // …
}

Я підготував і заповнив структури даними з JSON, які отримав з GitHub API раніше. Тепер можемо провести бенчмарки:

import (
    "encoding/json"
    "github.com/stretchr/testify/require"
    "gitlab.com/go-yp/proto-vs-json-research/models/fulljson"
    "testing"
)

func BenchmarkRepositoryMarshalJSON(b *testing.B) {
    var repository = &jsonExpectedRepository

    for i := 0; i < b.N; i++ {
        _, _ = json.Marshal(repository)
    }
}

func BenchmarkRepositoryUnmarshalJSON(b *testing.B) {
    var fixture = []byte(jsonRepositoryFixture)

    for i := 0; i < b.N; i++ {
        var repository = &fulljson.Repository{}

        json.Unmarshal(fixture, repository)
    }
}
import (
    "encoding/json"
    "github.com/golang/protobuf/proto"
    "github.com/stretchr/testify/require"
    "gitlab.com/go-yp/proto-vs-json-research/models/protos"
    "testing"
)

func BenchmarkRepositoryMarshalProto(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _, _ = proto.Marshal(protoExpectedRepository)
    }
}

func BenchmarkRepositoryUnmarshalProto(b *testing.B) {
    var repository = protoExpectedRepository
    var content, marshalErr = proto.Marshal(repository)

    require.NoError(b, marshalErr)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var repository = &protos.Repository{}

        _ = proto.Unmarshal(content, repository)
    }
}
Назва тестуСередній час ітераціїВиділення пам’яті
BenchmarkRepositoryMarshalJSON13172 ns/op6146 B/op 1 allocs/op
BenchmarkRepositoryUnmarshalJSON51246 ns/op6256 B/op 105 allocs/op
BenchmarkRepositoryMarshalProto8302 ns/op4208 B/op 8 allocs/op
BenchmarkRepositoryUnmarshalProto9357 ns/op5968 B/op 94 allocs/op

Як і очікували, Protobuf швидше серіалізує та потребує менше пам’яті.

JSON серіалізується в 5488 байтів, а Protobuf у 3811 байтів. У нашому прикладі на 30% менше пам’яті займає Protobuf.

Розглянемо «таємничний» 1 allocs/op при серіалізації JSON у бенчмарку BenchmarkRepositoryMarshalJSON. Стандартна бібліотека encoding/json має кеш sync.Pool, який перевикористовує раніше виділену пам’ять:

package json

// …

var encodeStatePool sync.Pool

func newEncodeState() *encodeState {
    if v := encodeStatePool.Get(); v != nil {
        e := v.(*encodeState)
        e.Reset()
        // …
        return e
    }
    return &encodeState{ptrSeen: make(map[interface{}]struct{})}
}

// …

func Marshal(v interface{}) ([]byte, error) {
    e := newEncodeState()

    err := e.marshal(v, encOpts{escapeHTML: true})
    if err != nil {
        return nil, err
    }
    buf := append([]byte(nil), e.Bytes()...)

    encodeStatePool.Put(e)

    return buf, nil
}

Таким чином отримуємо алокацію пам’яті buf := append([]byte(nil), e.Bytes()...) на всіх ітераціях циклу, окрім першої, де створюється encodeState.

Відсутня можливість виключити доданий кеш, щоб перевірити справжнє число алокацій.

JSON з кодогенерацією та перспективи серіалізації Protobuf

Коли тільки почав вчити Golang за уроками з «Техносфери», то дізнався, що стандартна JSON-серіалізація в Golang під капотом зроблена через рефлексію, яка використовує багато ресурсів у високонавантажених системах. Так розробники створили свою реалізацію JSON-серіалізації через кодогенерацію easyjson. Вона виконується швидше, потребує менше пам’яті та відбувається без рефлексії на момент виконання коду.

Для нашої структури Repository згенеруємо код, який буде серіалізувати в JSON.

Спершу встановимо easyjson:

go get -u github.com/mailru/easyjson/...

Тепер до структури Repository додамо службовий коментар easyjson:json. Він потрібний, щоб easyjson побачив, для якої структури треба згенерувати код:

//easyjson:json
type Repository struct {
    ID               int          `json:"id"`
    // …
    Owner            Owner        `json:"owner"`
    // …
    License          License      `json:"license"`
    // …
    Organization     Organization `json:"organization"`
    // …
}

type Owner struct {
    Login             string `json:"login"`
    ID                int    `json:"id"`
    // …
}

type License struct {
    Key    string `json:"key"`
    // …
}

type Organization struct {
    Login             string `json:"login"`
    ID                int    `json:"id"`
    // …
}

І запустимо кодогенерацію:

easyjson ./models/fulljson/repository.go

~/go/src/gitlab.com/go-yp/proto-vs-json-research
└── models
    └── fulljson
        ├── repository_easyjson.go [+]
        └── repository.go

У згенерованому файлі repository_easyjson.go нам будуть потрібні методи для серіалізації структури Repository:

// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.

// …            
            
// MarshalJSON supports json.Marshaler interface
func (v Repository) MarshalJSON() ([]byte, error) {
    // …
}
 
// …

// UnmarshalJSON supports json.Unmarshaler interface
func (v *Repository) UnmarshalJSON(data []byte) error {
    // …
}

Оновимо бенчмарки, які використовують згенеровані методи, і запустимо:

func BenchmarkRepositoryEasyMarshalJSON(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _, _ = jsonExpectedRepository.MarshalJSON()
    }
}

func BenchmarkRepositoryEasyUnmarshalJSON(b *testing.B) {
    var fixture = []byte(jsonRepositoryFixture)

    for i := 0; i < b.N; i++ {
        var repository = fulljson.Repository{}

        _ = repository.UnmarshalJSON(fixture)
    }
}
Назва тестуСередній час ітераціїВиділення пам’яті
BenchmarkRepositoryMarshalJSON13172 ns/op6146 B/op 1 allocs/op
BenchmarkRepositoryUnmarshalJSON51246 ns/op6256 B/op 105 allocs/op
BenchmarkRepositoryEasyMarshalJSON9718 ns/op6867 B/op 8 allocs/op
BenchmarkRepositoryEasyUnmarshalJSON13996 ns/op4128 B/op 86 allocs/op
BenchmarkRepositoryMarshalProto8302 ns/op4208 B/op 8 allocs/op
BenchmarkRepositoryUnmarshalProto
9357 ns/op5968 B/op 94 allocs/op

Як бачимо, ефективність десеріалізації значно підвищилась. А ось серіалізація стала використовувати більше пам’яті через відсутність схожого кешу, як у стандартної бібліотеки.

Епілог

Коли завершив статтю і відіслав друзям, від Павла дізнався, що вже є інструмент protoc-gen-gogofaster, який працює без рефлексії.

Protobuf-серіалізація без рефлексії

Ми під’єднаємо protoc-gen-gogofaster, згенеруємо новий код для Protobuf-серіалізації, оновимо бенчмарки та порівняємо результати.

Під’єднуємо та генеруємо (Makefile):

gogofaster:
    go get github.com/gogo/protobuf/protoc-gen-gogofaster

proto:
    protoc -I . protos/*.proto --gogofaster_out=models

make gogofaster

make proto

У результаті файл repository.pb.go буде мати 6576 рядків коду замість 1192, які були згенеровані стандартним інструментом protoc.

Оновимо бенчмарки:

func BenchmarkRepositoryFasterMarshalProto(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _, _ = protoExpectedRepository.Marshal()
    }
}

func BenchmarkRepositoryFasterUnmarshalProto(b *testing.B) {
    var content, marshalErr = protoExpectedRepository.Marshal()
    require.NoError(b, marshalErr)

    {
        var repository = protos.Repository{}
        var unmarshalErr = repository.Unmarshal(content)

        require.NoError(b, unmarshalErr)

        require.Equal(b, protoExpectedRepository, &repository)
    }

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var repository = protos.Repository{}

        _ = repository.Unmarshal(content)
    }
}
Назва тестуСередній час ітераціїВиділення пам’яті
BenchmarkRepositoryMarshalJSON13172 ns/op6146 B/op 1 allocs/op
BenchmarkRepositoryUnmarshalJSON51246 ns/op6256 B/op 105 allocs/op
BenchmarkRepositoryEasyMarshalJSON9718 ns/op6867 B/op 8 allocs/op
BenchmarkRepositoryEasyUnmarshalJSON13996 ns/op4128 B/op 86 allocs/op
BenchmarkRepositoryMarshalProto8302 ns/op4208 B/op 8 allocs/op
BenchmarkRepositoryUnmarshalProto9357 ns/op5968 B/op 94 allocs/op
BenchmarkRepositoryMarshalProto8302 ns/op4208 B/op 8 allocs/op
BenchmarkRepositoryUnmarshalProto9357 ns/op5968 B/op 94 allocs/op
BenchmarkRepositoryFasterMarshalProto1705 ns/op4096 B/op 1 allocs/op
BenchmarkRepositoryFasterUnmarshalProto
3894 ns/op4784 B/op 89 allocs/op

Як бачимо, результати стали кращими.

Уже після написання статті дізнався про інструмент, який мені потрібнен — protoc-gen-gogofaster. Сподіваюсь, цей простий мікробенчмарк стане корисним, коли захочете мігрувати з JSON-у на Protobuf, а також зможете його використовувати як шаблон для своїх досліджень. У цій статті мені вдалось поєднати дві речі: обмін досвідом та можливість краще розібратись з інструментами.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному5
LinkedIn

Схожі статті




63 коментарі

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

Как-то непонятно, это что за данные такие по сети перекидываются, что увеличение количества пользователей перегружает сеть?

Тема про порівняння форматів, якщо б міг додати інформацію про внутрішні дані так би і зробив

У результаті об’єм трафіку зменшився на третину, і це розв’язало проблему.

Ні. Це просто відклало проблему «на потім». На дуже близьке «потім».

Ось тут вже писав, що у проекті налаштоване маштабування серверів для мікросервісів так і для БД

2020 год, Гугл более чем 5 лет назад запилил
google.github.io/flatbuffers
как zero-copy deserialization с разными плюшками...
Есть масса других интересных способов сериализации...

Було б цікаво подивитись саме бенчмарки

З того що знайшов це:
Cap’n Proto, FlatBuffers, and SBE
JSON vs Protocol Buffers vs FlatBuffers

Ще знайшов приклад Comparison of Protocol Buffers v/s FlatBuffers де виграє FlatBuffers у Protobuf-а з рефлексією, а чи виграє у Gogofaster-Protobuf треба тестувати

Переход от json к protobuf в существующей системе дело затратное, но самое оно для любителей переписать все с нуля.

Менее затратный путь — msgpack. А для java так вообще с drop-in extension jackson-dataformat-msgpack

На прошлом проекте снизил на 10-15% сетевой трафик от пользовательских устройств к серверам.

якщо говоримо про java є цікавий проект protostuff, він має.можливість динамічно створювати proto, це дозволяє дуже спростити кількість змін в існуючій системі (банально додатеовий конвертер зявляється), ще jackson бібліотека має підпроект для protobuff, але він вже програє в продуктивності

Отличная статья, большое спасибо !

видимо речь об перегрузке внутренней сети от сервера к бд? или что там у них между «микросервисами»? такое вообще возможно?

если «объём трафика уменьшился на треть» что вы будете делать когда пользователей снова прибудет и трафик снова вырастет? скажем вдвое как вполне реалистичная оценка

До переходу вже було запущенно N серверів з мікросервісом і M серверів з key-value БД, і зараз вже більше серверів ніж півроку тому

Тому при збільшені трафіку число серверів з мікросервісами і БД буде збільшуватись, але сервери коштуть грошей і правильним рішенням було саме зменшити трафік

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

в современных реалиях как вы этого добиваетесь

Великий RPS

в смысле у вас внешний rps настолько велик что с его обработкой не справляется несколько внутренних нодов для микросервисов и отдельно для баз данных и соотв. внутренняя сеть между ними?

а ну ок ))

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

В основному так, але серверів більше

Поддерживаю вопрос коллеги.

С точки зрения Json vs Protobuf — отличная статья.
Но с точки зрения почему было выбран этот путь — не вижу объяснения.

Больше похоже — у нас проблема, а давайте вот так сделаем

С точки зрения Json vs Protobuf — отличная статья.

Дякую

Но с точки зрения почему было выбран этот путь — не вижу объяснения.

Поекспериментувати з внутрішніми даними і на цій основі вибрали таке рішення

В процесі написання статті обговорив з керівництвом про що саме можу розповідати, тому пояснень мало

Может там микросервисы общаются между собой по синхронным протоколам.

да всё равно у них скорее всего tcp )) т.е. с точки зрения сети там настолько глубоко... всё равно )) что вот от слова вообще

в принципе допустим сетевая проблема может быть как решение это если бы б целный message не помещался в сетевой кадр а если бы б помещался то это бы б существенно увеличивало бы б общей performance сети именно уже в согласовании с низким уровнем

... но как можно сделать так чтобы внешний rps мог «завалить» внутреннюю сеть... ну если умеючи значит можно! ))

У мене була проблема дуже схожа на описану в статті. Ми зберігали дані від багатьох інстансів мікросервісу в редіс і ботлнеком, власне, була пропускна спроможність мережевих карт інстансів з редісом, оскільки цих інстансів було менше, ніж інстансів мікросервісу.

В моєму випадку кількість запитів зростала поступово і використання кращого алгоритму компресії даних (ми також зупинилися на protobuf, зробивши бенчмарки 5 різних технологій) дозволило вирішити проблему.

І так, якщо кількість користувачів і запитів зростає, то потрібно шукати інше архітектурне рішення, але в багатьох випадках це зростання не надто швидке і краще за тиждень імплементувати рішення, яке відкладе проблему на кілька місяців, ніж місяць переписувати систему і кожен день гасити пожежі на проді.

Раджу подивитись чи почитати доповідь про 3х Кента Бека. Там дуже добре описано цей концепт вирішення проблем ботлнеків, але рівно настільки, щоб продукт міг отримати нову порцію користувачів і не займатись передчасними оптимізаціями

Ну і так, можна оптимізувати бізнес логіку вашого серверу з врахуванням mtu packet size та інших параметрів мережі, але я не думаю, що це варто робити більшості людей, які деплоять свої сервіси в клауд.

А от враховувати високорівневі параметри доступних їм інстансів, такі як пропускна спроможність мережі (незалежно від того, чим вона обмежена) можна навіть не вдаючись в деталі того як ця інфраструктура працює всередині

А як воно жметься? Чи не можна було того ж результату досягнути пожмакавши JSON?

Хорошо жмется. Там сравнимые результаты, но protobuf все равно немного эффективнее был на моих тестах.

в смысле жмётся лучше чем json? странно мне всегда казалось что чем лучше протокол жмётся тем больше значит у него «внутренней пустоты» а не наоборот ))

ЗЫ: тут фишка видимо в том «как бывает если за системные вещи берутся джаваскриптеры» ))

в смысле жмётся лучше чем json?

нет, в смысле пожатый json по размеру не будет особо отличаться от protobuf-а. Сам protobuf немного пожать можно, конечно, но там выигрыш не особо большой.

готов спорить пожатый json обгонит protobuf непожатый причём возможно и пожатый тоже просто потому что чистый текст слишком хорошо жмётся а protobuf это одна из вариаций «двоичного xml» кстати есть же ж и binary json причём что интересно я перепроверил штука изобретённая как раз MongoDB т.е. в контексте вообще торт ))

ну и потом фактически на сегодня лично по моему конечно же ж нескромному мнению но «сказал protobuf и не сказал grpc» это уже моветон

причём на столько что у самого тс таки уже есть стаття прямо на доу ))

Приклад gRPC-мікросервісу на Go

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

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

Потролил так потролил 🙂. На счет размеров, зависит от того, что в данных. Я даже почти не сомневаюсь, что можно найти пример, когда пожатый json будем сильно меньше весить. например

{"v": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}

Оглядаючих на попередні статті і сам бачу, що міг би написати краще і написати в іншій послідовності

Так склалось більше історично, бо в той момент хотів написати саме про досвід з gRPC і один з друзів зараз теж почав активно використовувати gRPC у C# мікросервісах

готов спорить пожатый json обгонит protobuf
var buffer = new(bytes.Buffer)
var zw, err = gzip.NewWriterLevel(buffer, gzip.BestCompression)
require.NoError(t, err)

_, err = zw.Write(content)
require.NoError(t, err)

err = zw.Close()
require.NoError(t, err)

t.Log(buffer.Len(), len(content))
JSON: 1186 з 5488
Protobuf: 1038 з 3811

Итого можно было просто жать данные и ничего не переписывать.
* Если, конечно, нету других не упомянутых выигрышей в других моментах

а расходы CPU на сжатие ? Это ведь совсем не бесплатно, если сравнивать соот. операции.

Если CPU в основном простаивает, то пофиг. По ситуации надо смотреть. Жмут же некоторые базы, ибо IO обычно более узкий момент.

Если CPU в основном простаивает, то пофиг.

а про расходы cpu тс ничего и не пишет он пишет только якобы они упёрлись в трафик причём трафик между сервисами и бд ))

Если реализовать сжатие стоит условный час работы, а переписывание на новый сериализатор несколько недель, то рациональнее, наверное, сжимать. Тем более, что система упиралась в пропускную способность сети, а не процессор. Да и сжимать не обязательно на максимум, json очень хорошо сжимается и с меньшими коэффициентами. API же как-то сжимает данные при каждом запросе (а некоторые еще и принимать сжатые умеют) и это никого не смущает.

Если реализовать сжатие стоит условный час работы

Если только так, то да, gzip в потоке наше все. сам так делал.

НО давайте смотреть шире, мы не знаем всех требований к системе вообще. Помимо расходов CPU, ведь сжатие и задержку добавляет. а это в некоторых случаях уже серьезно, вдруг там и к задержке очень строгие требования ?

а переписывание на новый сериализатор несколько недель

Ну слушайте, Вы искренне верите что в 2020-м несколько недель надо ковырять сериализатор при наличии готовых и используемых в бою библиотек ?

Ну слушайте, Вы искренне верите что в 2020-м несколько недель надо ковырять сериализатор при наличии готовых и используемых в бою библиотек ?

Это уже зависит от сложности системы, так как изменение может влять на контракты между подсистемами. Может Ярослав ответит сколько времени заняла каждая задача, судя по комментарию ниже он решил обе.

Пару днів, основний час це написання тестів, що перевіряються зворотню сумісність

Итого можно было просто жать данные и ничего

Для внутрішньої задачі після того як перевели на Protobuf ще пробував зжимати, але там вже був малий виграш ~ 30-50 байт

func BenchmarkRepositoryJSONZip(b *testing.B) {
	var content = []byte(jsonRepositoryFixture)

	var result *bytes.Buffer

	for i := 0; i < b.N; i++ {
		var buffer = new(bytes.Buffer)
		buffer.Grow(1024)
		var zw, err = gzip.NewWriterLevel(buffer, gzip.BestCompression)
		require.NoError(b, err)

		_, err = zw.Write([]byte(content))
		require.NoError(b, err)

		err = zw.Close()
		require.NoError(b, err)

		result = buffer
	}

	b.Log(result.Len(), len(content))
}
291882 ns/op
818550 B/op
32 allocs/op

что и требовалось доказать селяви ))

Т.е. включение сжатия поврех json позволяет прокачать по той же сети до 5 раз больше данных. Плюс это снижает объем хранимых данных в kv storage в те же пять раз. IMHO, это намного лучше перехода от json на protobuf с его выигрышем на 30%.

ось тут вже писав:

Для внутрішньої задачі після того як перевели на Protobuf ще пробував зжимати, але там вже був малий виграш ~ 30-50 байт

~ 30-50 байт і 30% це абсолютна і відносна величина і для порівняння іх треба переводити в щось одне

Для внутрішніх даних оптимальним рішенням було саме мігрування з JSON на Protobuf, додаткове зжимання дало малий ефект і потребувало ще ресурсів CPU тому від нього відмовились

Вы все отвечаете JSON -> Protobuf -> compressed Protobuf а не JSON -> compressed JSON, ну или так оно парсится и не только у меня)

Тоді правильно так:
було: DataStruct -> DataJSONStruct -> bytes
стало: DataStruct -> DataProtobufStruct -> bytes -> compressed bytes attempt

В 5 раз... Это кое-что говорит о контенте... В реальности такого не должно быть что строки заполняют повтором одного символа

Наприклад колекція словників у яких назви полів повторюються, а значення цих полів короткі.

протобаф выиграет по и размеру, и по скорости, единственный способ обогнать — именно значения строк

Я пояснював як стискання JSON може зменшити обсяг даних в рази — там ще не лише значення, а також імена полів передаються.

в 5 раз не выйдет сжать все равно — попробуй, будет меньше

Взяв спробував один зі своїх тестових даних — на менше ніж в 1К стискання в 4.5 рази. Так що для великого обсягу JSON думаю і в 50 разів можна стиснути.

зележить від алгоритму. Швидкі, типу gzip, не зможуть, а сильні зможуть набагато більше ніж 50 раз.

В наших данных 12-13 MB сжимаются примерно до 1-2 MB.

Ну если жать, то можно и блокчейн прикрутить. А что, все равно АЭС простаивают.

все жмут настолько что тот же ж grpc ходит через http2 который делать встроенный сжатие заголовков так вообще обязательное ещё раз «задача» «поставлена» якобы упёрлись в пропускную способность сети

... а то что такие задачи нельзя ставить джаваскриптерами я уже и так написал ))

угу, включить gzip фильтр)

Як на мене, тут ще є різниця між тим, куди ці дані відправляються. Якщо вони пишуться в базу і читаються з неї тим самим сервісом, то насправді все одно, яка саме технологія використовується, бо нас буде цікавити лише трейд оф між CPU та об’ємом даних, який напряму впливає на використання мережі.

А от якщо мова йде про комунікацію між різними серверами, то все стає набагато цікавіше і тут вже можна говорити про grpc vs rest ітд ітп, оскільки це треба розглядати в комплексі з комунікацією між командами, які ці сервіси розробляють, обраними мовами програмування та фреймворками, можливостями інфраструктури, де ці сервери розгорнуті, стандартами щодо міжсервісної комунікації всередині компанії тощо

В принципі очікувано

Але цікавить, якщо було перевантажено мережу на бд через кількість трафіку, як ваш фронтенд взагалі себе почував цілий рік? :)

Трафік поступово збільшувався і це стало відчутно на піках

Характер росту трафіку дозволяє екстраполювати тренд? Через який час пропускну буде вичерпано з врахуваннями змін у форматі зберігання?

Характер росту трафіку дозволяє екстраполювати тренд?

Так

Через який час пропускну буде вичерпано з врахуваннями змін у форматі зберігання?

За цим слідкує DevOps і докупає сервери

Підписатись на коментарі