35
1 Elixir – Ein Werkzeug für hochgradig verteilte, fehlertertolerante Systeme Herzlich willkommen – DevDay 2016

Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

1

Elixir – Ein Werkzeug für hochgradig verteilte, fehlertertolerante Systeme

Herzlich willkommen –DevDay 2016

Page 2: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

2

Übersicht

Geschaffen von José Valim

Erlang als erprobte Basis für höchstverfügbare Systeme

Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

Protokolle und Polymorphie in der funktionalen Welt

Behaviours (Generic Server)

Fehlertolerante Systeme mit Supervision Trees

Page 3: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

3

Motivation

Page 4: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

4

Erlang als Basis

Wurzeln in der Telekommunikation

Joe Armstrong (Ericsson, 1987)

Nebenläufigkeit und Fehlertoleranz

Transparente Verteilung über heterogener Hardware

Hochverfügbar (Nine Nines)

Lebendig (Erlang/OTP 18.2 16. Dez 2015)

Page 5: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

5

Wer setzt Erlang ein?

Amazon (SimpleDB, Amazon Elastic Compute Cloud (EC2))

Yahoo! (Delicious, 5 Mio User, 150 Mio Bookmarks)

Facebook (Chat backend)

WhatsApp (Messaging Server)

T-Mobile (SMS und Authentisierung)

Ericsson (Telekom)

Page 6: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

6

Erlang-Applikationen

Ejabberd (Instant Messaging)

CouchDB, Riak, Mnesia

RabbitMQ (AMQP-Implementierung)

Page 7: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

7

Stärken von Erlang

Leichtgewichtige Prozesse (Millionen)

Kein Shared Memory

Funktional (keine Seiteneffekte)

«Let it crash»-Philosophie (Supervision)

BEAM VM

OTP-Plattform

Page 8: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

8

Warum Elixir?

Erlang als erprobte Basis

Übersichtlicherer, kompakterer Code

Eleganz («syntaktischer Zucker»)

Meta-Programmierung

Mix Build-Tool

Page 9: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

9

Joe Armstrong liebt Elixir!

http://joearms.github.io/2013/05/31/a-week-with-elixir.html

«This has been my first week with Elixir, and I’m pretty excited.»

Elixir has a non-scary syntax and combines the good features of Ruby and Erlang. It’s not Erlang and it’s not Ruby and it has ideas of its own.

It’s a new language, but books are being written as the language is being developed. The first Erlang book came 7 years after Erlang was invented, and the first popular book 14 years later. 21 years is too long to wait for a decent book.

Dave [Thomas] loves Elixir, I think it’s pretty cool, I think we’re going to have fun together.

Erlang powers things like WhatsApp and crucial parts of half the world’s mobile phone networks. It’s going to be great fun to see what will happen when the technology becomes less scary and the next wave of enthusiasts joins the party.“

Page 10: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

10

Elixir im Einsatz

Web-Framework Phoenix

Millionen aktiver Connections auf einem Server

Antwortzeiten im Mikrosekundenbereich möglich

IoT-Startups bauen auf Elixir

Page 11: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

11

Eleganz

Page 12: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

12

Summierungsserver in Erlang

-module(sum_server).

-behaviour(gen_server).

-export([

start/0, sum/1, init/1, handle_call/3, handle_cast/2, handle_info/2,

terminate/2, code_change/3

]).

start() -> gen_server:start(?MODULE, [], []).

sum(Server, A, B) -> gen_server_call(Server, {sum, A, B}).

init(_) -> {ok. Undefined}.

handle_call({sum, A, B}, _From, State) -> {reply, A + B, State}).

handle_cast(_msg, State) -> {noreply, State}.

handle_info(_Info, State) -> {noreply, State}.

terminate(_Reason, _State) -> ok.

code_change(_OldVsn, State, _Extra) -> {ok, State}.

Page 13: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

13

Summierungsserver in Elixir

defmodule SumServer do

use GenServer

def start do

GenServer.start(__MODULE__, nil)

end

def sum(server, a, b) do

GenServer.call(server, {:sum, a, b})

end

def handle_call({:sum, a, b}, _from, state) do

{:reply, a + b, state}

end

end

Page 14: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

14

Sprache

Page 15: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

15

Funktionale Elemente von Elixir

Nicht-mutierbare Daten

Seiteneffektfreie Funktionen

Pattern Matching

Tail-Rekursion

Chaining von Funktionen

Polymorphie mit Protokollen

Page 16: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

16

Nicht-mutierbare Daten

Kein OO-Mutieren a la

hashMap.add(key, value)

sondern

newHash = HashMap.add(oldHash, key, value)

Intern effizient durch Daten-Reuse

Page 17: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

17

Seiteneffektfreie Funktionen

Funktionen liefern wie mathematische Funktionen bei jedem Aufruf das gleiche Ergebnis.

Page 18: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

18

Pattern Matching

iex(1)> {name, gehalt} = {"Max", 5000}

Matchoperator =

bindet name und gehalt:

iex(1)> name

"Max"

iex(1)> gehalt

5000

Page 19: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

19

Funktionsdefinitionen mittels Patterns

defmodule Geom do

def area({:square, r}), do: r * r

def area({:rectangle, a, b}), do: a * b

end

iex> c("geom.ex")

[Geom]

Iex> Geom.area({:rectangle, 45, 5})

225

Iex> Geom.area({:square, 45})

2025

Iex> Geom.area({:square, 45, 5})

