Thursday 2 February 2017

Erlang

25. Mai 16:00 BST (15:00 UTC 17:00 CET 08:00 PDT) Sportrisq ist ein Makler und Vertreiber von Risikomanagement-Lösungen und Produkten für die Sportindustrie. Hören Sie CTO Justin Worall beschreiben den Prozess der Migration von zwei Kernplattform-Komponenten von Python zu Erlang die zugrunde liegenden Probleme, die wahrgenommenen Vorteile von Erlang in diesen Situationen, den Entscheidungsprozess, die Anwendungsentwürfe und die Ergebnisse. Erlang-solutionsresourceswebinars. html In diesem Webinar, youll lernen: Der Prozess der Migration von Python-Komponenten mit geringer Latenz auf Erlang Die Entscheidungsfindung Anwendungsentwürfe und Ergebnisse Lernen Sie einige Erlang Hey, dort scheint Ihr Javascript deaktiviert ist. Das ist gut, die Website funktioniert ohne sie. Allerdings könnten Sie es vorziehen, lesen Sie es mit Syntax-Highlighting, die Javascript Rage Against Die Finite-State Machines Eine endliche Maschine (FSM) ist nicht wirklich eine Maschine, aber es hat eine endliche Anzahl von Staaten. Ich habe immer Finite-State-Maschinen mit Graphen und Diagrammen besser zu verstehen. Zum Beispiel wäre das folgende ein vereinfachtes Diagramm für einen (sehr dummen) Hund als Staatsmaschine: Hier hat der Hund 3 Zustände: Sitzen, Bellen oder Schwanzwedeln. Verschiedene Ereignisse oder Eingaben können es zwingen, seinen Zustand zu ändern. Wenn ein Hund ruhig sitzt und sieht ein Eichhörnchen, beginnt es zu bellen und wird nicht aufhören, bis Sie es wieder pet. Allerdings, wenn der Hund sitzt und Sie pet it, haben wir keine Ahnung, was passieren könnte. In der Erlang Welt könnte der Hund abstürzen (und schließlich von seinem Vorgesetzten neu gestartet werden). In der realen Welt, die ein freaky Ereignis sein würde, aber Ihr Hund würde zurückkommen, nachdem er durch ein Auto lief, also sein nicht ganz schlecht. Heres ein Katzen-Zustandsdiagramm für einen Vergleich: Diese Katze hat einen einzigen Zustand, und kein Ereignis kann es jemals ändern. Die Implementierung der Katzenstatus-Maschine in Erlang ist eine lustige und einfache Aufgabe: Wir können versuchen, das Modul zu sehen, dass die Katze niemals einen Mist gibt: Das gleiche gilt für den Hund FSM. Außer daß mehr Zustände verfügbar sind: Es sollte relativ einfach sein, jeden der Zustände und Übergänge mit dem überein, was auf dem Diagramm oben war. Heres die FSM im Einsatz: Sie können mit dem Schema folgen, wenn Sie wollen (ich normalerweise tun, es hilft sicher zu sein, dass Nichtigkeiten falsch). Das ist wirklich der Kern der FSMs als Erlang Prozesse umgesetzt. Es gibt Dinge, die anders getan haben könnte: Wir hätten den Zustand in den Argumenten der Zustandsfunktionen in einer Weise übergeben können, die ähnlich dem ist, was wir mit der Hauptschleife der Server tun. Ein weiterer Unterschied zwischen dem Hund und Katze FSMs ist, dass die Katzen Ereignisse synchron sind und die Hunde Ereignisse asynchron sind. In einer echten FSM konnten beide gemischt verwendet werden, aber ich ging für die einfachste Darstellung aus purer unerschlossener Faulheit. Es gibt andere Formen des Ereignisses, die die Beispiele nicht zeigen: globale Ereignisse, die in jedem Zustand passieren können. Ein Beispiel für ein solches Ereignis könnte sein, wenn der Hund bekommt einen Sniff von Lebensmitteln. Sobald der Geruch Essen Ereignis ausgelöst wird, egal in welchem ​​Zustand der Hund ist, hed go suchen für die Quelle der Nahrung. Jetzt wollen wir nicht allzu viel Zeit damit verbringen, das alles in unserem FSM aufzuschreiben. Stattdessen bewegen Sie sich direkt zum Genfsm-Verhalten. Das genfsm Verhalten ist etwas ähnlich dem genserver, dass es eine spezialisierte Version davon ist. Der größte Unterschied ist, dass anstatt Handhabung Anrufe und Abgüsse. Waren synchrone und asynchrone Ereignisse. Ähnlich wie unsere Hunde - und Katzenbeispiele wird jeder Zustand durch eine Funktion repräsentiert. Wieder gut durch die Callbacks gehen unsere Module müssen umzusetzen, um zu arbeiten. Dies ist die gleiche init1 wie für generische Server verwendet, mit Ausnahme der Rückgabewerte akzeptiert werden. . und . Das Stopp-Tupel arbeitet in der gleichen Weise wie für genserver s, und hibernate und Timeout behalten die gleiche Semantik. Was ist neu hier ist, dass StateName Variable. StateName ist ein Atom und repräsentiert die nächste aufzurufende Callback-Funktion. Die Funktionen StateName2 und StateName3 sind Platzhalternamen und Sie müssen entscheiden, was sie sein werden. Angenommen, die init1-Funktion gibt das Tupel zurück. Dies bedeutet, dass die Finite-State-Maschine in einem sitzenden Zustand ist. Dies ist nicht die gleiche Art von Staat, wie wir gesehen hatten mit genserver es ist ziemlich gleichbedeutend mit dem sitzen. Rinde und Bachstelzenstatus des vorherigen Hundes FSM. Diese Zustände diktieren einen Kontext, in dem Sie ein gegebenes Ereignis behandeln. Ein Beispiel hierfür wäre jemand, der Sie auf Ihrem Telefon anruft. Wenn youre im Zustand, der auf einem Samstag Morgen schläft, könnte Ihre Reaktion sein, im Telefon zu schreien. Wenn Ihr Staat wartet auf ein Vorstellungsgespräch, sind die Chancen youll wählen Sie das Telefon und höflich zu beantworten. Auf der anderen Seite, wenn youre in den Zustand tot, dann bin ich überrascht Sie können sogar diesen Text überhaupt lesen. Zurück zu unserem FSM. Die init1-Funktion sagte, wir sollten im sitzenden Zustand sein. Immer wenn der genfsm-Prozess ein Ereignis empfängt, wird die Funktion sitting2 oder sitting3 aufgerufen. Die Funktion sitting2 wird für asynchrone Ereignisse und session3 für synchrone Ereignisse aufgerufen. Die Argumente für sitting2 (oder allgemein StateName2) sind Ereignis. Die aktuelle Nachricht, die als Ereignis gesendet wird, und StateData. Die Daten, die über die Anrufe übertragen wurden. Sitting2 kann dann die Tupel zurückgeben. . und . Die Argumente für sitting3 sind ähnlich, außer es gibt eine From-Variable zwischen Event und StateData. Die From-Variable wird genau so verwendet wie für genserver s, einschließlich genfsm: reply2. Die StateName3-Funktionen können die folgenden Tupel zurückgeben: Beachten Sie, dass es keine Begrenzung gibt, wie viele dieser Funktionen Sie haben können, solange sie exportiert werden. Die Atome, die als NextStateName in den Tupeln zurückgegeben werden, bestimmen, ob die Funktion aufgerufen wird oder nicht. Handleevent Im letzten Abschnitt, erwähnte ich globale Ereignisse, die eine spezifische Reaktion auslösen würde, egal in welchem ​​Zustand waren (der Hund riechende Nahrung wird fallen lassen, was es tut und wird stattdessen nach Nahrung suchen). Für diese Ereignisse, die in jedem Zustand gleich behandelt werden sollten, ist der Callback von handleevent3 genau das, was Sie wollen. Die Funktion hat Argumente, die StateName2 ähnlich sind, mit der Ausnahme, dass sie eine Variable StateName zwischen ihnen akzeptiert und Ihnen mitteilt, was der Zustand war, als das Ereignis empfangen wurde. Es gibt dieselben Werte wie StateName2 zurück. Handlesyncevent Der handlesyncevent4-Rückruf ist StateName3, was handleevent2 zu StateName2 ist. Es behandelt synchrone globale Ereignisse, nimmt die gleichen Parameter und gibt die gleiche Art von Tupeln wie StateName3 zurück. Nun könnte es eine gute Zeit sein zu erklären, wie wir wissen, ob ein Ereignis global ist oder ob es in einem bestimmten Zustand gesendet werden soll. Um dies zu ermitteln, können wir die Funktion betrachten, die verwendet wird, um ein Ereignis an die FSM zu senden. Asynchrone Ereignisse, die auf jede StateName2-Funktion ausgerichtet sind, werden mit sendevent2 gesendet. Werden synchrone Ereignisse, die von StateName3 abgeholt werden sollen, mit syncsendevent2-3 gesendet. Die beiden äquivalenten Funktionen für globale Ereignisse sind sendallstateevent2 und syncsendallstateevent2-3 (ganz langer Name). Codechange Dies funktioniert genau das gleiche wie es für genserver s, außer dass es einen zusätzlichen Zustand Parameter, wenn aufgerufen, wie Code-Austausch (OldVersion, StateName, Data, Extra). Und gibt ein Tupel des Formulars zurück. Dies sollte wieder ein wenig wie das, was wir für generische Server haben. Terminate3 sollte das Gegenteil von init1 ausführen. Es ist an der Zeit, dies alles in die Praxis umzusetzen. Viele Erlang-Tutorials über Finite-State-Maschinen verwenden Beispiele mit Telefon-Schalter und ähnliche Dinge. Seine meine Vermutung, dass die meisten Programmierer selten mit Telefon-Switches für staatliche Maschinen zu tun haben. Aus diesem Grund würden wir uns ein Beispiel ansehen, das für viele Entwickler besser geeignet ist: Entwerfen und implementieren Sie ein Item-Handelssystem für ein fiktives und nicht existierendes Videospiel. Das Design, das ich ausgewählt habe, ist etwas anspruchsvoll. Anstatt einen Broker zu benutzen, bei dem die Spieler Gegenstände und Bestätigungen durchführen (was ehrlich gesagt einfacher wäre), würden wir einen Server implementieren, bei dem beide Spieler direkt miteinander sprechen (was den Vorteil haben würde, dass sie verteilbar sind). Weil die Umsetzung schwierig ist, verbringen Ill eine gute Zeit, die es beschreibt, die Art von Problemen und die Möglichkeiten, sie zu beheben konfrontiert werden. Zunächst sollten wir die Handlungen definieren, die von unseren Spielern beim Trading getätigt werden können. Die erste fragt nach einem Handel aufzubauen. Der andere Benutzer sollte auch in der Lage sein, diesen Handel zu akzeptieren. Wir geben ihnen nicht das Recht, einen Handel zu verweigern, obwohl, weil wir die Dinge einfach halten wollen. Es ist einfach, diese Funktion hinzufügen, sobald die ganze Sache getan ist. Sobald der Handel eingerichtet ist, sollten unsere Benutzer in der Lage sein, miteinander zu verhandeln. Dies bedeutet, dass sie in der Lage, Angebote zu machen und dann zurückziehen, wenn sie wollen. Wenn beide Spieler mit dem Angebot zufrieden sind, können sie sich jeweils als bereit erklären, den Handel abzuschließen. Die Daten sollten dann irgendwo auf beiden Seiten gespeichert werden. Zu jedem Zeitpunkt sollte es auch für einen der Spieler sinnvoll sein, den gesamten Handel abzubrechen. Einige Plebe konnte nur Gegenstände anbieten, die als unwürdig für die andere Partei angesehen werden (die sehr beschäftigt sein könnte) und so sollte es möglich sein, ihnen mit einer wohlverdienten Annullierung zurückzuhandeln. Kurz gesagt, sollten die folgenden Aktionen möglich sein: fragen Sie nach einem Trade nehmen ein Trade-Angebot zurückziehen ein Angebot erklären selbst als bereit brutal abbrechen den Handel Nun, wenn jede dieser Aktionen getroffen wird, sollten die anderen Spieler FSM bewusst gemacht werden . Das macht Sinn, denn wenn Jim seinem FSM sagt, ein Item an Carl zu schicken, muss Carls FSM darauf aufmerksam gemacht werden. Dies bedeutet, dass beide Spieler mit ihrer eigenen FSM sprechen können, die mit den anderen FSM sprechen wird. Das gibt uns ein bisschen so: Das erste, was zu beachten ist, wenn wir zwei identische Prozesse miteinander kommunizieren, ist, dass wir synchrone Aufrufe so weit wie möglich vermeiden müssen. Der Grund dafür ist, dass, wenn Jims FSM sendet eine Nachricht an Carls FSM und wartet dann auf seine Antwort, während zur gleichen Zeit Carls FSM sendet eine Nachricht an Jims FSM und wartet auf seine eigene spezifische Antwort, beide am Ende wartet auf den anderen Ohne jemals zu antworten. Dadurch werden beide FSMs gefriert. Wir haben eine Deadlock. Eine Lösung dafür ist, auf ein Timeout zu warten und dann weiterzugehen, aber dann wird es Überbleibsel Nachrichten in beiden Prozessen Mailboxen und das Protokoll wird durcheinander gebracht werden. Das ist sicher eine Dose Würmer, und so wollen wir es vermeiden. Der einfachste Weg, es zu tun ist, um alle synchronen Nachrichten zu vermeiden und gehen voll asynchron. Beachten Sie, dass Jim noch einen synchronen Aufruf an seine eigenen FSM-Theres es kein Risiko hier, weil die FSM muss nicht Jim anrufen und so keine Deadlock zwischen ihnen auftreten können. Wenn zwei dieser FSMs miteinander kommunizieren, könnte der gesamte Austausch so aussehen: Beide FSMs befinden sich im Ruhezustand. Wenn Sie Jim fragen, zu handeln, muss Jim akzeptieren, bevor die Dinge weitergehen. Dann können beide Gegenstände anbieten oder sie zurückziehen. Wenn Sie beide erklären sich bereit, kann der Handel stattfinden. Dies ist eine vereinfachte Version von allem, was passieren kann und sehen Sie alle möglichen Fälle mit mehr Details in den nächsten Absätzen. Hier kommt der schwierige Teil: Definition des Zustandsdiagramms und wie Zustandsübergänge geschehen. In der Regel ein gutes Denken geht in diese, da müssen Sie an all die kleinen Dinge, die schief gehen könnte denken. Einige Dinge können schief gehen, auch nachdem sie es viele Male überprüft haben. Aus diesem Grund habe ich einfach die, die ich beschlossen, hier zu implementieren und dann erklären. Zunächst beginnen beide Finite-State-Maschinen im Ruhezustand. An dieser Stelle können wir einen anderen Spieler bitten, mit uns zu verhandeln: Wir gehen in den Idlewait-Modus, um auf eine eventuelle Antwort zu warten, nachdem unsere FSM die Nachfrage weitergeleitet hat. Sobald die andere FSM die Antwort sendet, kann unsere zum Verhandeln wechseln: Der andere Spieler sollte auch im Verhandlungszustand danach sein. Offensichtlich, wenn wir die anderen einladen können, kann die andere uns einladen. Wenn alles gut geht, sollte das am Ende so aussehen: So ist das ziemlich genau das Gegenteil, wie die beiden vorherigen Zustandsdiagramme in einem gebündelt. Beachten Sie, dass wir erwarten, dass der Spieler das Angebot in diesem Fall akzeptiert. Was geschieht, wenn durch reines Glück wir den anderen Spieler bitten, mit uns zu handeln, während er uns bittet, zu handeln Was hier geschieht, ist, dass beide Klienten ihre eigene FSM bitten, mit dem anderen zu verhandeln. Sobald die ask negotiate-Meldungen gesendet werden, wechseln beide FSMs in den Idlewait-Zustand. Dann werden sie die Verhandlungsfrage bearbeiten können. Wenn wir die vorherigen Zustandsdiagramme überprüfen, sehen wir, daß diese Kombination von Ereignissen die einzige Zeit ist, die gut empfangen wird, Verhandlungsmitteilungen zu erhalten, während sie sich im Leerlaufzustand befinden. Folglich wissen wir, dass das Erhalten dieser Nachrichten im Leerlauf bedeutet, dass wir die Race-Bedingung getroffen haben und annehmen können, dass beide Benutzer miteinander reden wollen. Wir können beide bewegen, um Staat zu verhandeln. Hurra. Also jetzt verhandeln. Nach der Liste der Aktionen, die ich früher aufgeführt habe, müssen wir Nutzer unterstützen, die Artikel anbieten und dann das Angebot zurückziehen: All dies tut, ist unsere Klient-Nachricht an die andere FSM weiterzuleiten. Beide Finite-State-Maschinen müssen eine Liste der Elemente von jedem Spieler angeboten halten, so können sie diese Liste aktualisieren, wenn solche Meldungen. Wir bleiben im Verhandlungsstaat, danach möchte der andere Spieler auch Gegenstände anbieten: Hier geht unsere FSM grundsätzlich in ähnlicher Weise vor. Das ist normal. Sobald wir müde, Dinge zu bieten und denken großzügig genug waren, müssen wir sagen, waren bereit, den Handel zu vereinheitlichen. Weil wir beide Spieler synchronisieren müssen, müssen wir einen Zwischenstaat nutzen, wie wir es für Idle und Idlewait getan haben: Was wir hier tun, ist, dass, sobald unser Spieler bereit ist, unsere FSM Jims FSM fragt, wenn er bereit ist. Bis zu ihrer Antwort fällt unsere eigene FSM in ihren Wartezustand. Die Antwort gut bekommen wird auf Jims FSM Zustand abhängen: wenn seine in Wartezustand, itll sagen, dass seine bereit. Andernfalls sagen wir, dass es noch nicht fertig ist. Das ist genau das, was unsere FSM automatisch auf Jim antwortet, wenn er uns fragt, ob wir im Verhandlungszustand bereit sind: Unsere finite Zustandsmaschine bleibt im Verhandlungsmodus, bis unser Spieler bereit ist, bereit zu sein. Wir gehen davon aus, dass er es getan hat und nun im Wartezustand war. Allerdings ist Jims noch nicht da. Dies bedeutet, dass, wenn wir uns als bereit erklärt, gut gefragt, Jim, wenn er auch bereit war und seine FSM wird noch nicht geantwortet: Hes nicht bereit, aber wir sind. Wir können nicht viel tun, aber warten. Beim Warten auf Jim, die immer noch über die Art und Weise verhandeln, ist es möglich, dass er versuchen wird, uns mehr Gegenstände zu schicken oder seine bisherigen Angebote zu annullieren: Natürlich wollen wir vermeiden, dass Jim alle seine Gegenstände entfernt und dann Im fertig klickt, Verschraubt uns dabei. Sobald er die angebotenen Artikel wechselt, gehen wir wieder in den Verhandlungsstaat zurück, so dass wir entweder unser eigenes Angebot ändern oder das aktuelle prüfen und entscheiden können. Spülen und wiederholen. Irgendwann wird Jim bereit sein, den Handel zu beenden. Wenn dies geschieht, wird seine Finite-State-Maschine fragen, ob wir bereit sind: Was unsere FSM antwortet, dass wir tatsächlich bereit sind. Wir bleiben im Wartezustand und weigern uns, in den Bereitschaftszustand zu gehen. Warum ist dies Weil es eine potenzielle Rasse Bedingung Stellen Sie sich vor, dass die folgende Abfolge von Ereignissen stattfindet, ohne diesen notwendigen Schritt: Dies ist ein bisschen komplex, so Ill erklären. Wegen der Art und Weise, wie Botschaften empfangen werden, können wir das Angebot nur bearbeiten, nachdem wir uns bereit erklärt haben und auch, nachdem Jim sich als bereit erklärt hat. Das bedeutet, dass wir, sobald wir die Angebotsnachricht gelesen haben, zurück zum Verhandlungsstatus wechseln. Während dieser Zeit wird Jim uns gesagt haben, dass er bereit ist. Wenn er die Zustände genau dort ändern und weiterziehen würde, um fertig zu werden (wie oben dargestellt), würde er auf unbestimmte Zeit warten, während wir nicht wissen, was die Hölle tun würde. Das könnte auch umgekehrt Ugh passieren. Eine Möglichkeit, dies zu lösen, ist durch Hinzufügen einer Schicht der Indirektion (Dank an David Wheeler). Deshalb bleiben wir im Warte-Modus und senden fertig (wie in unserem vorherigen Zustandsdiagramm gezeigt). Heres, wie wir mit dieser fertigen Nachricht umgehen, vorausgesetzt, wir waren bereits im fertigen Zustand, weil wir unserem FSM gesagt hatten, dass wir im Voraus bereit waren: Wenn wir von der anderen FSM bereit sind, schicken wir uns wieder zurück. Dieses ist, um sicherzustellen, dass wir nicht die doppelte Rennenbedingung haben, die oben erwähnt wird. Dies wird eine überflüssige Bereitschaftsmeldung in einer der beiden FSMs erzeugen, muss aber nur in diesem Fall ignorieren. Wir senden dann eine Ack-Nachricht (und die Jims FSM wird das gleiche tun), bevor sie in den Bereit-Zustand. Der Grund, warum diese ack-Nachricht existiert, ist auf einige Implementierungsdetails zum Synchronisieren von Clients zurückzuführen. Ive setzte es im Diagramm für das Sein des korrekten, aber ich erkläre es nicht bis später. Vergessen Sie es für jetzt. Endlich konnten wir beide Spieler synchronisieren. Puh. So jetzt theres der bereite Zustand. Das ist etwas ganz Besonderes. Beide Spieler sind bereit und haben grundsätzlich die Finite-State-Maschinen die ganze Kontrolle, die sie benötigen. Dies ermöglicht es uns, eine bastardisierte Version eines zweiphasigen Commits zu implementieren, um sicherzustellen, dass die Dinge richtig gehen, wenn Sie den Trade offiziell machen: Unsere Version (wie oben beschrieben) wird eher vereinfacht sein. Das Schreiben eines wirklich korrekten Zwei-Phasen-Commits erfordert viel mehr Code als das, was für uns notwendig ist, um Finite-State-Maschinen zu verstehen. Schließlich müssen wir nur zulassen, dass der Handel jederzeit storniert wird. Dies bedeutet, dass irgendwie, egal in welchem ​​Zustand waren, würden auf die Abbrechen-Nachricht von beiden Seiten zu hören und beenden Sie die Transaktion. Es sollte auch gemeinsame Höflichkeit, um die andere Seite wissen, waren vor dem Verlassen weg. Alright Es ist eine ganze Menge Informationen auf einmal zu absorbieren. Dont worry, wenn es eine Weile dauert, es vollständig zu erfassen. Es dauerte eine Menge Leute, um über mein Protokoll zu sehen, um zu sehen, ob es richtig war, und selbst dann haben wir alle ein paar Racebedingungen verpasst, die ich dann ein paar Tage später beim Überprüfen des Codes beim Schreiben dieses Textes erwischt habe. Sein normales, um es mehr als einmal zu lesen, besonders wenn Sie nicht an asynchrone Protokolle gewöhnt sind. Wenn dies der Fall ist, ermutige ich Sie zu versuchen und Design Ihres eigenen Protokolls. Dann fragen Sie sich, was geschieht, wenn zwei Menschen die gleichen Aktionen sehr schnell tun Was passiert, wenn sie zwei andere Ereignisse schnell verknüpfen Was mache ich mit Nachrichten, die ich nicht behandeln, wenn Änderungen Zustände Youll sehen, dass die Komplexität schnell wächst. Vielleicht finden Sie eine Lösung wie meine, vielleicht eine bessere (lassen Sie mich wissen, wenn dies der Fall ist) Egal, das Ergebnis, seine eine sehr interessante Sache zu arbeiten und unsere FSMs sind immer noch relativ einfach. Sobald Sie alle dieses (oder vorher, wenn youre ein Rebellenleser) verdaut haben, können Sie zum folgenden Abschnitt gehen, in dem wir das Spielsystem implementieren. Für jetzt können Sie eine nette Kaffeepause nehmen, wenn Sie Lust haben, so zu tun. Das erste, was getan werden muss, um unser Protokoll mit OTPs genfsm implementieren, ist die Schnittstelle zu erstellen. Es gibt 3 Anrufer für unser Modul: den Spieler, das Genfsm Verhalten und die anderen Spieler FSM. Wir müssen nur die Player-Funktion und genfsm-Funktionen exportieren. Das liegt daran, dass die andere FSM auch im tradefsm-Modul läuft und von innen auf sie zugreifen kann: So das ist unsere API. Sie sehen im Im-Planen, dass einige Funktionen synchron und asynchron sind. Dies ist vor allem, weil wir wollen, dass unser Client uns synchron in einigen Fällen, aber die anderen FSM kann es asynchron. Mit dem Client synchronisiert vereinfacht unsere Logik eine ganze Menge durch die Begrenzung der Anzahl der widersprüchlichen Nachrichten, die nacheinander geschickt werden können. Gut erhalten dort. Lets first implementieren die eigentliche öffentliche API nach dem oben definierten Protokoll: Das ist ziemlich standard alle diese genfsm Funktionen wurden zuvor (außer start3-4 und startlink3-4, die ich glaube, Sie können herausfinden) in diesem Kapitel behandelt. Next well Umsetzung der FSM zu FSM-Funktionen. Die ersten haben mit Handels-Setups zu tun, wenn wir zuerst den anderen Benutzer bitten wollen, uns in einem Trade zu verbinden: Die erste Funktion fragt die andere pid, wenn sie handeln wollen, und die zweite wird verwendet, um darauf zu antworten ( Asynchron, natürlich). Wir können dann die Funktionen anbieten und Angebote annullieren. Nach unserem Protokoll oben, das ist, was sie sein sollten: So, jetzt, dass weve haben diese Anrufe getan, müssen wir auf den Rest konzentrieren. Die restlichen Anrufe beziehen sich darauf, fertig zu sein oder nicht und die endgültige Commit bearbeitet. Auch bei diesem Protokoll haben wir drei Anrufe: Sie sind schon da. Die die Antworten notyet oder bereit haben können. Die einzigen Funktionen, die übrig bleiben, sind diejenigen, die von beiden FSMs verwendet werden sollen, wenn das Commit im ready-Zustand ausgeführt wird. Ihre genaue Verwendung wird später detaillierter beschrieben werden, aber für den heutigen Tag sollten die Namen und das Sequenzdiagramm von vornherein ausreichen. Trotzdem können Sie sie noch zu Ihrer eigenen Version von tradefsm transkribieren: Oh und theres auch die Höflichkeitfunktion, die uns erlaubt, die andere FSM zu warnen, die wir den Handel abgesagt haben: Wir können jetzt zum wirklich interessanten Teil bewegen: die genfsm Rückrufe. Der erste Rückruf ist init1. In unserem Fall, wollen auch, dass jede FSM einen Namen für den Benutzer, den es repräsentiert (so, unsere Ausgabe ist schöner) in den Daten hält, die es weitergibt, um sich selbst zu halten. Was sonst wollen wir in Erinnerung halten In unserem Fall wollen wir die anderen pid, die Artikel, die wir anbieten und die Artikel die andere bietet. Wurden auch die Referenz eines Monitors (so dass wir wissen, um abzubrechen, wenn die anderen stirbt) hinzuzufügen, und ein aus Feld, verwendet, um verzögerte Antworten zu tun: Im Fall von init1. Gut nur kümmern uns um unseren Namen für jetzt. Beachten Sie, dass auch im Ruhezustand beginnen: Die nächsten Callbacks zu prüfen wäre die Staaten selbst. Bisher Ive beschrieben die staatlichen Übergänge und Anrufe, die gemacht werden können, aber gut brauchen einen Weg, um sicherzustellen, dass alles in Ordnung geht. Nun schreiben Sie ein paar Utility-Funktionen zuerst: Und wir können mit dem Ruhezustand zu starten. Im Interesse der Konvention, Ill decken die asynchrone Version zuerst. Dieses sollte nicht für alles außer dem anderen Spieler gefragt werden, der um einen Handel bittet, der unserem eigenen Spieler gegeben wird, wenn man auf die API-Funktionen schaut, benutzt einen synchronen Anruf: Ein Monitor wird aufgestellt, um uns zu erlauben, das andere Sterben zu behandeln, und Wird sein ref in den FSMs-Daten zusammen mit den anderen pid gespeichert, bevor es in den Idlewait-Zustand geht. Beachten Sie, dass wir alle unerwarteten Ereignisse melden und sie ignorieren werden, indem wir in dem Zustand bleiben, in dem wir bereits waren. Wir können hier und da ein paar Out-of-Band-Nachrichten haben, die das Ergebnis der Racebedingungen sein könnten. Seine normalerweise sicher zu ignorieren, aber wir können nicht leicht loszuwerden. Es ist einfach besser, nicht die gesamte FSM auf diese unbekannten, aber etwas erwarteten Nachrichten abstürzen. Wenn unser eigener Client die FSM bittet, einen anderen Spieler für einen Trade zu kontaktieren, sendet er ein synchrones Ereignis. Der idle3-Rückruf wird benötigt: Wir gehen ähnlich wie die asynchrone Version, außer wir müssen die andere Seite tatsächlich fragen, ob sie mit uns verhandeln wollen oder nicht. Youll bemerken, dass wir nicht auf den Client noch nicht antworten. Dies liegt daran, wir haben nichts Interessantes zu sagen, und wir wollen den Client gesperrt und warten, bis der Handel akzeptiert werden, bevor Sie etwas tun. Die Antwort wird nur gesendet, wenn die andere Seite einmal im Leerlauf war. Als wir dort waren, haben wir es mit den anderen zu tun, zu verhandeln und die anderen zu verhandeln (das Ergebnis einer Race Condition, wie im Protokoll beschrieben): Das gibt uns zwei Übergänge in den Verhandlungsstaat, aber bedenke, dass wir müssen Verwenden Sie genfsm: reply2 antworten Sie auf unseren Client, um ihm zu sagen, dass es okay ist, Angebot zu beginnen. Es gibt auch den Fall, dass unser FSM-Kunde den von der anderen Partei vorgeschlagenen Handel akzeptiert: Erneut bewegt sich dieser Schritt zum Verhandlungsstaat. Hier müssen wir asynchrone Abfragen behandeln, um Elemente hinzuzufügen und zu entfernen, die sowohl vom Client als auch vom anderen FSM kommen. Jedoch haben wir noch nicht entschieden, wie man Einzelteile speichert. Weil Im etwas faul und ich davon ausgehen, Benutzer werden nicht handeln, dass viele Artikel, einfache Listen wird es für jetzt tun. Allerdings könnten wir unsere Meinung zu einem späteren Zeitpunkt zu ändern, so wäre es eine gute Idee, Element Operationen in ihrer eigenen Funktionen zu wickeln. Fügen Sie die folgenden Funktionen am unteren Rand der Datei mit notice3 und unexpected2: Simple, aber sie haben die Rolle zu isolieren, die Aktionen (Hinzufügen und Entfernen von Elementen) aus ihrer Umsetzung (mit Listen). Wir könnten leicht zu Proplisten, Arrays oder beliebige Datenstruktur verschieben, ohne den Rest des Codes zu stören. Mit diesen beiden Funktionen können wir das Anbieten und Entfernen von Objekten implementieren: Dies ist ein hässlicher Aspekt, asynchrone Nachrichten auf beiden Seiten zu verwenden. Ein Satz von Nachricht hat die Form machen und zurückziehen, während die andere hat und rückgängig machen. Dies ist völlig willkürlich und wird nur verwendet, um zwischen Player-zu-FSM-Kommunikation und FSM-zu-FSM-Kommunikation zu unterscheiden. Beachten Sie, dass bei denjenigen, die von unserem eigenen Spieler kommen, wir die andere Seite über die Änderungen erzählen müssen. Eine andere Verantwortlichkeit ist, die areyouready Mitteilung zu behandeln, die wir im Protokoll erwähnt haben. Dieses ist das letzte asynchrone Ereignis, das im Verhandlungszustand behandelt werden soll: Wie im Protokoll beschrieben, wann immer nicht im Wartezustand war und diese Nachricht empfangen wird, müssen wir mit notyet antworten. Waren auch die Ausgabe von Handelsdaten an den Benutzer, so dass eine Entscheidung getroffen werden kann. Wenn eine solche Entscheidung getroffen wird und der Benutzer bereit ist, wird das fertige Ereignis gesendet. Dieses sollte synchron sein, weil wir nicht möchten, dass der Benutzer sein Angebot modifiziert, indem er Elemente hinzufügt, während er behauptet, dass er bereit ist: An diesem Punkt sollte ein Übergang zum Wartezustand erfolgen. Beachten Sie, dass nur das Warten auf das andere ist nicht interessant. Wir speichern die Variable From, damit wir sie mit genfsm: reply2 verwenden können, wenn wir dem Client etwas mitteilen müssen. Der Wartezustand ist ein komisches Tier. Neue Elemente können angeboten und zurückgezogen werden, da der andere Benutzer möglicherweise nicht bereit ist. Es ist dann sinnvoll, automatisch in den Verhandlungsstaat zurückzukehren. Es würde saugen, große Gegenstände zu uns geboten zu haben, nur für die anderen, um sie zu entfernen und erklären sich bereit, stehlen unsere Beute. Zurück zu Verhandlungen ist eine gute Entscheidung: Jetzt ist das etwas Sinnvolles und wir antworten dem Spieler mit den Koordinaten, die wir in Sstate. from gespeichert haben. Der nächste Satz von Nachrichten, die wir uns darum kümmern müssen, sind die, die mit dem Synchronisieren von beiden FSMs zusammenhängen, damit sie in den Bereitschaftszustand übergehen und den Handel bestätigen können. Dafür sollten wir uns auf das zuvor definierte Protokoll konzentrieren. Die drei Nachrichten, die wir haben konnten, sind schon (weil der andere Benutzer sich nur bereit erklärt hat), notyet (weil wir den anderen gefragt haben, ob er bereit war und er nicht war) und bereit (weil wir den anderen fragten, ob er bereit war und er war ). Nun beginnen mit areyouready. Denken Sie daran, dass im Protokoll haben wir gesagt, dass es könnte eine Rasse Zustand versteckt dort. Das einzige, was wir tun können, ist senden Sie die fertige Nachricht mit amready1 und befassen sich mit dem Rest später: Well be stuck wieder warten, so ist es nicht lohnt sich auf unsere Client noch nicht. Ähnlich antworten wir nicht auf den Klienten, wenn die andere Seite ein notyet zu unserer Einladung sendet: Auf der anderen Seite, wenn das andere bereit ist, schicken wir eine zusätzliche bereite Mitteilung zum anderen FSM, antworten auf unseren eigenen Benutzer und bewegen dann zu Die bereit Status: Sie haben vielleicht bemerkt, dass Ive verwendet acktrans1. In der Tat sollten beide FSMs es verwenden. Warum ist dies Um dies zu verstehen, müssen wir anfangen zu betrachten, was in den Bereitschaftszustand geht. Wenn im Bereitschaftszustand, werden beide Spieler Aktionen nutzlos (außer Annullierung). Wir interessieren uns nicht für neue Einzelteilangebote. Das gibt uns einige Freiheit. Grundsätzlich können beide FSMs frei miteinander reden, ohne sich um den Rest der Welt zu kümmern. Damit können wir unsere Bastardisierung eines zweiphasigen Commits realisieren. Um diese Verpflichtung zu beginnen, ohne dass ein Spieler handeln muss, braucht es ein Ereignis, um eine Aktion aus den FSMs auszulösen. Das ack-Ereignis von acktrans1 wird dafür verwendet. Sobald in den Bereitschaftszustand, die Nachricht behandelt wird und handeln auf die Transaktion kann beginnen. Zwei-Phasen-Commits erfordern jedoch eine synchrone Kommunikation. Dies bedeutet, wir können nicht beide FSMs die Transaktion auf einmal, weil sie am Ende deadlocked. Das Geheimnis ist, einen Weg zu finden, zu entscheiden, dass eine endliche Zustandsmaschine das Commit initiieren sollte, während die andere sitzt und auf Aufträge vom ersten abwartet. Es stellt sich heraus, dass die Ingenieure und Informatiker, die Erlang entworfen hatten, ziemlich schlau waren (gut, das wussten wir schon). Die Pids jedes Prozesses können miteinander verglichen und sortiert werden. Dies kann getan werden, egal, wann der Prozess hervorgebracht wurde, ob er noch lebt oder nicht, oder ob er von einer anderen VM kommt (sehen Sie mehr darüber, wenn wir in verteilte Erlang bekommen). Wenn wir wissen, dass zwei PIDs verglichen werden können und eine größer als die andere ist, können wir eine Funktion priority2 schreiben, die zwei PIDs annimmt und einem Prozess sagt, ob er gewählt wurde oder nicht: Und indem wir diese Funktion aufrufen, können wir einen Prozess starten Das Commit und das andere nach den Aufträgen. Heres, was dieses gibt uns, wenn in den bereiten Zustand eingeschlossen, nach dem Empfangen der ack Mitteilung: Dieser große Versuch. Catch Ausdruck ist die führende FSM entscheiden, wie das Commit funktioniert. Sowohl askcommit1 als auch docommit1 sind synchron. Damit lässt sich die führende FSM frei nennen. Sie können sehen, dass die andere FSM einfach geht und wartet. Er wird dann die Aufträge vom führenden Prozess erhalten. Die erste Nachricht sollte askcommit sein. Dies ist nur um sicherzustellen, dass beide FSMs sind immer noch nichts falsch passiert, theyre beide gewidmet, um die Aufgabe abgeschlossen: Sobald dies empfangen wird, wird der führende Prozess fragen, um die Transaktion mit docommit zu bestätigen. Das ist, wenn wir unsere Daten verpflichten müssen: Und sobald seine getan, gehen wir. Die führende FSM erhält ok als Antwort und wird wissen, um an seinem eigenen Ende danach zu begehen. Das erklärt, warum wir den großen Versuch brauchen. Fang. Wenn der antwortende FSM stirbt oder sein Spieler die Transaktion annulliert, werden die synchronen Anrufe nach einem Timeout abstürzen. Das Commit sollte in diesem Fall abgebrochen werden. Nur so dass Sie wissen, definierte ich die Commit-Funktion wie folgt: Ziemlich underwhelming, eh Seine in der Regel nicht möglich, ein echtes sicheres Commit mit nur zwei Teilnehmermdasha Dritten zu tun ist in der Regel erforderlich, um zu beurteilen, ob beide Spieler alles richtig gemacht. Wenn Sie eine echte Commit-Funktion schreiben wollten, sollte sie sich mit dieser dritten Partei im Namen der beiden Spieler in Verbindung setzen und dann das sichere Schreiben in eine Datenbank für sie durchführen oder den gesamten Austausch zurücksetzen. Wir gehen nicht in solche Details und die aktuelle Comm1-Funktion wird genug für die Bedürfnisse dieses Buches. Waren noch nicht fertig. Wir haben noch nicht zwei Arten von Ereignissen abgedeckt: ein Spieler, der den Handel annulliert und die anderen Spieler finite Zustand Maschine Absturz. Erstere können mit den Callbacks handleevent3 und handlesyncevent4 behandelt werden. Immer wenn der andere Benutzer annulliert, erhalten Sie eine asynchrone Benachrichtigung: Wenn wir es tun, dürfen wir nicht vergessen, das andere zu erklären, bevor wir uns verlassen: Und voil Das letzte Ereignis zu kümmern ist, wenn die andere FSM sinkt. Glücklicherweise hatten wir einen Monitor im Ruhezustand zurückgesetzt. Wir können dazu passen und entsprechend reagieren: Beachten Sie, dass selbst wenn die Abbruch - oder DOWN-Ereignisse passieren, während sie sich im Commit befinden, alles sicher sein sollte und niemand seine Items gestohlen bekommen sollte. Hinweis: Für die meisten unserer Nachrichten verwendeten wir io: format2, damit die FSMs mit ihren eigenen Clients kommunizieren können. In einer realen Welt-Anwendung möchten wir vielleicht etwas flexibler als das. One way to do it is to let the client send in a Pid, which will receive the notices sent to it. That process could be linked to a GUI or any other system to make the player aware of the events. The io:format2 solution was chosen for its simplicity: we want to focus on the FSM and the asynchronous protocols, not the rest. Only two callbacks left to cover Theyre codechange4 and terminate3. For now, we dont have anything to do with codechange4 and only export it so the next version of the FSM can call it when itll be reloaded. Our terminate function is also really short because we didnt handle real resources in this example: We can now try it. Well, trying it is a bit annoying because we need two processes to communicate to each other. To solve this, Ive written the tests in the file tradecalls. erl. which can run 3 different scenarios. The first one is mainab0. It will run a standard trade and output everything. The second one is maincd0 and will cancel the transaction halfway through. The last one is mainef0 and is very similar to mainab0. except it contains a different race condition. The first and third tests should succeed, while the second one should fail (with a crapload of error messages, but thats how it goes). You can try it if you feel like it. If youve found this chapter a bit harder than the others, I must remind you that its entirely normal. Ive just gone crazy and decided to make something hard out of the generic finite-state machine behaviour. If you feel confused, ask yourself these questions: Can you understand how different events are handled depending on the state your process is in Do you understand how you can transition from one state to the other Do you know when to use sendevent2 and syncsendevent2-3 as opposed to sendallstateevent2 and syncsendallstateevent3. If you answered yes to these questions, you understand what genfsm is about. The rest of it with the asynchronous protocols, delaying replies and carrying the From variable, giving a priority to processes for synchronous calls, bastardized two-phase commits and whatnot are not essential to understand . Theyre mostly there to show what can be done and to highlight the difficulty of writing truly concurrent software, even in a language like Erlang. Erlang doesnt excuse you from planning or thinking, and Erlang wont solve your problems for you. Itll only give you tools. That being said, if you understood everything about these points, you can be proud of yourself (especially if you had never written concurrent software before). You are now starting to really think concurrently. In a real game, there is a lot more stuff going on that could make trading even more complex. Items could be worn by the characters and damaged by enemies while theyre being traded. Maybe items could be moved in and out of the inventory while being exchanged. Are the players on the same server If not, how do you synchronise commits to different databases Our trade system is sane when detached from the reality of any game. Before trying to fit it in a game (if you dare), make sure everything goes right. Test it, test it, and test it again. Youll likely find that testing concurrent and parallel code is a complete pain. Youll lose hair, friends and a piece of your sanity. Even after this, youll have to know your system is always as strong as its weakest link and thus potentially very fragile nonetheless. Dont Drink Too Much Kool-Aid: While the model for this trade system seems sound, subtle concurrency bugs and race conditions can often rear their ugly heads a long time after they were written, and even if theyve been running for years. While my code is generally bullet proof (yeah, right), you sometimes have to face swords and knives. Beware the dormant bugs. Fortunately, we can put all of this madness behind us. Well next see how OTP allows you to handle various events, such as alarms and logs, with the help of the genevent behaviour. Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution Non-Commercial No Derivative LicenseTrading Systems Expert Kenny Stone develops front and back office trading systems for Connamara Systems, LLC, with a prior background in C and assembly for embedded telecommunications products. His experience at Connamara has been varied, building low latency CC trading applications as well as web applications with Ruby on Rails and Sinatra. As a senior engineer at Connamara, Kenny is tasked with pairing the right technology for the problem at hand, and he believes that Erlang offers a compelling choice for many solutions in financial software engineering. Connamara Systems provides end-to-end trading systems development including order and execution management, algorithmic trading, exchange connectivity and market data integration. Our clients are top-tier futures, options and equities trading companies. Kenny Stone is Giving the Following Talks The Erlang Stock Exchange One year ago, Joel Reymont challenged the Erlang community to build an open source stock market. In this talk, youll see the result of my effort to build it - the triumphs, the frustrations, and the benchmarks and I will directly compare the Erlang solution to a Java solution we built for a client.


No comments:

Post a Comment