Минимальный HTTP API Endpoint используя Elixir

от автора

Давайте рассмотрим создание минимального HTTP API Endpoint используя Elixir. Так же, как и Rack в Ruby, Elixir идет в комплекте с Plug. Это универсальный инструмент для работы с HTTP соединениями.

Использование Plug: строим Endpoint HTTP

Во-первых, давайте создадим новый проект Elixir:

$ mix new http_api --sup 

Новое Elixir OTP приложение создано. Теперь нужно добавить :cowboy и :plug в виде шестнадцатиричных и примененных зависимостей.

# Измените следующие части в <code>mix.exs</code>    def application do     [applications: [:logger, :cowboy, :plug],      mod: {HttpApi, []}]   end    defp deps do     [       {:cowboy, "~>1.0.4"},       {:plug, "~>1.1.0"}     ]   end 

Plug комплектуется маршрутизатором, который мы можем использовать для простого создания Endpoint HTTP. Давайте создадим модуль, чтобы инкапсулировать маршрутизатор:

# lib/http_api/router.ex defmodule HttpApi.Router do   use Plug.Router    plug :match   plug :dispatch    get "/" do     send_resp(conn, 200, "Hello Plug!")   end    match _ do     send_resp(conn, 404, "Nothing here")   end end 

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

Для запуска сервера нужно, чтобы супервизор данного приложения запустил Plug Cowboy адаптер

# lib/http_api.ex  defmodule HttpApi do   use Application    def start(_type, _args) do     import Supervisor.Spec, warn: false      children = [       # `start_server` function is used to spawn the worker process       worker(__MODULE__, [], function: :start_server)     ]     opts = [strategy: :one_for_one, name: HttpApi.Supervisor]     Supervisor.start_link(children, opts)   end    # Start Cowboy server and use our router   def start_server do     { :ok, _ } = Plug.Adapters.Cowboy.http HttpApi.Router, []   end end 

Полный код для приведенного выше примера можно найти здесь. Вы можете запустить сервер с помощью:

$ iex -S mix 

Команда запускает интерактивную оболочку Elixir, а также и Ваше приложение на Erlang VM. Теперь можем перейти к более веселой части.

Визуализация использования процессов: observer

В подсказке iex запустите Erlang :observer инструмент, используя эту команду:

iex> :observer.start 

Команда открывает GUI интерфейс, который выглядит примерно так:

На левой стороне панели Applications Вы видите список всех приложений, в настоящее время работающих на Erlang VM — это включает наше приложение (http_api) и все его зависимости. Важными для нас являются Cowboy и Ranch.

Cowboy и Ranch

Cowboy — популярный HTTP сервер в мире Erlang. И он использует Ranch — библиотеку Erlang для обработки TCP соединений.
Когда мы запускаем маршрутизатор Plug, то переходим на модуль маршрутизатора для Plug’s Cowboy адаптера. Получая соединение, Cowboy передает его к Plug, а тот, в свою очередь, обрабатывает соединение и посылает запрос обратно.

Параллельные запросы

Plug по умолчанию просит, чтобы Cowboy запустил 100 TCP принимающих соединений Ranch. Вы можете видеть 100 принимающих процессов для себя, если видите график использования приложения в Ranch используя :observer.

Означает ли это, что может быть только 100 параллельных соединений? Давайте узнаем. Мы изменим число получателей к 2, передавая его в качестве параметра для адаптера Plug’s Cowboy:

Plug.Adapters.Cowboy.http HttpApi.Router, [], [acceptors: 2] 

Давайте посмотрим, как процессы выглядят теперь:

Хорошо, таким образом у нас есть только 2 принимающих процесса соединения TCP. Давайте попытаемся выполнить 5 длительных параллельных запросов и посмотреть, что из этого получится.

# lib/http_api/router.ex  # Modify router to add some sleep defmodule HttpApi.Router do   use Plug.Router    plug :match   plug :dispatch    # Sleep for 100 seconds before sending the reponse   get "/" do     :timer.sleep(100000)     send_resp(conn, 200, "Hello Plug!")   end    match _ do     send_resp(conn, 404, "Nothing here")   end end 

Теперь давайте выполним 5 запросов, делая это в подсказке iex:

for n <- 1..5, do: spawn(fn -> :httpc.request('http://localhost:4000') end) 

Запустите :observer от использования iex :observer.start и посмотрите график процесса:

Мы видим, что все еще есть только 2 принимающих процесса, а 5 других были порождены где-то в другом месте. Это процессы соединения, которые содержат принятые соединения. Что значит — принимающие процессы не диктуют, сколько процессов мы можем принимать за один раз. Нет, они просто ограничивают новые процессов, которые могут быть приняты за один раз. Даже если Вы хотите обслуживать 1000 параллельных запросов, безопасно оставить число принимающих процессов в значении по умолчанию 100.

Итог

Вы можете создать простые конечные точки HTTP, используя Plug маршрутизатор.
Ranch способен обработать многократные соединения TCP за один раз, создавая процессы.
Erlang :observer — отличный способ визуализировать параллелизм в Ваших приложениях.
Получатель обрабатывает только принятые соединения. Из них Вам нужны лишь 100.

ссылка на оригинал статьи http://habrahabr.ru/post/275563/