Минимальный 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.

    APD: Оригинал данного поста доступен тут.
    ua-hosting.company 81,73
    Хостинг-провайдер
    Поделиться публикацией
    Комментарии 5
    • +2
      Эээ, а ничего, что это (посредственный) перевод статьи совсем другого человека?
      • +1
        Приветствую.

        Это перевод поста написанного мистером Emil Soman. Я действительно забыл дать ссылку на первоисточник, увы, но все мы люди. Спасибо, что деликатно про это напомнили.
        • +4
          «шестнадцатиричные зависимости» убили намертво. вы с Elixir знакомы вообще хоть немного?
          • +3
            hex and application dependencies

            Да тут машинный перевод.
            • +3
              Да и в фразе «Ranch способен обработать многократные соединения TCP за один раз, создавая процессы.» мне кажется, имеется ввиду «множество».

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

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

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое