Minimum HTTP Endpoint API using Elixir

    Let's look at creating a minimal HTTP Endpoint API using Elixir. Like Rack in Ruby, Elixir comes bundled with Plug. This is a universal tool for working with HTTP connections.



    Using Plug: building Endpoint HTTP

    First, let's create a new Elixir project:

    $ mix new http_api --sup
    


    New Elixir OTP application created. Now you need to add :cowboyand :plugas hexadecimal and applied dependencies.

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


    Plug comes with a router that we can use to easily create Endpoint HTTP. Let's create a module to encapsulate the router:

    # 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
    


    If you worked with frameworks like sinatra, all of this will seem familiar to you. You can study the documentation for the router if you are curious to find out how it all works.

    To start the server, the supervisor of this application must run the Plug Cowboy adapter

    # 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
    


    The full code for the above example can be found here . You can start the server with:

    $ iex -S mix
    


    The team launches the Elixir interactive shell, as well as your Erlang VM application. Now we can move on to the fun part.

    Process Usage Visualization: observer


    In the iex tooltip, launch the Erlang :observertool using this command:

    iex> :observer.start
    

    The command opens a GUI interface that looks something like this:



    On the left side of the Applications panel, you see a list of all the applications currently running on Erlang VM - this includes our application (http_api) and all its dependencies. Important for us are Cowboy and Ranch.

    Cowboy and ranch


    Cowboy is a popular HTTP server in the Erlang world. And he uses the Ranch - Erlang library to handle TCP connections.
    When we launch the Plug router, we go to the router module for the Plug's Cowboy adapter. Upon receiving the connection, Cowboy passes it to the Plug, which in turn processes the connection and sends the request back.

    Concurrent queries


    Plug by default asks Cowboy to start 100 TCP host Ranch connections. You can see 100 host processes for yourself if you see a graph of application usage in Ranch using: observer.



    Does this mean that there can only be 100 parallel connections? Let's find out. We will change the number of recipients to 2, passing it as a parameter for the Plug's Cowboy adapter:

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


    Let's see how the processes look now:



    Okay, so we only have 2 host TCP connection processes. Let's try to execute 5 lengthy parallel queries and see what happens.

    # 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
    


    Now let's execute 5 queries, doing this at the iex hint:

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


    Run: observer from using iex: observer.start and look at the process graph:



    We see that there are still only 2 host processes, and 5 others were spawned somewhere else. These are connection processes that contain received connections. What does it mean - host processes do not dictate how many processes we can take at one time. No, they just limit the new processes that can be taken at a time. Even if you want to serve 1000 concurrent requests, it is safe to leave the number of receiving processes at the default value of 100.

    Total


    You can create simple HTTP endpoints using the Plug Router.
    Ranch is able to handle multiple TCP connections at a time, creating processes.
    Erlang: observer is a great way to visualize concurrency in your applications.
    The receiver processes only received connections. Of these, you only need 100.

    APD: The original of this post is available here .

    Also popular now: