0 1.2K ru

Gateway Aggregation паттерн

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

Контекст и проблема

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

На диаграмме ниже клиент отправляет запросы каждому сервису (1,2,3). Каждый сервис обрабатывает запрос и отправляет ответ обратно в приложение (4,5,6). В сотовой сети с обычно высоким пингом использование отдельных запросов таким образом неэффективно и может привести к нарушению соединения или обрывам запросов. Хотя каждый запрос может выполняться параллельно, приложение должно отправлять, ждать и обрабатывать данные для каждого запроса, все через отдельные соединения, что увеличивает вероятность сбоя.

gateway aggregation

Решение

Используйте Gateway, чтобы уменьшить количество запросов между клиентом и сервером.

Этот шаблон может уменьшить количество запросов, которые приложение отправляет к back-end сервисам, и повысить производительность приложения в high-latency сетях.

На следующей диаграмме приложение отправляет запрос шлюзу (1). Запрос содержит пакет дополнительных запросов. Шлюз декомпозирует их и обрабатывает каждый запрос, отправляя его соответствующему сервису(2). Каждый сервис возвращает ответ в gateway(3). Gateway объединяет ответы от каждого сервиса и отпавляет ответ приложению (4). Приложение делает один запрос и получает только один ответ от шлюза.

gateway aggregation

Проблемы и вопросы при реализации

  • Gateway не должен создавать coupling между back-end сервисами.
  • Gateway должен располагаться рядом с back-end сервисами, чтобы максимально уменьшить задержку.
  • Сервис шлюза может создать Single point of failure. Убедитесь, что gateway спроектирован правильно и соответствует требованиям доступности вашего приложения.
  • Gateway может стать узким местом. Убедитесь, что gateway имеет достаточную производительность для обработки нагрузки и может быть масштабирован в соответствии с ожидаемым ростом.
  • Выполните нагрузочное тестирование шлюза, чтобы исключить каскадные сбои сервисов.
  • Реализуйте resilient design, используя такие методы, как: bulkheads, circuit breaking, retry, и timeouts .
  • Если один или несколько сервисных вызовов занимают слишком много времени, может быть допустимо установить тайм-аут и вернуть частичный набор данных. Подумайте, как ваше приложение будет обрабатывать этот сценарий.
  • Используйте асинхронный I/O, чтобы гарантировать, что задержка на сервере не вызовет проблем с производительностью в приложении.
  • Реализуйте распределенную трассировку, используя correlation ID для отслеживания каждого отдельного реквеста.
  • Отслеживайте показатели запросов и размеры ответов.
  • Рассмотрите возможность возврата кэшированных данных в качестве стратегии отработки отказа для обработки сбоев.
  • Вместо того чтобы встраивать агрегацию в gateway, рассмотрите возможность размещения сервиса агрегации за шлюзом. Агрегация запросов, вероятно, будет иметь другие требования к ресурсам, чем другие сервисы в шлюзе, и может повлиять на функции маршрутизации и разгрузки шлюза.

Используйте этот шаблон, когда:

  • Клиент должен взаимодействовать с несколькими внутренними сервисами для выполнения операции.
  • Клиент может использовать сети со значительной задержкой, такие как сотовые сети.

Не используйте этот шаблон, когда:

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

Пример агрегации шлюзов NGINX с помощью Lua

В следующем примере показано, как создать простой сервис агрегации шлюзов NGINX с помощью Lua.

worker_processes  4;

events {
  worker_connections 1024;
}

http {
  server {
    listen 80;

    location = /batch {
      content_by_lua '
        ngx.req.read_body()

        -- read json body content
        local cjson = require "cjson"
        local batch = cjson.decode(ngx.req.get_body_data())["batch"]

        -- create capture_multi table
        local requests = {}
        for i, item in ipairs(batch) do
          table.insert(requests, {item.relative_url, { method = ngx.HTTP_GET}})
        end

        -- execute batch requests in parallel
        local results = {}
        local resps = { ngx.location.capture_multi(requests) }
        for i, res in ipairs(resps) do
          table.insert(results, {status = res.status, body = cjson.decode(res.body), header = res.header})
        end

        ngx.say(cjson.encode({results = results}))
      ';
    }

    location = /service1 {
      default_type application/json;
      echo '{"attr1":"val1"}';
    }

    location = /service2 {
      default_type application/json;
      echo '{"attr2":"val2"}';
    }
  }
}

Comments:

Please log in to be able add comments.