[recovery mode] Добавляем очень быстрый JSON API к нашему приложению

Все наши микросервисы, вне зависимости от того, как они общаются друг с другом, предоставляют своего рода heartbeat интерфейсы, чтобы система мониторинга могла в любой момент узнать, как там дела; типа общего состояния здоровья и каких-то специфичных частностей, скажем, контрольных сумм для внутренних данных, с которыми они имеют дело. Речь не про основной транспорт: тут прекрасно справляются RabbitMQ и Redis.

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

Поэтому вместо того, чтобы заново изобретать велосипеды с каждым новым микросервисом, я решил создать подключаемую библиотеку, которая могла бы решать эту бесхитростную проблему по предоставлению произвольных данных из любого приложения с нулевым кодом (если не считать трех строчек в config.exs). Будь то простой heartbeat (HTTP 200 OK), или длинный список актуальных курсов валют.

Решение основано на этом твите Дэйва Томаса.


JSON API сервер в весе пера

Camarero — это готовое к использованию решение для добавления некоторых функций JSON API в существующее приложение или даже для реализации не слишком запутанного JSON API с нуля, когда более сложные (читай: тяжелые) решения нежелательны. На картинке ниже показано, как в типичном случае мы можем его подключить и использовать.

Camarero Ties

Библиотека была создана ни в коем случае не для того, чтобы стать заменой полновесным решениям, типа Финикса. Ни в коем случае, нет. Это именно такой drop-in, когда микросервису всего-то и нужно выставить наружу пару-тройку API HTTP интерфейсов. Camarero, в некоторых случаях, может оказаться хорошим кандидатом на замену Redis, или любого другого хранилища ключевых значений (тоже в своей весовой группе). Основное отличие от подобного типа решений для веба — эта библиотека действительно быстра.

Вот времена отклика HTTP для возврата значения по ключу из хещ-таблицы с миллионом ключей.

1M key-value storage lookup: 10μs±

Да, тут нет никакого подвоха. Время HTTP отклика на запрос по kv-хранилищу с миллионом значений — несколько десяткой микросекунд в худшем случае.


Детали реализации

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

config :camarero,
  carta: [Camarero.Carta.Heartbeat],
  root: "api/v1"

Тут все и так, наверное, понятно: /api/v1 — корень веб-сервера, один маршрут heartbeat (настраивается изнутри модуля, по умолчанию — имя без префикса) — с обработчиком Camarero.Carta.Heartbeat. Обработчики также могут быть динамически добавлены в рантайме с помощью вызовов Camarero.Catering.route!.


Обработчики

Обработчик — это модуль, реализующий Camarero.Plato behaviour. Он состоит из стандартных CRUD методов манипулирования хранилищем. Для использования в качестве обработчика для входящих HTTP-запросов — годится любой модуль, реализующий этот behaviour.

Есть еще более тонкая настройка: behaviour Camarero.Tapas, которое заведует CRUD«ом внутри каждого контейнера Camarero.Plato, для пары ключ/значение. Обычно так глубоко копать при использовании библиотеки не нужно.

Реализация по умолчанию использует %{} map в качестве контейнера, и выглядит довольно компактно:

defmodule Camarero.Carta.Heartbeat do
  use Camarero.Plato
end

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


Тонкая настройка

Все без исключения методы из обеих реализаций по умолчанию (Camarero.Tapas и Camarero.Plato) — легко переопределяются. Например, чтобы использовать пользовательский маршрут для модуля, а также пользовательский контейнер, можно сделать следующее:

defmodule Camarero.Carta.Heartbeat do
  use Camarero.Plato, container: %MyStructWithAccessBehaviour{}

  @impl true
  def plato_route(), do: "internal/heartbeat"
end


Конфигурация веб-сервера

Camarero для работы нужен сервер Cowboy2 и CowboyPlug. Вот типичная настройка Cowboy2, в config.exs:

config :camarero,
  cowboy: [port: 4043, scheme: :https, options: []]


На что Camarero не претендует

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

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

Зато она по всем бенчмаркам быстрее любых аналогов.

Удачных быстрых ответов!

© Habrahabr.ru