Why Elixir / Phoenix?
- Concurrent - run thousands of processes
- Fast - response times in Microseconds (500 µs)
- Reliable - recover from errors
Concurrent
- Background tasks built in
- Send messages to other processes
- No need for Resque or DelayedJobs!
- Easy to send messages to processes
Fast
- Responses typically in microseconds
- Capable of serving two million connections on single node
- Router compiles to cat-quick pattern matching
- Templates are precompiled
- Comparision chart
Reliable
- Supervision of processes baked in
- Recovery is a first class citizen
- Powerful tools to debug & monitor
Quick Elixir Review
- Functions are defined by their arity
- `create/1` or `create/2`
- Pattern matching
- `{result, list} = MyModule.doApiCall(token)`
- Pipe operator - `|>`
- ingredients |> stir |> bake |> eat
The players of the game
- Elixir = functional language, compiles to BEAM
- Phoenix = web framework for backend / frontend
- Mix = build tool for Elixir
- Hex = Elixir's package manager
- Ecto = database tool
Out of the box..
- Live reloading on code changes
- Websockets set up
- Asset compilation/minification of JS / CSS / IMG
- Database connectivity, migration, retrieval
- Automatic error recovery
Getting started is easy
mix local.hex
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
mix phoenix.new hello_phoenix
Looking inside Phoenix
Phoenix makes explicit calls with pipes
lters data along the way, and sends response
connection
|> phoenix
=> Response
Peeling the onion back further
connection
|> endpoint
|> router
|> pipelines
|> controller
|> action
|> view
|> template
Introducing Plugs
- Plugs are functions
- Your web applications are pipelines of plugs
- Plugs take a connection, modify, returns new conn
Anatomy of an Endpoint
- Handles all aspects of requests until Router
- Provides core set of plugs to all requests
- Dispatches requests into designated router
Anatomy of a Router
- Parses incoming requests and dispatches to controller/action
- Provides helpers for route paths or urls
- Defines named pipelines for requests
- Pipelines allow grouping of plugs
Anatomy of a Controller
- Provides actions to handle requests
- Prepare data and pass into views
- Invoke rendering via views
- Perform redirects
Anatomy of a View
- Render templates
- Act as presentation layer
- Define helper functions available in templates
Anatomy of a Template
- HTML template as they sound
- Are precompiled
Anatomy of a Channel
- Manage socket for realtime communication
- Analagous to Controllers
- Allow bi-directional communication
- Persistent connections
Plug - the core of it all
A look at an endpoint
defmodule MyApp.Endpoint do
use Phoenix.Endpoint, otp_app: :MyApp
socket "/socket", MyApp.UserSocket
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production.
plug Plug.Static,
at: "/", from: :MyApp, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
plug Plug.RequestId
plug Plug.Logger
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Poison
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session,
store: :cookie,
key: "_MyApp_key",
signing_salt: "PD+nvtps"
plug MyApp.Router
end
Plug - the core of it all
A look at a router
defmodule MyApp.Router do
use MyApp.Web, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", MyApp do
pipe_through :browser # Use the default browser stack
resources "/user", UsersController
resources "/songs", SongController
get "/", PageController, :index
end
# Other scopes may use custom stacks.
# scope "/api", MyApp do
# pipe_through :api
# end
end
Sockets are dead simple
# From channel itself:
broadcast! socket, "new:msg", %{user: msg["user"], body: msg["body"]}
# From IEx / Controller
Chat.Endpoint.broadcast("rooms:lobby", "new:msg", %{message: "hello from the console"})
Database access -> Ecto
- Provides Repo for database
- Models define data schema and assocations
- Queries tie models and repositories
- Changeset declare transformations to models
- Changesets do type casting, validations, and more
- Adapter to database of choice (PostgreSQL, MySQL, etc)
- Additional clients for Redis, Riak, RethinkDB, etc
Static Assets
- Comes with default HTML5 build tool, Brunch
- Easy to swap out for Webpack or browserify
- Live reloading on changes
- Run when server runs
- Support for ES6
- SASS, image compression, and more
OTP
- Provides framework for general servers
- Supervise processes, restart when error occurs
- Finite state machine
- Agents to hold state in separate process
- Distributed database server (Mnesia)
- Event handling
- And so much more...
Supervision of processes
defmodule MyApp do
use Application
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
# Start the endpoint when the application starts
supervisor(MyApp.Endpoint, []),
# Start the Ecto repository
worker(MyApp.Repo, []),
# Here you could define other workers and supervisors as children
worker(MyApp.BackgroundWorker, [arg1, arg2, arg3]),
worker(MyApp.Cleanup, [arg1, arg2, arg3]),
worker(MyApp.Emailer, [arg1, arg2, arg3]),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
MyApp.Endpoint.config_change(changed, removed)
:ok
end
end
Powerful monitoring
- Try out `:observer.start()`
- Instantly see processes, memory usage
- View supervision of application
- Metrics of when to split out supervision
- Normally runs at 15MB usage!
Develop fast with the REPL
- Write and test code instantly
- Run `iex -S mix` to load your application
- Run `recompile` and hot-reload code to try out
- You can even `pry` debug points, test code in place
It's ahead of it's time.
Many of the things you need to add into Node / Rails / Etc
has been in Erlang for 20+ years. State machines, process
watching / restarting, pattern matching, and stateful processes
are available now!
Want more?
- Tons of packages you could hope for
- Hex is growing very quickly
- 1,672 packages (as of 3/6/2016)
- Authentication - controller, socket, and more
- Alternate data access => Moebius
Feeding the Fire
Concurrent |> Fast |> Reliable
Created by Josh Bavari / @jbavari