** (FunctionClauseError) no function clause matching in Geom.area/1

geom.ex:2: Geom.area({:square, 45, 5})

Page 20: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

20

Tail-Rekursion

defmodule M do

def fac(0), do: 1

def fac(n), do: n * fac(n - 1)

end

Letzter Aufruf in der Funktion: TailCall-Optimierung. So baut man Loops in Elixir!

Page 21: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

21

Verkettung von Funktionen, Pipe-Operator – Beispiel von Joe Armstrong (Erlang)

capitalize_atom(X) ->

list_to_atom(binary_to_list(capitalize_binary(list_to_binary(atom_to_list(X))))).

Elixir:capitalize_atom = fn(x) ->

x

|> to_char_list

|> to_string

|> String.capitalize

|> to_char_list

|> List.to_atom

end

Wesentlich lesbarer!

Page 22: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

22

Datenstrukturen mit structs

defmodule Fraction do

defstruct nom: nil, denom: nil

def new(n, d) do

%Fraction{nom: n, denom: d}

end

end

Page 23: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

23

Protokolle

Definition:

defprotocol String.Chars do

def to_string(thing)

end

Verwendung:

iex(1)> String.Chars.to_string(1)

„1“

iex(2)> String.Chars.to_string(:myatom)

„myatom“

iex(3)> String.Chars.to_string(Fraction.new(1, 2))

** (Protocol.UndefinedError) protocol String.Chars not implemented

Page 24: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

24

Implementation von Protokollen

defimpl String.Chars, for: Fraction do

def to_string(frac) do

"#{frac.nom} / #{frac.denom}"

end

end

iex> String.Chars.to_string(Fraction.new(1, 2))

"1 / 2"

iex> one_half = Fraction.new(1, 2)

iex> IO.puts(one_half)

1 / 2

:ok

Page 25: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

25

Verteilung

Page 26: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

26

Prozesse, Actor-Modell

pid = spawn(fn) startet einen Prozess

send(pid, message) schickt Nachricht

Empfang:

receive do

Pattern_1 -> action_1

Pattern_2 -> action_2

end

Page 27: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

27

Server-Prozesse

defmodule SumServer do

use GenServer

def start do

GenServer.start(__MODULE__, nil)

end

def sum(server, a, b) do

GenServer.call(server, {:sum, a, b})

end

def handle_call({:sum, a, b}, _from, state) do

{:reply, a + b, state}

end

end

Interface-Funktion

(start, sum)

Callback-Funktion

(handle_call, handle_cast)

Handgestrickt mit Tail-RekursionBesser: Implementation von GenServer (OTP gen_server behaviour)

Page 28: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

28

Nodes

Nodes sind oberhalb von Prozessen angesiedelt und können auf verschiedenen Maschinen liegen

Namen: iex --sname node1@localhost

Verbindung: Node.connect(:node1@localhost)

Kommunikation:

caller = selfNode.spawn(

:node2@localhost,fn -> send(caller, {:response, „hallo“} end

)

Page 29: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

29

Fehlertoleranz

Page 30: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

30

Fehlertoleranz

Elixir verfügt über try/catch/after-Konstrukt

Wird selten benötigt, verträgt sich nicht mit Tail-Rekursion

Typischerweise wird ein Prozess von einem Supervisor neu gestartet und verfügt dann wieder über einen «sauberen» Zustand

Page 31: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

31

Trapping exits

Prozesse können mit spawn_link bidirektional verknüpft werden

Mittels Process.monitor kann ein Prozess unidirektional überwacht werden

Der Eltern-Prozess kann Exits trappen und entsprechend reagieren

Behaviour Supervisor startet überwachte Prozesse neu

Page 32: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

32FractionServer

defmodule FractionServer do

use GenServer

def start_link(nominator) do

GenServer.start_link(FractionServer, nominator, name: :fraction_server)

end

def fraction(pid, denom) do

GenServer.call(pid, {:fraction, denom})

end

def init(_), do: {:ok, 1}

def handle_call({:fraction, denom}, _, state) do

{:reply, state / denom, state}

end

end

Page 33: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

33FractionServer Supervisor

defmodule FractionSupervisor do

use Supervisor

def start_link do

Supervisor.start_link(__MODULE__, nil, name: :fraction_server_supervisor)

end

def init(_) do

processes = [worker(FractionServer, [1])]

supervise(processes, strategy: :one_for_one)

end

end

Page 34: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

34Supervision in Aktion

iex(3)> FractionSupervisor.start_link

Starting fraction server

{:ok, #PID<0.72.0>}

iex(7)> pid = Process.whereis(:fraction_server)

#PID<0.73.0>

iex(8)> FractionServer.fraction(pid, 3)

0.3333333333333333

iex(9)> FractionServer.fraction(pid, 0)

Starting fraction server

iex(9)> pid = Process.whereis(:fraction_server)

#PID<0.83.0>

iex(10)> FractionServer.fraction(pid, 3)

0.3333333333333333

Page 35: Elixir Ein Werkzeug für hochgradig verteilte ... · Erlang als erprobte Basis für höchstverfügbare Systeme Funktionale Elemente von Elixir (Pattern matching, looping ohne loop-Konstrukte)

35

Supervisor B

Server 1Server 2

Worker 1

Worker 2

Worker 3

one_for_one

one_for_one

simple_one_for_oneone_for_all

Beispiel Supervision Tree

Supervisor CSupervisor A

Root-Supervisor