Any experiences and performance results compared to running the Phoenix server on a TCP port? Any minimum system requirements to use Unix socket?
I donāt think you can run Cowboy (which is the only server Phoenix officially works with AFAIK) on unix sockets.
EDIT: someone says you can since OTP 19.
As far as performance goes, itās not specific to Erlang and should be somewhat faster.
Whatās the state of this?
There was a PR for OTP. Itās marked as closed on Github, but the discussion seems to indicate that it got put into OTP-19.0-rc2.
I want to write a Docker plugin in Elixir, but it requires communicated with Docker over a Unix socket.
Thanks for the help.
With :gen_tcp
you can use the tuple {:local, path}
to connect to a unix socket. It is mentioned in the :inet
module docs:
http://erlang.org/doc/man/inet.html#type-local_address
If you want to make HTTP requests to a unix socket you can use HTTPoison/Hackney with the āhttp+unix://ā scheme, which is a bit of a hidden feature:
For posterity, this worksā¦
{:ok, pid} = Plug.Adapters.Cowboy.http(MyPlug, [], ip: {:local, "my_plug.sock"}, port: 0)
The port: 0
is important; it wonāt work without it.
Also, you gotta rm my_plug.sock
when youāre done otherwise it wonāt start up again: :eaddrinuse (address already in use)
Any suggestions on getting the Phoenix.Endpoint
to accept these options?
Iām getting an error in ranch_listener_sup
:
config :hello, HelloWeb.Endpoint,
http: [port: 0, ip: {:local, "hello.sock"}],
debug_errors: true,
code_reloader: false,
check_origin: false,
watchers: []
Error Details
** (Mix) Could not start application hello: Hello.Application.start(:normal, []) returned an error: shutdown: failed to start child: HelloWeb.Endpoint
** (EXIT) shutdown: failed to start child: Phoenix.Endpoint.Handler
** (EXIT) shutdown: failed to start child: {:ranch_listener_sup, HelloWeb.Endpoint.HTTP}
** (EXIT) an exception was raised:
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:error, :einval}. This protocol is implemented for: Atom, BitString, Date, DateTime, Float, Integer, List, NaiveDateTime, Time, URI, Version, Version.Requirement
(elixir) /private/tmp/elixir-20180316-64850-zsrybb/elixir-1.6.4/lib/elixir/lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) /private/tmp/elixir-20180316-64850-zsrybb/elixir-1.6.4/lib/elixir/lib/string/chars.ex:22: String.Chars.to_string/1
(phoenix) lib/phoenix/endpoint/cowboy_handler.ex:115: Phoenix.Endpoint.CowboyHandler.info/3
(phoenix) lib/phoenix/endpoint/cowboy_handler.ex:100: Phoenix.Endpoint.CowboyHandler.start_link/3
(stdlib) supervisor.erl:365: :supervisor.do_start_child/2
(stdlib) supervisor.erl:348: :supervisor.start_children/3
(stdlib) supervisor.erl:314: :supervisor.init_children/2
(stdlib) gen_server.erl:365: :gen_server.init_it/2
(stdlib) gen_server.erl:333: :gen_server.init_it/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
What would be the reason to do this? Are plain Unix sockets faster than using a TCP port (when putting Phoenix+Cowboy behind a webserver like Nginx or Apache?)
@Qqwy There is a noticeable speed boost from using sockets over tcp. From what I recall it is 10-15%, in addition to avoiding the overhead of TLS.
Iāve explored using a socket to connect to a legacy app running in a different languageālike terraformer, but on the same host and without the extra overhead.
AFAIK a Unix socket does not suffer from the TCP slow start. Especially for short lived connections this is a noticeable bottleneck.
So, is it possible to run Phoenix on a Unix socket?
In general it seems to be possible, as you can inject the socket after it has been established into cowboy, Iām not sure though how this fits into phoenix startup procedure.
How it will work in Erlang using cowboy is explained briefly in this issue:
Iām primarily interested in unix sockets as a way to implement zero downtime deploys by updating a symlink to the current
release and reloading nginx config. Never used Capistrano but I understand it worked similarly.
After trying to get this working Iāve run into a couple more issues. The error above just requires a small change to the startup message phoenix outputs which assumes a port number.
The next issue I hit is the Plug.Static plug. Eventually it calls into the :file.sendfile
kernel function, which fails with {:error, :badarg}
when given a unix socket. There is a work-around in :ranch_transport.sendfile
but Iām not sure if the best place to fix this is in erlang, ranch, cowboy or plug?
Or maybe you could serve static files from nginx since you are already using it anyway?
Turns out the new file system in OTP 21 fixed the issue, so Phoenix should now run over a unix socket.
Much of this depends on the version of kernel. It is true that since the beginning of time, communication over the loopback subnet went through the entire TCP stack, twice, āoutgoingā and āincomingā. But in some recentāish Linux kernel short-circuiting was added so that those comms now skip that overhead and work much more like domain sockets.
Ooooo I hope this gets documented, should switch my server over. ^.^
If you follow the trail from the Phoenix endpoint docs, you land at: https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
Which does mention how to set the ip
and port
appropriately for unix sockets.
Great, looking forward to test it.
Indeed, it is possible like this:
http: [ip: {:local, '/srv/phx/project/development.socket'}, port: 0],