class: middle #.main[Let's build a server] --- class: middle ## Why **Elixir**? --- class: middle ## Why **Elixir**? ### Processes --- class: middle ## Why **Elixir**? ### Processes ### **Processes** ### PROCESSES ### **PROCESSES!!!** --- class: middle # **Hi** ## *name -* Peter Saxton ## *@internets -* CrowdHailer --- class: middle #.main[Questions?] p.s. Talks not over yet. --- class: middle 1. Elixir what? 2. Just starting out. 3. Happy with the syntax and immutable data. 4. Know all about of spawning and linking processes --- class: middle # Process fundamentals - `spawn` - `send` - `receive` --- class: middle ```elixir main = self() IO.puts("main process: '#{inspect(main)}'") spawn(fn() -> IO.puts("worker process: '#{inspect(self())}'") Process.sleep(1_000) IO.puts("Done") send(main, :done) end) receive do :done -> IO.puts("Exiting") end ``` --- class: middle # Our first server ```elixir opts = [:binary, {:reuseaddr, true}, {:active, false}] {:ok, socket} = :gen_tcp.listen(8080, opts) {:ok, connection} = :gen_tcp.accept(socket) {:ok, message} = :gen_tcp.recv(connection, 0) :ok = :gen_tcp.send(connection, message) ``` --- class: middle ```elixir defmodule DemoServer do def listen(port) do opts = [:binary, {:reuseaddr, true}, {:active, false}] :gen_tcp.listen(8080, opts) end def echo_client(socket) do {:ok, connection} = :gen_tcp.accept(socket) {:ok, message} = :gen_tcp.recv(connection, 0) :ok = :gen_tcp.send(connection, message) end end ``` --- class: middle ## Blocking operations ```elixir {:ok, socket} = DemoServer.listen(8080) {:ok, message} = DemoServer.echo_client(socket) IO.puts(message) ``` ??? *[https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html](https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html)* --- class: middle ## Preemptive scheduling ```elixir {:ok, socket} = DemoServer.listen(8080) spawn(fn() -> DemoServer.echo_client(socket) end) endless_stream = Stream.cycle([1, 2, 3, 4, 5]) for value <- endless_stream do IO.puts(value) end ``` ??? *[https://hamidreza-s.github.io/erlang/scheduling/real-time/preemptive/migration/2016/02/09/erlang-scheduler-details.html](https://hamidreza-s.github.io/erlang/scheduling/real-time/preemptive/migration/2016/02/09/erlang-scheduler-details.html)* --- class: middle ## Lightweight processes ```elixir {:ok, socket} = DemoServer.listen(8080) for i <- 1..100_000 do spawn(fn() -> DemoServer.echo_client(socket) end) end for max <- 1..100_000 do spawn(fn() -> for j <- Stream.cycle(1..max) do IO.puts(j) end end) end ``` ??? elixir --erl "+P 1000000" v3.ex --- class: middle ## Fault tolerance ```elixir {:ok, socket} = DemoServer.listen(8080) for i <- 1..10 do spawn(fn() -> if rem(i, 2) == 0 do 1 / 0 end DemoServer.echo_client(socket) end) end ``` --- class: middle # Error Handling - Propagation - Detection - Handling *[http://crypt.codemancers.com/posts/2016-01-24-understanding-exit-signals-in-erlang-slash-elixir/](http://crypt.codemancers.com/posts/2016-01-24-understanding-exit-signals-in-erlang-slash-elixir/)* --- class: middle ## Fault Propagation ```elixir {:ok, socket} = DemoServer.listen(8080) for _ <- 1..10 do spawn_link(fn() -> DemoServer.echo_client(socket) end) end ``` --- class: middle ## Fault Handling ```elixir {:ok, socket} = DemoServer.listen(8080) for _ <- 1..10 do spawn_link(fn() -> DemoServer.echo_client(socket) end) end Process.flag(:trap_exit, true) receive do message -> IO.inspect(message) end ``` --- class: middle #.main[OK?] --- class: middle # What is **OTP**? --- class: middle # Walking tour of **Ace**. *[http://joearms.github.io/2016/03/13/Managing-two-million-webservers.html](http://joearms.github.io/2016/03/13/Managing-two-million-webservers.html)* --- class: middle, center ![](ace_waiting.png) --- class: middle, center ![](ace_step_1.png) --- class: middle, center ![](ace_step_2.png) --- class: middle ```elixir # /server.ex def init(state) do {:ok, {:initialized, state}} end def handle_call({:accept, s}, from = {pid, _}, {:initialized, app}) do connection_ref = make_ref() GenServer.reply(from, {:ok, connection_ref}) {:ok, connection} = Connection.accept(s) connection_info = Connection.information(connection) send(pid, ack(connection_ref, connection_info)) handle_connect(app, connection_info) end ``` --- class: middle ```elixir # /governer.ex def init({socket, server_sup}) do {:ok, server} = Supervisor.start_child(server_sup, []) true = Process.link(server) {:ok, ref} = Server.accept_connection(server, socket) {:ok, {socket, server_sup, ref, server}} end def handle_info(ack(ref, _), {socket, server_sup, ref, server}) do true = Process.unlink(server) {:ok, new_server} = Supervisor.start_child(server_sup, []) true = Process.link(new_server) {:ok, new_ref} = Server.accept_connection(new_server, socket) {:noreply, {socket, server_sup, new_ref, new_server}} end ``` --- class: middle ```elixir def drain(governor_supervisor) do Supervisor.which_children(governor_supervisor) |> Enum.map(fn({_i, pid, :worker, _}) -> ref = Process.monitor(pid) true = Process.exit(pid, :shutdown) ref end) |> Enum.map(fn(ref) -> receive do {:DOWN, ^ref, :process, _pid, _reason} -> :ok end end) :ok end ``` --- class: middle # Thank you *See the code* - **[github.com/crowdhailer/ace](https://github.com/crowdhailer/ace)**
Elixir servers for TCP and TLS(ssl) endpoints. *Also* - **[hexdocs.pm/raxx](https://hexdocs.pm/raxx)**
A pure webserver interface for Elixir. - **[hexdocs.pm/tokumei](https://hexdocs.pm/tokumei)**
Tiny yet MIGHTY Elixir webframework.