class: middle # Message passing for **actors** and **humans** --- class: middle ## **Hi** ### *name -* Peter Saxton ### *@internets -* CrowdHailer ### *@works -* [paywithcurl.com](paywithcurl.com) --- class: middle ![](/images/pebble-1.jpg) --- class: middle ### lets talk about, ## The **Actor** model --- class: middle ## Actors - **Universal** primitive of concurrent computation - Communicate via **asynchronous** message passing - React to messages by making **local** decisions *First proposed by Carl Hewitt in 1973* --- class: middle ![](/images/actor-model.png) --- class: middle ### **Universal** primitive - An actor has three essential elements: 1. Processing 2. Storage 3. Communication - **Everything** is an actor ??? https://gist.github.com/rbishop/9082539 --- class: middle ### **Asynchronous** message passing - No delivery guarantees - No order guarantees *Similar to original Object Oriented programming (OOP)* --- class: middle ### **Local** decisions - No shared/global state - In response to a message, an actor may: 1. Create more actors 2. Send messages to other Actors that it has addresses for 3. Designate how the Actor is going to handle the next message it receives ??? The Actor gets to decide what it wants to do with incoming messages. Example: A chicken Actor doesn’t have to agree to cut off its own head. https://gist.github.com/rbishop/9082539 --- class: middle ## In the **wild** - Elixir - erlang - Akka (JVM) --- class: middle ### Let's build our own ## in **JavaScript** --- class: middle ### The simplest **actor** ```js var state = 0 while (true) { const message = await mailbox.receive() state = state + 1 } ``` ```js // somewhere else mailbox.deliver(message) ``` ??? You might have a messge to stop things but we won't worry about that here block on receive it't the only input for an actor --- class: middle ### Blocking mailbox ```js function Mailbox () { var messages = [], awaiting = undefined function receive () { return new Promise(function (resolve) { if (next = messages.shift()) { resolve(next) } else { awaiting = resolve } }) } async function deliver (message) { messages.push(message) if (awaiting) { awaiting(messages.shift()) awaiting = undefined } } return {receive: receive, deliver: deliver} } ``` --- class: middle ### General purpose actor ```js function init () { return 0 } function handle (message, count) { return count + 1 } var state = init() while (true) { const message = await mailbox.receive() state = handle(message, state) } ``` --- class: middle ### Starting actors ```js function Actor (init, handle) { const mailbox = Mailbox() (async function run () { var state = init() while (true) { const message = await mailbox.receive() state = handle(message, state) } })() return {deliver: mailbox.deliver} } ``` *Guarantees only this actor is able to receive from the mailbox* ??? third argument could be self --- class: middle ## Actor system - **requirements** Actors specify a concurrent program. To run the program requires an **Actor System** that handles. - Allocating addresses - Delivering messages - Scheduling actors --- class: middle ### Actor **System** ```js const actors = [] function start (init, handle) { return actors.push(Actor(init, handle)) - 1 } async function deliver (address, message) { actors[address].dispatch(message) } ActorSystem = {start, deliver} ``` --- class: middle ## Actor system - **trade offs** - **Concurrent** (not parallel) - **Cooperative** (not preemtive) - **Voluntary** (not obligatory) --- class: middle ![](/images/concurrency-vs-parallelism.jpg) --- class: middle ### Cooperative vs Preemtive - **Cooperative** - processes must yield control. - **Preemtive** - a process can be stoped at any time. ```js const message = mailbox.receive() // greedy process while (true) { // run forever } ``` --- class: middle ### Voluntary vs Mandatory ```js // send a mutable message const message = [] ActorSystem.dispatch(actor, message) // later message.push('surprise') ``` ```js const message = mailbox.receive() // use global state window.message = message ``` --- class: middle ### Example: **Ping pong** ```js // actor behaviour function init () { return null } function handle ({type, address}, state) { if (type == 'ping') { ActorSystem.dispatch(address, {type: 'pong'}) } else { console.log('Received Pong!') } return state } // run const a1 = ActorSystem.start(init, handle) const a2 = ActorSystem.start(init, handle) ActorSystem.dispatch(a1, {type: 'ping', address: a2}) ``` --- class: middle # Why? --- class: middle ## Where is **shared memory** ![](/images/shared-memory.png) ??? http://www.erlang-factory.com/upload/presentations/45/keynote_joearmstrong.pdf shared memory slide 29 --- class: middle ## Your **distributed** system - High availability - client and server - backups failover - multicore Sending data ALWAYS has **latency**, and is **unreliable**. --- class: middle ## Programming is **parallel** ![](/images/42-years-processor-trend.png) --- class: middle ![](/images/amdahls-law.png) --- class: middle > “My first message is that concurrency is best regarded as a program structuring principle” *Tony Hoare* ??? https://qconlondon.com/london-2009/qconlondon.com/dl/qcon-london-2009/slides/JoeArmstrong_ErlangALanguageForProgrammingReliableSystems.pdf --- class: middle > Packing huge big rocks into containers is very very difficult, but pouring sand into containers is really easy. If you think of processes like little grains of sand and you think of schedulers like big barrels that you have to fill up, filling your barrels up with sand, you can pack them very nicely, you just pour them in and it will work. *Joe Armstrong* --- class: middle ## ActorSystem2 ```js navigator.hardwareConcurrency // 4 new Worker('./actor-system.js') ``` Left as an exercise for the **reader**. --- class: middle ## **Abstracted** communication ![](/images/envelope.jpg) --- class: middle ![](/images/mail-by-rail.jpg) --- class: middle Many to Many relationship among Actors and Addresses. ```sh $ dig +short google.com 216.58.204.14 ``` --- class: middle ## Descriptive **side effects** ```js var state = init() while (true) { const message = await mailbox.receive() {outbound, state} = handle(message, state) outbound.forEach(doSend) } ``` *handle can now be a totally pure function* ### Session types http://www.di.unito.it/~dezani/papers/sto.pdf --- class: middle ![](/images/pebble-1.jpg) --- class: middle # HTTP is **message passing** > The Hypertext Transfer Protocol (HTTP) is a stateless application- level request/response protocol that uses extensible semantics and self-descriptive message payloads for flexible interaction with network-based hypertext information systems. Let's build a server --- class: middle ## **Mandatory** actor system > The Erlang view of the world is that everything is a process and that processes can interact only by exchanging messages. *Joe Armstrong* --- class: middle # What is **Raxx**? 1. Elixir interface for HTTP servers, frameworks (and clients) 2. Toolkit for web development 3. Simple streaming support ### and **Ace**? A server to run Raxx applications 1. HTTP/2 + HTTPS, by default 2. Isolated message exchanges --- class: middle ## Walking tour of **Ace** > We do not have ONE web-server handling 2 millions sessions. We have 2 million webservers handling one session each. [Managing Two Million Webservers](https://joearms.github.io/published/2016-03-13-Managing-two-million-webservers.html) *Joe Armstrong* --- class: middle, center ![](/talks/2017-03-29/building-a-webserver-in-elixir/ace_waiting.png) --- class: middle, center ![](/talks/2017-03-29/building-a-webserver-in-elixir/ace_step_1.png) --- class: middle, center ![](/talks/2017-03-29/building-a-webserver-in-elixir/ace_step_2.png) --- class: middle ## GenServer ```elixir defmodule MyServer do use GenServer def handle_call(:request, _from, state) do {:reply, :response, state} end end ``` --- class: middle ## Raxx.SimpleServer ```elixir defmodule Greetings do use Raxx.Server def handle_request( _request, _state) do %Raxx.Response{status: 200, headers: ["content-type", "text/plain"] body: "Hello, World!"} end end ``` --- class: middle ## Raxx.SimpleServer ```elixir defmodule Greetings do use Raxx.Server def handle_request( _request, _state) do response(:ok) |> set_header("content-type", "text/plain") |> set_body("Hello, World!") end end ``` --- class: middle ## Raxx.SimpleServer ```elixir defmodule Greetings do use Raxx.Server def handle_request( %{path: ["name", name]}, _state) do response(:ok) |> set_header("content-type", "text/plain") |> set_body("Hello, #{name}!") end end ``` --- class: middle ## Raxx.SimpleServer ```elixir defmodule Greetings do use Raxx.Server def handle_request( %{path: ["name", name]}, %{greeting: greeting}) do response(:ok) |> set_header("content-type", "text/plain") |> set_body("#{greeting}, #{name}!") end end ``` --- class: middle # What about **streaming**? --- class: middle ``` tail | data(1+) | head(request) --> Client ============================================ Server <-- head(response) | data(1+) | tail ``` --- class: middle ```elixir defmodule Upload do use Raxx.Server def handle_head(%{path: ["upload"] body: true}, _) do {:ok, io_device} = File.open("my/path") {[], {:file, device}} end def handle_data(data, state = {:file, device}) do IO.write(device, data) {[], state} end def handle_tail(_trailers, state) do response(:see_other) |> set_header("location", "/") end end ``` --- class: middle ## The **Raxx** toolkit - Routing ✔️ - Middleware ✔️ - Templates ✔️ (**EExHTML**) - Code reloading ✔️ (**Raxx.Kit**) - Project generators ✔️ (**Raxx.Kit**) --- class: middle # Next? ![](/images/client-server-model-diagram.png) --- class: middle # GenBrowser > GenBrowser treats clients as just another process in one continuous, if widely distributed, system. Every client gets an **address** to which messages can be dispatched; and a **mailbox** where messages are delivered. --- class: middle ## Actor **lifecycle** 1. Client joins, it is not started. ```js const client = await GenBrowser.start('http://localhost:8080') const {address, mailbox, send, communal} = client console.log(address) // "g2gCZA ..." ``` 2. Disconnected clients are not dead. --- class: middle ## Messages from the **server** ```elixir message = %{ "type" => "ping", "from" => GenBrowser.Address.new(self()) } GenBrowser.send("g2gCZA ...", message) receive do message -> IO.inspect(message) end # => %{"type" => "pong"} ``` --- class: middle ## Messages from a **client** ```js client.send("g2gCZA ...", {type: 'ping', from: client.address}) const reply = await client.mailbox.receive({timeout: 5000}) console.log("Pong received") ``` --- class: middle ## Guarantees - There should be only one actor per mailbox - Reconnection requires a cursor signed by the server - Addresses are unforgable - Addresses are all signed by the server - Object capability model --- class: middle ## Try it out ```elixir # Plug/Phoenix integration communal = %{myProcess: GenBrowser.Address.new(MyNamedProcess)} plug GenBrowser.Plug, communal: communal ``` ```sh # Docker playground docker run -it -e SECRET=s3cr3t -p 8080:8080 gen-browser ``` --- class: middle ## What's behind an address? ```elixir {:via, GenBrowser, "abc123"} ```
```elixir {:via, IOTSensor, "lightbulb"} ``` ```elixir {:via, PersistantActor, "4ever"} ``` ```elixir {:via, EmailService, "bob@example.com"} ``` --- class: middle # Humans ![](/images/pitstop.gif) --- class: middle ## Humans - **Universal** primitive of concurrent computation - Communicate via **asynchronous** message passing - React to messages by making **local** decisions ??? Only difference is they are non deterministic I'm looking for communication protocol from early navys --- class: middle ## Thank you *See the code* - **[github.com/crowdhailer/raxx](https://github.com/crowdhailer/raxx)**
Interface for HTTP webservers, frameworks and clients. - **[github.com/crowdhailer/gen_browser](https://github.com/crowdhailer/gen_browser)**
Actors for the client and server *Comments and questions* - [twitter.com/CrowdHailer](twitter.com/CrowdHailer)