程序代写代做代考 concurrency Erlang Advanced Programming – Erlang for Robust Systems

Advanced Programming – Erlang for Robust Systems

Advanced Programming
Erlang for Robust Systems

Ken Friis Larsen
kflarsen@diku.dk

Department of Computer Science
University of Copenhagen

October 9, 2018

1 / 16

Today’s Menu

I Recap Linking processes

I Supervisors

I Library code for making robust servers

I Open Telecom Platform (OTP)

2 / 16

Robust Systems

I We need at least two
computers(/nodes/processes) to make a
robust system: one
computer(/node/process) to do what we
want, and one to monitor the other and
take over when errors happens.

I link(Pid) makes a symmetric link
between the calling process and Pid.

I monitor(process, Pid) makes an
asymmetric link between the calling
process and Pid.

S W

P1 P2

3 / 16

Linking Processes

I If we want to handle when a linked process crashes then we
need to call process_flag(trap_exit, true).

I Thus, we have the following idioms for creating processes:
I Idiom 1, I don’t care:

Pid = spawn(fun() -> … end)
I Idiom 2, I won’t live without her:

Pid = spawn_link(fun() -> … end)
I Idiom 3, I’ll handle the mess-ups:


process_flag(trap_exit, true),
Pid = spawn_link(fun() -> … end),
loop(…).

loop(State) ->
receive
{‘EXIT’, Pid, Reason} -> HandleMess, loop(State);

end.
4 / 16

Example: Keep Trucking Looping

Suppose that we really most have a phonebook server running at all
times. How do we monitor the phonebook server and restart it if
(when?) it crashes.

5 / 16

Example: Keep Looping

start() -> keep_looping().
request_reply(Pid, Request) -> Ref = make_ref(),

Pid ! {self(), Ref, Request},
receive {Ref, Response} -> Response end.

keep_looping() ->
spawn(fun () ->

process_flag(trap_exit, true),
Worker = spawn_link(fun() -> loop(#{}) end),
supervisor(Worker)

end).
supervisor(Worker) ->

receive
{‘EXIT’, Pid, Reason} ->

io:format(“~p exited because of ~p~n”, [Pid,Reason]),
Pid1 = spawn_link(fun() -> loop(#{}) end),
supervisor(Pid1);

Msg -> Pid ! Msg, supervisor(Pid)
end.

6 / 16

Generic Servers

I Goal: Abstract out the difficult handling of concurrency to a
generic library

I The difficult parts:
I The start–request_reply(/async)–loop pattern
I Supervisors
I Hot-swapping of code

7 / 16

Vanilla Server Library

start(Mod) ->
spawn(fun() -> loop(Mod, Mod:init()) end).

request_reply(Pid, Request) ->
Pid ! {self(), Request},
receive

{Pid, Reply} -> Reply
end.

loop(Mod, State) ->
receive

{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {self(), Reply},
loop(Mod, State1)

end.
8 / 16

Basic Server Library

start(Name, Mod) ->
register(Name, spawn(fun() -> loop(Name, Mod, Mod:init())

end)).
request_reply(Pid, Request) ->
Pid ! {self(), Request},
receive
{Pid, Reply} -> Reply

end.
loop(Name, Mod, State) ->
receive

{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {Name, Reply},
loop(Name, Mod, State1)

end.

9 / 16

Example: Phonebook Callback Module, 1

-module(pb).
-import(basicserver, [request_reply/2]).

%% Interface
start() -> basicserver:start(phonebook, pb).
add(Contact) -> request_reply(phonebook, {add, Contact}).
list_all() -> request_reply(phonebook, list_all).
update(Contact) -> request_reply(phonebook, {update, Contact}).

10 / 16

Example: Phonebook Callback Module, 2

%% Callback functions
init() -> #{}.

handle({add, {Name, _, _} = Contact}, Contacts) ->
case maps:is_key(Name, Contacts) of

false -> {ok, Contacts#{Name => Contact}};
true -> {{error, Name, is_already_there},

Contacts}
end;

handle(list_all, Contacts) ->
List = maps:to_list(Contacts),
{{ok, lists:map(fun({_, C}) -> C end, List)},
Contacts};

handle({update, {Name, _, _} = Contact}, Contacts) ->
{ok, Contacts#{Name => Contact}}.

11 / 16

Hot Code Swapping

swap_code(Name, Mod) -> request_reply(Name, {swap_code, Mod}).
request_reply(Pid, Request) ->

Pid ! {self(), Request},
receive {Pid, Reply} -> Reply
end.

loop(Name, Mod, State) ->
receive

{From, {swap_code, NewMod}} ->
From ! {Name, ok},
loop(Name, NewMod, State);

{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {Name, Reply},
loop(Name, Mod, State1)

end.

12 / 16

Open Telecom Platform (OTP)

I Library(/framework/platform) for building large-scale,
fault-tolerant, distributed applications.

I A central concept is the OTP behaviour
I Some behaviours

I supervisor
I gen_server
I gen_statem (or gen_fsm)
I gen_event

I See proc_lib and sys modules for basic building blocks.

13 / 16

Using gen_server

I Step 1: Decide module name

I Step 2: Write client interface functions
I Step 3: Write the six server callback functions:

I init/1
I handle_call/3
I handle_cast/2
I handle_info/2
I terminate/2
I code_change/3

(you can implement the callback functions by need.)

14 / 16

Summary

I To make a robust system we need two parts: one to do the job
and one to take over in case of errors

I Structure your code into the infrastructure parts and the
functional parts.

I Use gen_server for building robust servers.

I This week’s assignment: Flamingo

15 / 16

Exam

I One week take-home project (2/11–9/11)

I Hand in via Digital Exam

I Max group size is 1 (one)

I The University has a zero-tolerance policy against exam fraud
(including assisting).

16 / 16