Reaktive Programmierung
Vorlesung 15 vom 29.06.15: Robustheit und Entwurfsmuster
Christoph Lüth, Martin Ring Universität Bremen Sommersemester 2017
12:21:45 2017-07-04 1 [24]
Fahrplan
I Einführung
I Monaden als Berechnungsmuster I Nebenläufigkeit: Futures and Promises I Aktoren I: Grundlagen
I Aktoren II: Implementation I Bidirektionale Programmierung I Meta-Programmierung I Reaktive Ströme I I Reaktive Ströme II
I Functional Reactive Programming I Software Transactional Memory I Eventual Consistency I Robustheit und Entwurfsmuster I Theorie der Nebenläufigkeit, Abschluss
RP SS 2017 2 [24]
Rückblick: Konsistenz
I Strikte Konsistenz in verteilten Systemen nicht erreichbar I Strong Eventual Consistency
I Wennlängere Zeitkeine Änderungen stattgefunden haben befinden sich schließlich alle Knoten imgleichen Zustand.
I Wenn zwei Knoten diegleiche MengeUpdates beobachten befinden sie sich imgleichen Zustand.
I Conflict-Free replicated Data Types:
I Zustandsbasiert: CvRDTs
I Operationsbasiert: CmRDTs I Operational Transformation
I Strong Eventual Consistency auch ohne kommutative Operationen
RP SS 2017 3 [24]
Robustheit in verteilten Systemen
Lokal:
I Nachrichten gehen nicht verloren
I Aktoren können abstürzen - Lösung: Supervisor Verteilt:
I Nachrichten können verloren gehen I Teilsysteme können abstürzen
IHardware-Fehler
IStromausfall
IGeplanter Reboot (Updates)
INaturkatastrophen / Höhere Gewalt
ISoftware-Fehler
RP SS 2017 4 [24]
Zwei-Armeen-Problem
A1 -B A2
I Zwei ArmeenA1undA2sind jeweils zu klein um gegen den FeindBzu gewinnen.
I Daher wollen sie sich über einen Angriffszeitpunkt absprechen.
RP SS 2017 5 [24]
Zwei-Armeen-Problem
A1 -B A2
• -•
• •
• -•
I Unlösbar – Wir müssen damit leben!
RP SS 2017 6 [24]
Unsichere Kanäle
I Unsichere Kanäle sind ein generelles Problem der Netzwerktechnik I Lösungsstrategien:
I Redundanz – Nachrichten mehrfach schicken
I Indizierung – Nachrichten numerieren
I Timeouts – Nicht ewig auf Antwort warten
I Heartbeats – Regelmäßige „Lebenszeichen“
I Beispiel: TCP
I Drei-Wege Handschlag
I Indizierte Pakete
RP SS 2017 7 [24]
Gossipping
N1 N2 N3 N4
N5 N6
-
N7 N8
N9
N10 N11 -
N12
RP SS 2017 8 [24]
Gossipping
I Jeder Knoten verbreitet Informationen periodisch weiter anzufällige weitere Knoten
I Funktioniert besonders gut mit CvRDTs
I Nachrichtenverlust unkritisch I Anwendungen
I Ereignis-Verteilung
I Datenabgleich
I Anti-entropy Protokolle
I Aggregate, Suche
RP SS 2017 9 [24]
Heartbeats
I Kleine Nachrichten in regelmäßigen Abständen I Standardabweichung kann dynamisch berechnet werden I Φ =−log10(1−F(timeSinceLastHeartbeat))
RP SS 2017 10 [24]
Akka Clustering
I Verteiltes Aktorsystem
I Infrastruktur wird über gossipping Protokoll geteilt
I Ausfälle werden über Heartbeats erkannt I Sharding: Horizontale Verteilung der Resourcen
I In Verbindung mit Gossipping mächtig
RP SS 2017 11 [24]
(Anti-)Patterns: Request/Response
I Problem: Warten auf eine Antwort — Benötigt einen Kontext der die Antwort versteht
I Pragmatische Lösung: Ask-Pattern importakka . patterns . ask ( otherActor ? Request) map {
caseResponse => //
}
I Eignet sich nur für sehr einfache Szenarien I Lösung: Neuer Aktor für jeden Response Kontext
RP SS 2017 12 [24]
(Anti-)Patterns: Nachrichten
I Nachrichten solltentypisiertsein
otherActor ! "add 5 to your l o c a l state " //NO otherActor ! Modify(_ + 5)//YES
I Nachrichten dürfennichtveränderlich sein!
val state : scala . c o l l e c t i o n . mutable . Buffer otherActor ! Include ( state ) //NO otherActor ! Include ( state . toList ) //YES
I Nachrichten dürfenkeine Referenzenauf veränderlichen Zustand enthalten
var state = 7
otherActor ! Modify(_ + state ) //NO val stateCopy = state
otherActor ! Modify(_ + stateCopy ) //YES
RP SS 2017 13 [24]
(Anti-)Patterns: State-Leaks
I Lokaler Zustand darf auf keinen Fall “auslaufen”!
var state = 0
( otherActor ? Request) map { caseResponse =>sender ! RequestComplete }
I Besser?
( otherActor ? Request) map { caseResponse =>
state += 1; RequestComplete } pipeTo sender
I So geht’s!
( otherActor ? Request) map { caseResponse =>
s e l f ! IncState RequestComplete } pipeTo sender
RP SS 2017 14 [24]
(Anti-)Patterns: Single-Responsibility
I Problem: Fehler in Komplexen Aktoren sind kaum behandelbar var i n t e r e s t D i v i s o r = i n i t i a l
def receive = {
case Divide ( dividend , d i v i s o r ) =>
sender ! Quotient ( dividend / d i v i s o r ) case CalculateInterest (amount) =>
sender ! I n t e r e s t (amount/ i n t e r e s t D i v i s o r ) case A l t e r I n t e r e s t (by) =>
i n t e r e s t D i v i s o r += by }
I Welche Strategie beiDivByZeroException?
I Ein Aktor sollte immer nureineAufgabe haben!
RP SS 2017 15 [24]
(Anti-)Patterns: Aktor-Beziehungen
M
S1 init
-
S2
init
I Problem: Wer registriert sich bei wem in einer Master-Slave-Hierarchie?
I Slaves sollten sich beim Master registrieren!
IFlexibel / Dynamisch
IEinfachere Konfiguration in verteilten Systemen
RP SS 2017 16 [24]
(Anti-)Patterns: Aufgabenverteilung
I Problem: Nach welchen Regeln soll die Aktorhierarchie aufgebaut werden?
I WichtigeInformationen und zentrale Aufgaben sollten möglichst nah an der Wurzel sein.
I Gefährlichebzw. unsichere Aufgaben sollten immer Kindern übertragen werden.
RP SS 2017 17 [24]
(Anti-)Patterns: Zustandsfreie Aktoren
I Ein Aktor ohne Zustand
class Calculator extends Actor { def receive = {
caseDivide (x , y) =>sender ! Result (x / y) }
}
I Ein Fall für Käpt’n Future!
class UsesCalculator extends Actor { def receive = {
case Calculate ( Divide (x , y) ) =>
Future (x/y) pipeTo s e l f case Result (x) =>
p r i n t l n ("Got i t : " + x) }
}
RP SS 2017 18 [24]
(Anti-)Pattern: Initialisierung
I Problem: Aktor benötigt Informationen bevor er mit der eigentlichen Arbeit loslegen kann
I Lösung: Parametrisierter Zustand class Robot extends Actor {
def receive = u n i n i t i a l i z e d def u n i n i t i a l i z e d : Receive = {
case I n i t (pos , power) =>
context . become( i n i t i a l i z e d (pos , power) ) }
def i n i t i a l i z e d (pos : Point , power : Int ) : Receive = { caseMove(North) =>
context . become( i n i t i a l i z e d (pos + (0 ,1) , power−1) ) }
}
RP SS 2017 19 [24]
(Anti-)Patterns: Kontrollnachrichten
I Problem: Aktor mit mehreren Zuständen behandelt bestimmte Nachrichten in jedem Zustand gleich
I Lösung: Verkettete partielle Funktionen class Obstacle extends Actor {
def rejectMoveTo : Receive = { caseMoveTo =>sender ! Reject }
def receive = u n i n i t i a l i z e d orElse rejectMoveTo def u n i n i t i a l i z e d : Receive =. . .
def i n i t i a l i z e d : Receive =. . . }
RP SS 2017 20 [24]
(Anti-)Patterns: Circuit Breaker
I Problem: Wir haben eine elastische, reaktive Anwendung aber nicht genug Geld um eine unbegrenzt große Server Farm zu betreiben.
I Lösung: Bei Überlastung sollten Anfragen nicht mehr verarbeitet werden.
class DangerousActor extends Actor with ActorLogging { val breaker =
new CircuitBreaker ( context . system . scheduler , maxFailures = 5 ,
callTimeout = 10. seconds ,
resetTimeout = 1. minute) .onOpen(notifyMeOnOpen() ) def notifyMeOnOpen() : Unit =
log . warning("My CircuitBreaker i s now open , and w i l l not close for one minute")
RP SS 2017 21 [24]
(Anti)-Patterns: Message Transformer
class MessageTransformer(from : ActorRef , to : ActorRef , transform : PartialFunction [Any,Any] ) extends Actor { def receive = {
casem =>to forward transform (m) }
}
RP SS 2017 22 [24]
Weitere Patterns
I Lange Aufgaben unterteilen I Aktor Systeme sparsam erstellen I Futures sparsam einsetzen
I Await.result()nurbei Interaktion mit Nicht-Aktor-Code I Dokumentation Lesen!
RP SS 2017 23 [24]
Zusammenfassung
I Nachrichtenaustausch in verteilten Systemen ist unzuverlässig I Zwei Armeen Problem
I Lösungsansätze
IDrei-Wege Handschlag
INachrichtennummerierung
IHeartbeats
IGossipping Protokolle I Patterns und Anti-Patterns
I Nächstes mal: Theorie der Nebenläufigkeit
RP SS 2017 24 [24]