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:
New Elixir OTP application created. Now you need to add
Plug comes with a router that we can use to easily create Endpoint HTTP. Let's create a module to encapsulate the router:
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
The full code for the above example can be found here . You can start the server with:
The team launches the Elixir interactive shell, as well as your Erlang VM application. Now we can move on to the fun part.
In the iex tooltip, launch the Erlang
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 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.
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:
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.
Now let's execute 5 queries, doing this at the iex hint:
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.
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 .
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
as 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
:observer
tool 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 .