📜 ⬆️ ⬇️

Minimal HTTP Endpoint API using Elixir

Let's look at creating a minimal HTTP Endpoint API using Elixir. Just like Rack in Ruby, Elixir comes with a 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 :cowboy and :plug in the form of hexadecimal and applied dependencies.

 #     <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 


A plug comes with a router, which we can use to easily create an Endpoint HTTP. Let's create a module to encapsulate a 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 have worked with frameworks like sinatra, all of this will seem familiar to you. You can study the documentation on the router if you are curious to find out how it all works.

To start the server, it is necessary for the supervisor of this application to launch 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 command launches the Elixir interactive shell, as well as your application on the Erlang VM. Now we can move on to the more fun part.

Process Usage Visualization: observer


At the iex prompt, run the Erlang :observer tool using this command:

 iex> :observer.start 

The command opens a GUI interface that looks like this:



On the left side of the Applications panel, you see a list of all 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 world of Erlang. And it uses the Ranch - Erlang library to handle TCP connections.
When we launch the Plug router, we switch to the router module for the Plug's Cowboy adapter. When receiving the connection, the Cowboy sends it to the Plug, and the latter, in turn, processes the connection and sends the request back.

Parallel requests


Plug by default asks Cowboy to launch 100 TCP Ranch host connections. You can see 100 host processes for yourself if you see a graph of using the application 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 to the Plug's Cowboy adapter:

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


Let's see how the processes look now:



OK, so we only have 2 host TCP connection processes. Let's try to execute 5 long 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 do 5 queries by 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 see the process graph:



We see that there are still only 2 receiving processes, and 5 others were spawned somewhere else. These are connection processes that contain accepted connections. What does it mean - receiving processes do not dictate how many processes we can take at one time. No, they simply limit new processes that can be taken at once. Even if you want to serve 1000 parallel requests, it is safe to leave the number of receiving processes in 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 once, creating processes.
Erlang: observer is a great way to visualize concurrency in your applications.
The recipient processes only accepted connections. Of these, you need only 100.

APD: The original of this post is available here .

Source: https://habr.com/ru/post/275563/


All Articles