FlutBuffers в 2024 году: сможем ли мы воссоздать старый успех? Оптимизация производительности занимает центральное место!

FlutBuffers в 2024 году: сможем ли мы воссоздать старый успех? Оптимизация производительности занимает центральное место!

22 января 2024 г.

Привет!

О чем эта статья?

В 2021 году я работал над проектом, где практически на всё не было лишних средств — не было ресурсов от AWS, а мобильные телефоны, для которых мы разрабатывали ПО, были очень простыми, выполнявшими только необходимые функции и предоставлявшими доступ в Интернет. . Иногда это были планшеты от нашего поставщика, которые тоже не были флагманскими устройствами и стабильно отставали.

В этой статье я расскажу, как мы существенно оптимизировали скорость наших сервисов, а позже в 2024 году напишем новые версии и протестируем их!

Приятного чтения!

Начнем.

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

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

А что, если наш случай:

  1. Слабый кластер AWS, которому необходимо вместить более 10 логических сервисов плюс мониторинг.
  2. Наши телефоны — это специальные Android-гаджеты с оперативной памятью не более 4 ГБ, чаще всего планшеты.
  3. Мы часто делаем снимки из приложения на серверную часть.
  4. Нам необходимо проверить часть данных, прежде чем продвигать их дальше по бизнес-потоку.
  5. КАК ЕСТЬ

    Итак, вот в чем дело — слабый бэкенд, слабые устройства, 1 MVP и 3 разработчика. Миссия — расширяться как можно меньше, не тратя лишних денег на AWS, пока мы находимся в зоне MVP.

    Если MVP справится с задачей, ресурсы потекут.

    В противном случае проект может быть приостановлен.

    Звучит как вызов?

    sorry, what?

    Мы засучили рукава и начали экспериментировать. Что есть на рынке и что мы делаем с услугами:

    1. REST (JSON)
    2. gRPC (прото, двоичный)
    3. «Специальный гость» (также в двоичном формате)
    4. Еще раз о спецификации: в качестве хорошего примера наш документ должен выглядеть так:

      {
        "docs": {
          "name": "name_for_documents",
          "department": {
            "code": "uuid_code",
            "time": 123123123,
            "employee": {
              "name": "Ivan",
              "surname": "Polich",
              "code": "uuidv4"
            }
          },
          "price": {
            "categoryA": "1.0",
            "categoryB": "2.0",
            "categoryC": "3.0"
          },
          "owner": {
            "uuid": "uuid",
            "secret": "dsfdwr32fd0fdspsod"
          },
          "data": {
            "transaction": {
              "type": "CODE",
              "uuid": "df23erd0sfods0fw",
              "pointCode": "01"
            }
          },
          "delivery": {
            "company": "TTC",
            "address": {
              "code": "01",
              "country": "uk",
              "street": "Main avenue",
              "apartment": "1A"
            }
          },
          "goods": [
            {
              "name": "toaster v12",
              "amount": 15,
              "code": "12312reds12313e1"
            }
          ]
        }
      }
      

      Например, у нас есть компактный сервис всего с двумя методами:

      1. Сохраните документы и подтвердите код отдела, компанию доставки и адрес.
      2. Найти все с разбивкой по страницам с ограничением/смещением.
      3. m?

        Этап 1. REST-сервис

        Ничего особенного, мы собираемся создать небольшой сервис с Gin gonic и http lib. Хороший пример: «Golang RESTful API»: ==нажмите здесь==

        Давайте напишем что-нибудь вот так. Полный код здесь: ==Github json==

        const (
         post = "/report"
         get  = "/reports"
         TTL  = 5
        )
        
        func main() {
         router := gin.Default()
         p := ginprometheus.NewPrometheus("gin")
         p.Use(router)
        
         sv := service.NewReportService()
         gw := middle.NewHttpGateway(*sv)
        
         router.POST(post, gw.Save)
         router.GET(get, gw.Find)
        
         srv := &http.Server{
          Addr:    "localhost:8080",
          Handler: router,
         }
        }
        

        Этот код представляет собой тест для функции BenchmarkCreateAndMarshal, измеряющий производительность операций создания и маршалинга.

        // BenchmarkCreateAndMarshal-10       168706       7045 ns/op
        func BenchmarkCreateAndMarshal(b *testing.B) {
         for i := 0; i < b.N; i++ {
          doc := createDoc()
          _ = doc.Docs.Name // for tests
        
          bt, err := json.Marshal(doc)
          if err != nil {
           log.Fatal("parse error")
          }
        
          parsedDoc := new(m.Document)
          if json.Unmarshal(bt, parsedDoc) != nil {
           log.Fatal("parse error")
          }
          _ = parsedDoc.Docs.Name
         }
        }
        
        • BenchmarkCreateAndMarshal-10: это строка вывода, предоставляемая инструментом тестирования Go.
        • 168706: это количество итераций, выполненных во время теста.
        • 7045 нс/оп: это среднее время, необходимое для одной итерации в наносекундах. Здесь ns/op означает наносекунды на операцию.

        Таким образом, результат показывает, что функция BenchmarkCreateAndMarshal выполняется примерно за 7045 наносекунд на операцию за 168706 итераций.

        Отсюда мы начали свой путь, а сейчас рассматриваем первую ключевую точку на нашем пути. Хватило ли этого для запуска? Ответ – да! Но как долго? Ответ — нет.

        Мы успешно завершили первую часть и запустили MVP на тестирование, в том числе с синтетическими нагрузками. Каков был результат? У нас закончилась память; при передаче пакета отчетов наше окружение покраснело от нагрузки, и мы работали, но не так быстро, как могли.

        Отсюда открывается новая ветка нашего исследования. Зачем добавлять память, если мы можем использовать некоторые процессы более эффективно? Да, мы говорим о сериализации, и начинается вторая глава, значительно ускоряющая нашу обработку.

        Are you ready?

        Этап 2. Переход на gRPC

        Иногда: не беспокойтесь, если вы новичок в gRPC! Делать это шаг за шагом — отличный подход. Я помню, что был в одной лодке: копирование и вставка документации — обычная практика при погружении в новые технологии. Это фантастический способ понять концепции и понять, как все работает. Продолжайте изучать руководство и не стесняйтесь обращаться к нему, если у вас возникнут вопросы. Приятного кодирования! 🚀 Подробнее здесь: https://protobuf.dev/overview/

        Итак, gRPC:

        gRPC обеспечивает более эффективную и компактную двоичную связь по сравнению с текстовой природой HTTP.

        * Тип: ориентирован на передачу двоичных данных и структурированных сообщений. * Протокол: поддерживает состояние и дуплексную связь. * Формат данных: Протокольные буферы (protobuf) — формат сериализации двоичных данных. * Транспорт: в качестве транспортного протокола используется HTTP/2.

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

        В простом мире REST мы используем инструменты Swagger или OAS3, такие как Apicurio и тому подобные — это экономит время и делает процесс более прозрачным, но все равно требует времени. И знаете что, давайте еще раз взглянем на Protobuf — он уже поставляется со схемой, и есть его версия (если мы храним ее в Git) — огромный плюс. Поделитесь им с командой, и теперь эта спецификация будет у всех.

        Хорошо, как это работает?

        Ну, простой пример, напишем файл example.proto:

        syntax = "proto3";
        
        message Person {
         required string name = 1;
         required int32 id = 2;
         optional string email = 3;
        }
        

        И сгенерируйте его:

        protoc - python_out=. example.proto
        

        При этом будет создан файл `example_pb2.py`, который содержит сгенерированный код для работы с данными, определенными в `example.proto`. Использование в Python:

        import example_pb2
        
        # Create a Person object
        person = example_pb2.Person()
        person.name = "John"
        person.id = 123
        person.email = "john@example.com"
        
        # Serialize to binary format
        serialized_data = person.SerializeToString()
        
        # Deserialize from binary format
        new_person = example_pb2.Person()
        new_person.ParseFromString(serialized_data)
        

        Здесь мы создаем объект Person, устанавливаем его поля, сериализуем его в двоичный формат, а затем десериализуем обратно. Примечание. `example_pb2` — это сгенерированный модуль, созданный компилятором protobuf. Буферы протокола обеспечивают формат двоичных данных, который является компактным и эффективным для передачи. Он также поддерживает различные языки программирования, что делает его удобным для использования в различных частях вашего технологического стека.

        Все еще не знаете, как это работает? Давайте воспользуемся предыдущим примером Person. Представьте, что у нас есть объект Person с заполненными полями:

        Person person = {
         name: "John Doe",
         id: 123,
         email: "john@example.com"
        };
        

        Когда этот объект сериализуется в двоичный формат, каждое поле будет представлено как тегированный элемент. В данном случае тегами являются числа 1, 2 и 3. После сериализации поток двоичных данных может выглядеть примерно так (в упрощенной форме):

        08 4A 6F 68 6E 20 44 6F 65 10 7B 1A 14 6A 6F 68 6E 40 65 78 61 6D 70 6C 65 2E 63 6F 6D
        

        Давайте разберемся:

        • 08 представляет тег 1 (поле name), за которым следует длина поля.
        • 4A 6F ​​68 6E 20 44 6F 65 представляет коды ASCII для строки «Джон Доу».
        • 10 представляет тег 2 (поле id), за которым следует значение 123 в кодировке переменной длины (Varint).
        • 1A представляет тег 3 (поле email), за которым следует длина строки 20 и коды ASCII для строки «john@example.com».

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

        • Эффективность: двоичный формат обеспечивает более компактное представление данных, уменьшая объем передаваемой информации по сети.
        • Скорость: операции сериализации и десериализации выполняются быстрее, поскольку двоичные данные могут обрабатываться эффективно.

        Пришло время создать наш прототип сервиса со спецификацией:

        syntax = "proto3";
        
        package docs;
        option go_package = "proto-docs-service/docs";
        
        service DocumentService {
            rpc GetAllByLimitAndOffset(GetAllByLimitAndOffsetRequest) returns (GetAllByLimitAndOffsetResponse) {}
            rpc Save(SaveRequest) returns (SaveResponse) {}
        }
        
        message GetAllByLimitAndOffsetRequest {
            int32 limit = 1;
            int32 offset = 2;
        }
        
        message GetAllByLimitAndOffsetResponse {
            repeated Document documents = 1;
        }
        
        message SaveRequest {
            Document document = 1;
        }
        
        message SaveResponse {
            string message = 1;
        }
        
        message Document {
          string name = 1;
          Department department = 2;
          Price price = 3;
          Owner owner = 4;
          Data data = 5;
          Delivery delivery = 6;
          repeated Goods goods = 7;
        }
        
        message Department {
          string code = 1;
          int64 time = 2;
          Employee employee = 3;
        }
        
        message Employee {
          string name = 1;
          string surname = 2;
          string code = 3;
        }
        
        message Price {
          string categoryA = 1;
          string categoryB = 2;
          string categoryC = 3;
        }
        
        message Owner {
          string uuid = 1;
          string secret = 2;
        }
        
        message Data {
          Transaction transaction = 1;
        }
        
        message Transaction {
          string type = 1;
          string uuid = 2;
          string pointCode = 3;
        }
        
        message Delivery {
          string company = 1;
          Address address = 2;
        }
        
        message Address {
          string code = 1;
          string country = 2;
          string street = 3;
          string apartment = 4;
        }
        
        message Goods {
          string name = 1;
          int32 amount = 2;
          string code = 3;
        }
        

        После этого нам нужно собрать его с помощью простого скрипта:

        # if it is your first downloading:
        brew install protobuf
        go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
        go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
        export PATH="$PATH:$(go env GOPATH)/bin"
        
        # only generator
        cd .. && cd grpc
        mkdir "docs"
        protoc --go_out=./docs --go_opt=paths=source_relative 
            --go-grpc_out=./docs --go-grpc_opt=paths=source_relative docs.proto
        

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

        our code!

        Как вы проводите локальное тестирование? Я предпочитаю использовать BloomRpc (к сожалению, он устарел :D; Postman может делать то же самое). На этот раз я пропущу детали реализации сервера и логику обработки документов. Однако еще раз напишем тест. Естественно, мы ожидаем беспрецедентного роста!

        // BenchmarkCreateAndMarshal-10       651063       1827 ns/op
        func BenchmarkCreateAndMarshal(b *testing.B) {
         for i := 0; i < b.N; i++ {
          doc := CreateDoc()
          _ = doc.GetName()
          r, e := proto.Marshal(&doc)
          if e != nil {
           log.Fatal("problem with marshal")
          }
        
          nd := new(docs.Document)
          if proto.Unmarshal(r, nd) != nil {
           log.Fatal("problem with unmarshal")
          }
          _ = nd.GetName()
         }
        }
        

        Этот код представляет собой тест под названием BenchmarkCreateAndMarshal, который измеряет производительность операций создания и маршалинга. Результаты показывают, что в среднем тест выполняет эти операции за 1827 наносекунд на итерацию из 651063 итераций. Итак, полный код здесь: ==нажмите, пожалуйста==

        Это может показаться успехом, и мы могли бы остановиться, но почему? Кажется, мы еще можем выжать из сервиса больше производительности и добиться лучших результатов, но как? И вот последняя глава истории...

        Yes, we managed!

        Этап 3. Переход на плоские буферы.

        А теперь давайте представим нашего гостя среди протоколов — большинство из вас, вероятно, даже не слышали о нем — это FlatBuffers.

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

        Person person;
        person.id = 123;
        person.name = "John Doe";
        person.age = 30;
        

        Конечно, давайте представим сериализованные байты в шестнадцатеричном формате для данной структуры Person:

        // Serialized bytes (hexadecimal representation)
        // (assuming little-endian byte order)
        1B 00 00 00    // Data size (including this byte)
        7B 00 00 00    // ID (123 in little-endian byte order)
        09 00 00 00    // Name string length (including null-terminator)
        4A 6F 68 6E    // Name ("John" in ASCII, including null-terminator)
        20 00 00 00    // Age (30 in little-endian byte order)
        

        В этом примере:

        * Первые 4 байта представляют размер данных, включая этот байт. В данном случае размер составляет 27 байт (0x1B). * Следующие 4 байта представляют собой id (123 в порядке байтов с прямым порядком байтов). * После этого 4 байта представляют длину строки имени (9 байт). * Последующие 9 байтов представляют строку имени «Джон Доу», включая нулевой терминатор. * Последние 4 байта представляют возраст (30 в порядке байтов с прямым порядком байтов).

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

        typing!

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

        Конечно, нам придется испачкать руки кодированием сериализации вручную, но потенциальная выгода того стоит. Это своего рода компромисс — больше усилий на начальном этапе, но контроль и потенциальное повышение производительности могут сделать это выгодной сделкой в ​​долгосрочной перспективе. В конце концов, иногда нужно глубоко погрузиться в код, чтобы творить чудеса, верно?

        Пример кода здесь: GitHub

        // BenchmarkCreateAndMarshalBuilderPool-10      1681384        711.2 ns/op
        func BenchmarkCreateAndMarshalBuilderPool(b *testing.B) {
         builderPool := builder.NewBuilderPool(100)
        
         for i := 0; i < b.N; i++ {
          currentBuilder := builderPool.Get()
        
          buf := BuildDocs(currentBuilder)
          doc := sample.GetRootAsDocument(buf, 0)
          _ = doc.Name()
        
          sb := doc.Table().Bytes
          cd := sample.GetRootAsDocument(sb, 0)
          _ = cd.Name()
        
          builderPool.Put(currentBuilder)
         }
        }
        

        Поскольку мы находимся в режиме «оптимизации своими руками», я решил собрать небольшой пул сборщиков, который очищаю после использования. Таким образом, мы можем перерабатывать их, не выделяя память снова и снова.

        Это немного похоже на набор инструментов, который мы убираем после каждого использования — он сохраняет порядок и эффективность. Зачем тратить ресурсы на создание новых строителей, если мы можем перепрофилировать уже имеющиеся, верно? Все дело в эффективности работы своими руками.

        const builderInitSize = 1024
        
        // Pool - pool with builders.
        type Pool struct {
         mu     sync.Mutex
         pool   chan *flatbuffers.Builder
         maxCap int
        }
        
        // NewBuilderPool - create new pool with max capacity (maxCap)
        func NewBuilderPool(maxCap int) *Pool {
         return &Pool{
          pool:   make(chan *flatbuffers.Builder, maxCap),
          maxCap: maxCap,
         }
        }
        
        // Get - return builder or create new if it is empty
        func (p *Pool) Get() *flatbuffers.Builder {
         p.mu.Lock()
         defer p.mu.Unlock()
        
         select {
         case builder := <-p.pool:
          return builder
         default:
          return flatbuffers.NewBuilder(builderInitSize)
         }
        }
        
        // Put return builder to the pool
        func (p *Pool) Put(builder *flatbuffers.Builder) {
         p.mu.Lock()
         defer p.mu.Unlock()
        
         builder.Reset()
        
         select {
         case p.pool <- builder:
          // return to the pool
         default:
          // ignore
         }
        }
        

        Пришло время проверить результаты

        Теперь давайте углубимся в результаты наших тестов, и вот что мы видим:

        | протокол | итерации | скорость | |----|----|----| | json | 168706 | 7045 нс/оп | | прото | 651063 | 1827 нс/оп | | квартира | 1681384 | 711,2 нс/оп |

        Ну-ну-ну — похоже, Флэт здесь является демоном скорости, оставляя остальных в пыли с коэффициентом T. Цифры не лгут, и похоже, что наша самодельная оптимизация окупается с лихвой!

        Что ж, тестирование — это хорошо, но давайте попробуем написать сервисы по этим протоколам и посмотрим, какие результаты мы получим!

        Технические требования:

        Язык: Golang, http framework: Gin gonic, база данных: mognodb

        ок, ок, ноТеперь пришло время применить наши протоколы к настоящему тестируем — раскрутим сервисы, подключим их к метрикам Prometheus, добавим подключения к MongoDB и вообще сделаем из них полноценные сервисы. Мы можем пока пропустить тесты, но это не приоритет.

        В классической настройке, как упоминалось ранее, у нас будет два метода — сохранить и найти по пределу и смещению. Мы реализуем их для всех трех реализаций и проведем стресс-тестирование всего процесса с помощью Яндекс Танк + Пандора.

        Для простоты графика я использую сервис Яндекса Overload и оставляю ссылки на наши тесты. Перейдем к делу!

        Метод сохранения, 1000 об/с, 60 сек, профиль:

        rps: { duration: 60s, type: const,  ops: 1000 }
        

        Результаты:

        | JSON | первый | второй | |----|----|----| | 99% | 1.630 | 1.260 | | 98% | 1.160 | 1.070 | | 95% | 1 | 0,920 |

        Ссылки: первый тест и секунду.

        | ПРОТО | первый | второй | |----|----|----| | 99% | 1.800 | 2.040 | | 98% | 1.380 | 1.540 | | 95% | 1.160 | 1,220 |

        Ссылки: первый тест и секунду.

        | КВАРТИРА | первый | второй | |----|----|----| | 99% | 3.220 | 3.010 | | 98% | 2.420 | 2.490 | | 95% | 1.850 | 1,840 |

        Ссылки: первый тест и секунда

        А теперь давайте добавим еще один метод, который охватывает тот самый случай, о котором я упоминал вначале, — нам нужно быстро извлечь поле из запроса и проверить его. Если есть какие-либо проблемы, мы отклоняем запрос; если все хорошо, продолжим.

        Метод проверки, 1000 об/с, 60 секунд, тот же профиль:

            rps: { duration: 60s, type: const,  ops: 1000 }
        

        | JSON | первый | второй | |----|----|----| | 99% | 1.810 | 1,980 | | 98% | 1.230 | 1.290 | | 95% | 0,970 | 1,070 |

        Ссылки: первый тест и секунду.

        | ПРОТО | первый | второй | |----|----|----| | 99% | 1.060 | 1.010 | | 98% | 0,700 | 0,660 | | 95% | 0,550 | 0,530 |

        Ссылки: первый тест и секунду.

        | КВАРТИРА | первый | второй | |----|----|----| | 99% | 2.920 | 3.010 | | 98% | 2.170 | 2.490 | | 95% | 1.540 | 1.510 |

        Ссылки: первый тест и секунда

        Заключение

        Эксперименты проводились с целью усовершенствовать наше приложение на несколько шагов — именно это вдохновило на создание этой статьи. Мы перешли с JSON на Proto2, затем на Proto3, но настоящий прирост производительности произошел только с FlatBuffer. Перенесемся на два года вперед: разработчики и сообщество значительно улучшили Protobuf. Теперь в нашем стеке мы используем высокопроизводительный язык Go вместо Kotlin с сопрограммами и Spring Boot. В моем проекте, запущенном в 2021 году, мы получили беспрецедентный прирост производительности, и вся эта история органично интегрировалась с нашей логикой и процессами.

        Итак, если вы когда-нибудь окажетесь в ситуации, когда быстрая сериализация имеет решающее значение, рассмотрите FlatBuffer.


        ???

        Результаты весьма неоднозначные. Сериализация с помощью FlatBuffer, как и ожидалось, происходит быстрее, чем простой JSON. Случай с валидацией тоже удивил — мы ожидали, что FlatBuffer победит, но gRPC вышел на первое место. Давайте углубимся в то, почему мы получили такие результаты. Но почему в нагрузочных тестах победил другой протокол?

        В качестве примеров я написал пару достаточно простых сервисов, которые только обрабатывают входящие сообщения или «проверяют» их. Как мы можем заменить, теперь победителями являются gRPC и протокол Protocol Buffer. Кстати, считайте этот эксперимент базовым, и если у вас возникнет вопрос об ускорении сериализации и передачи сообщений по цепочке, стоит протестировать его на своем процессе. Не забывайте, что это тоже важно. принять во внимание язык программирования и стек, который вы используете. И еще раз хочу подчеркнуть важность проведения MVP проекта, если вы все же хотите перейти на другие протоколы сериализации.

        Если говорить о сериализации:

        JSON: В стресс-тестах метода save с нагрузкой 1000 запросов в секунду JSON демонстрирует стабильные результаты, время выполнения примерно 7045 наносекунд на одну операцию.

        Protobuf: Protobuf демонстрирует высокую эффективность, превосходя JSON, со временем выполнения около 1827 наносекунд на операцию в том же тесте.

        FlatBuffers: FlatBuffers выделяется среди других, демонстрируя значительно меньшее время выполнения — около 711,2 наносекунд на операцию в том же стресс-тесте.

        Эти результаты подчеркивают, что FlatBuffers обеспечивает значительное преимущество в производительности по сравнению с JSON и Protobuf. Несмотря на то, что требуется более сложное обучение и использование, его реальная эффективность подчеркивает, что инвестиции в оптимизацию производительности могут окупиться в долгосрочной перспективе.

        Итак, подведем итоги:

        Судя по тестам, JSON все равно работает быстрее остальных — цифры не врут, правда? Действительно, мы создали сервис, который сохраняет все в базу данных. Но что, если помимо базы данных нам понадобится еще какое-то сетевое соединение? У gRPC больше преимуществ, поскольку он работает по протоколу HTTP/2.

        1. Если вам нужно быстро сериализовать данные, используйте FlatBuffers.
        2. Если у вас много сервисов и вам необходимо передавать запросы между ними, используйте gRPC — ничто не сравнится с его скоростью.
        3. Если вам просто нужен переводчик JSON с телефона в базу данных, выберите REST + JSON.
        4. Если вам нужно сэкономить память на устройстве и вы можете немного подождать обработки на сервере, используйте FlatBuffers.
        5. Глядя на наши показатели, мы говорим о 1–3 мс — только подумайте, насколько это быстро!

          Я надеюсь, что это было полезно для вас. Спасибо! 🙏

          !

          Как хороший шанс поделиться своими статьями о нагрузочном тестировании:

          https://hackernoon.com /turbocharge-load-testing-yandextank-ghz-combo-for-lightning-fast-code-checks?embedable=true

          https://hackernoon.com/leveraging-yandex -pandora-stress-testing-grpc-and-flatbuffer-services?embedable=true


          Также опубликовано здесь.


          Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE