Geovane Fedrecheski - geovane@lsi.usp.br
Pablo C. Ccori - pablo@lsi.usp.br
School of Engineering
University of São Paulo
f(g(x), h(...))
Steve Vinoski, IEEE Computing Society 2009
"... ironically, the resurgence of interest in FP languages owes a lot to Java’s popularity."
"Another reason [...] is the move toward multicore architectures."
"... developers look for the opportunity to
get more done with less."
Lisp
Elm
Core committer
Unsatisfied with thread-based concurrency
~2010
Unsatisfied with thread-based concurrency
Repetitive strain
injury
by Bruce A. Tate
context
Created in the Telecommunication industry
...
~1980
requirements
Programming language
Virtual Machine - BEAM
Framework - OTP
-module(fact).
-export([fac/1]).
fac(0) -> 1;
fac(N) -> N * fac(N-1).
The VM
Concurreny
Fault tolerance
Data structures
Consistent libs
Modern tooling
I loved everything that Erlang had
but I hated everything that it didn't had
- José Valim
Goals
> {method, path} = {:get, "temp"}
{:get, "temp"}
> method
:get
> path
"temp"
b = :binary.encode_unsigned(258)
# b has value <<1, 2>>
<<x, 2>> = b
# x has value 1
i = :binary.decode_unsigned(<<x, 2>>)
# i has value 258
request = {:get, "temp"}
case request do
{:post, path, payload} ->
send_resp 200
{:get, path} ->
send_resp 200, "{'value':17.5, 'unit': 'celsius'}"
end
defmodule Math do
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n-1) + fib(n-2)
end
Math.fib(8) # => 21
> add = fn(a, b) -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
> add.(1, 2)
3
> Enum.map([17.9, 20.2, 18.4], fn(x) -> x * 2 end)
[35.8, 40.4, 36.8]
Can be passed as parameters
spawn
A lightweight thread of execution
(The key Erlang/Elixir abstraction)
message
Excuse me, have you ever heard of (VM) processes?
Scheduler
Sched.
Sched.
Sched.
OS Processes
OS Threads
VM Processes
OS
pid = spawn(fn ->
:timer.sleep(3000)
IO.puts "hi"
end)
fn ..
spawn
self()
sleep
puts
A lightweight thread of execution
pid = spawn(...)
send(pid, "hello")
receive do
msg ->
IO.puts msg
end
fn ..
spawn
self()
receive
send
Node.spawn(
:"app@computer-2",
fn -> Hello.world end)
send
receive
app@computer-1
app@computer-2
spawn
spawn
worker
worker
worker
worker
import Supervisor.Spec children = [ worker(Cache, []), supervisor(DatabaseSupervisor, []), supervisor(TCPSupervisor, [4040]) ] Supervisor.start_link(children, strategy: :one_for_one)
app@computer-1
app@computer-2
Easy CoAP binary parser
Fault-tolerant microservices
Elixir and Java load test
Automatic deployment of Elixir images
Constrained Application Protocol
kind of a "smaller HTTP, for IoT"
request
response
(binary header)
0.01 (GET) (Request)
0.02 (POST) (Request)
2.05 (Content) (Response)
4.04 (Not Found) (Response)
000.00001
000.00010
010.00101
100.00100
1
0
0
0.01
123
01000000 00000001 0000000001111011 11111111
255
64 1 0 123 255
(empty)
(empty)
(empty)
%Message(type: :con,
code: :get,
msg_id: 123,
options: [])
int coap_parseHeader(coap_header_t *hdr,
const uint8_t *buf,
size_t buflen)
{
if (buflen < 4)
return COAP_ERR_HEADER_TOO_SHORT;
hdr->ver = (buf[0] & 0xC0) >> 6;
if (hdr->ver != 1)
return COAP_ERR_VERSION_NOT_1;
hdr->t = (buf[0] & 0x30) >> 4;
hdr->tkl = buf[0] & 0x0F;
hdr->code = buf[1];
hdr->id[0] = buf[2];
hdr->id[1] = buf[3];
return 0;
}
https://github.com/1248/microcoap/blob/master/coap.c
def decode(<<1::2, type::2, token_len::4,
code_class::3, code_detail::5,
msg_id::16,
token::binary-size(token_len),
rest::binary>>) do
def decode(<<1::2, type::2, token_len::4,
code_class::3, code_detail::5,
msg_id::16,
token::binary-size(token_len),
rest::binary>>) do
code = decode_code(code_class, code_detail)
token = decode_token(token, token_len)
{options, payload} =
decode_options_and_payload(rest)
%Message{
version: 1,
type: Registry.from(:types, type),
code: code,
msg_id: msg_id, token: token,
options: options, payload: payload
}
end
int coap_parseHeader(coap_header_t *hdr,
const uint8_t *buf,
size_t buflen)
{
if (buflen < 4)
return COAP_ERR_HEADER_TOO_SHORT;
hdr->ver = (buf[0] & 0xC0) >> 6;
if (hdr->ver != 1)
return COAP_ERR_VERSION_NOT_1;
hdr->t = (buf[0] & 0x30) >> 4;
hdr->tkl = buf[0] & 0x0F;
hdr->code = buf[1];
hdr->id[0] = buf[2];
hdr->id[1] = buf[3];
return 0;
}
Source: http://martinfowler.com/articles/microservices.html
Source: http://martinfowler.com/articles/microservices.html
Services as components
Organized as business capabilites
Smart endpoints and dumb pipes
Decentralized Governance
Decentralized Data Management
Infrastructure Automation
Design for failure
Evolutionary Design
Martin Fowler, MicroservicePremium, May 2015
Processes as nanoservices
Processes packaged in Applications
Fault-tolerant processes
Process supervisors
Distributed Erlang
*http://blog.plataformatec.com.br/2015/06/elixir-in-times-of-microservices/
load test
Lines of Code
CPU
Memory
Requests / sec.
Quad-core computer (x86)
6GB of RAM
Java 1.8
Erlang 18.2
Elixir 1.2
Load test tool: Tsung 1.6
Lines of Code |
||
---|---|---|
CPU |
||
Memory |
||
Requests / sec. |
Raspberry Pi
Beaglebone Black
sudo vi /etc/apt/sources.list
# install erlang stuff...
# clone elixir
make && make install
Repeat the process for each board?
Maybe write a script
What if want to use another board? (will need a different setup)
Craft and deploy bulletproof embedded software in Elixir
Buildroot is a simple, efficient and easy-to-use tool to generate embedded Linux systems through cross-compilation.
Toolchain
$ mix nerves.new hello_nerves --target rpi3 $ cd hello_nerves $ mix deps.get $ mix compile
(write your project in Elixir)
$ mix firmware $ mix firmware.burn
$ NERVES_TARGET=bbb mix firmware
$ http POST "192.168.0.121:8988/firmware" \
content-type:application/x-firmware \
x-reboot:true < firmware.fw
:observer.start
(this is the appendix!)
(other OS processes)
OS
defmodule Math do
# this is a comment.
# below is a function
def sum(a, b) do
a + b
end
end
# pattern matching is like method
# overloading on steroids!
defmodule Math do
def zero?(0) do
true
end
def zero?(x) when is_integer(x) do
false
end
end
{a, b, c} = {:hello, "world", 42}
a # has value :hello
b # "world"
c # 42
b = :binary.encode_unsigned(258)
# b has value <<1, 2>>
<<x, 2>> = b
# x has value 1
i = :binary.decode_unsigned(<<x, 2>>)
# i has value 258
Some Drips
my = self() #PID<0.141.0>
pid = spawn(fn ->
:timer.sleep(3000)
send(my, {:get, "temp"})
end)
receive do # will block for 3 secs!
{:post, path} ->
IO.puts "was a post on path '#{path}'"
{:get, path} ->
IO.puts "was a get on path '#{path}'"
end
> x = 1
1
> 1 = x
1
> 2 = x
** (MatchError)