Bobyl bestätigt PHP. SOA: Erstellen Sie einen äußerst zuverlässigen, fehlertoleranten Webdienst in PHP anders als Sie es gewohnt sind. So führen Sie paralleles Rechnen in SOA durch

Ich erstelle eine einfache Liste in PHP, in der der Benutzer Name, Alter usw. hinzufügen kann. E-Mails usw. Ich habe auch eine Löschoption hinzugefügt, möchte aber eine Bestätigungsmeldung hinzufügen, wenn der Benutzer auf die Schaltfläche „Löschen“ klickt.

Ich habe versucht, bei Google zu suchen, habe aber nur jQuery- und JavaScript-Lösungen gefunden. Gibt es eine Möglichkeit, dies nur mit PHP zu tun?

Löschen.php

V

Wenn Sie dies nur in PHP tun möchten, müssen Sie Ihrem Skript „Schritte“ hinzufügen, etwa so:

Schritt 1 (Formular anzeigen) -> Schritt 2 (Validierung erfragen) -> Schritt 3 (Validieren)

Dazu können Sie Sitzungen verwenden, um den Formularinhalt zu speichern, und einen GET-Parameter verwenden, um diesen Schritt zu verfolgen. Ansonsten am meisten einfache Lösung ist die Verwendung von Javascript:

Echo" X"; //verwende doppelte Anführungszeichen für js innerhalb von PHP!

Das ist es, was Sie brauchen

While($query2=mysql_fetch_array($query1)) ( echo " ".$query2["name"].""; Echo " ".$query2["Alter"].""; Echo " Bearbeiten"; Echo " X"; ) in while($query2=mysql_fetch_array($query1)) ( echo " ".$query2["name"].""; Echo " ".$query2["Alter"].""; Echo " Bearbeiten"; Echo " X"; }

und erstellen Sie eine Javascript-Funktion

Funktion bestätigenDelete(Anchor) ( var conf = bestätigen("Möchten Sie diesen Datensatz wirklich löschen?"); if(conf) window.location=anchor.attr("href"); )

vertrau mir, es ist Arbeit :)

Fügen Sie ein onClick-Ereignis hinzu, um das Dialogfeld aufzurufen, und javascript:return bestätigen("Sind Sie sicher, dass Sie dies löschen möchten?");

Echo" X";

function deletetconfig())( var del=confirm("Sind Sie sicher, dass Sie diesen Datensatz löschen möchten?"); if (del==true)( Alert ("Datensatz gelöscht") ) return del; ) //onclick-Ereignis hinzufügen onclick = „return deletetconfig()“

funktioniert für mich, aber ändere Folgendes:

Onclick="javascript:confirmationDelete($(this));return false;"

Onclick="confirmationDelete(this);return false;"

Unten finden Sie eine Variante des oben Gesagten, die ein Bestätigungsfeld enthält und die Variable von PHP an Javascript und zurück an PHP übergibt.
Ich habe dies verwendet, um ein Optionsfeld auszuwählen, um eine Datei aus einer Dateiliste zu entfernen.
Sehen Sie sich die OnClick-Triggerfunktion namens php $fileName in Javascript an, bestätigen Sie mit dem Dateinamen und übergeben Sie, wenn ja, mit Variablen für $_GET an href

PHP/HTML-Code:

In dem Artikel geht es nicht um Cluster, nicht um Sharding mit Replikation und nicht einmal um Clouds. In dem Artikel geht es um den Aufbau eines äußerst zuverlässigen Systems Computerarchitektur, in dem die Zahl der Nutzer und deren Anfragen lawinenartig ansteigen kann. Und für Unternehmen ist es von entscheidender Bedeutung, dass der Webdienst jede Anfrage annimmt, sie korrekt und bis zum Ende verarbeitet (unabhängig von Ausfällen und Ausfällen einiger Komponenten) und dem Kunden garantiert eine Antwort liefert. Und natürlich ohne die „astronomischen“ Kosten für Ausrüstung und Gehälter für Systemadministratoren.

Mit anderen Worten: Denken Sie zunächst darüber nach: „Brauche ich das?“ Wenn jemand einen Online-Shop hat, der sprechende Hamster mit einem Umsatz von 100 Bestellungen pro Monat verkauft, dann wahrscheinlich nicht. Und wenn Sie planen, ein Unternehmen zu leiten, das Hunderttausende und Millionen von Benutzern aufnehmen kann, eine große Menge an Berechnungen erfordert, mit äußerst wertvollen Daten arbeitet, die Transaktionalität jedes Geschäftsprozesses garantiert und eine parallele Datenverarbeitung erfordert, dann ist dies das Richtige für Sie .

Finanzsektor, große Online-Shops mit einem Bestand von Hunderttausenden Einheiten, Online-Auktionen, Hotel- und Flugticketbuchungssysteme, neue „Cloud“ bzw soziale Dienste mit Ambitionen, am nächsten Tag nach Beginn einer Werbekampagne eine millionste Nutzerbasis zu erreichen – potenziell interessiert an einem äußerst zuverlässigen System für ihre Webdienste.

An wen richtet sich dieses Material?

1. Entwickler großer Webprojekte, die daran interessiert sind, hochlastige und fehlertolerante Computerdienste zu erstellen.

2. Eigentümer neuer oder expandierender Unternehmen, die ein „explosives“ Wachstum der Benutzerbasis erwarten und hohe Anforderungen an den Computerbereich stellen.

3. Technische Leiter und Manager großer Webprojekte, die mit dem Ist-Zustand nicht zufrieden sind und über eine grundlegende Neuorganisation nachdenken.

Warum wird so viel über „Computing“ geredet?

Denn die nahe Zukunft großer Webprojekte liegt im Bereich „Big Data“ – ein Trend, der bereits 2011 neben Virtualisierung, Energieeinsparung und Monitoring zu den Toptrends zählte und seit 2013 auch dabei ist Es hat sich in der Branche fest etabliert und wurde sogar zu einem der akademischen Fächer an großen ausländischen Universitäten.

Das Datenvolumen, das zur Lösung geschäftlicher Probleme verarbeitet werden muss, nimmt heute kontinuierlich zu und dieser Prozess wird sich in Zukunft noch beschleunigen. Während es vor zehn Jahren ausreichte, dem Nutzer ein „Online-Showcase“ mit einem Produkt zu zeigen, reichte es vor einem Jahr aus, den Weg des Nutzers durch die Seite zu analysieren und ihm ein streng relevantes Produkt zu zeigen (sogenannte „Verhaltenstechnologien“). , aber heute gilt es als die Norm, alles über den Benutzer zu wissen, einschließlich Größe, Gewicht, Alter und den Namen Ihres Lieblingshundes.

Die Gesetze des natürlichen Wettbewerbs schreiben vor, dass Sie mehr Daten und mehr Rechenleistung benötigen, wenn Sie Ihren Kunden mehr Wert als Ihre Konkurrenten bieten möchten. Und früher oder später könnte Ihr Projekt ohne die richtigen Ansätze darin untergehen – so wie verschiedene Projekte jetzt überall untergehen: manche wegen der Komplexität der Unterstützung, manche wegen einfach schlechtem Code, manche wegen der hohen Kopplung von Modulen , teilweise aufgrund der Verwendung unzuverlässiger Komponenten.

Daher wird jeder, der heute mit großen Ambitionen ein Webprojekt startet, gegenüber seinen Mitbewerbern mehr Vorteile erzielen, wenn er sein Projekt zunächst nicht nur als Programmcode, sondern auch als Computerumgebung betrachtet – ein System mit eigenen Existenz- und Entwicklungsgesetzen. Je mehr Aufmerksamkeit Sie schenken Computermanagement Je mehr Chancen Sie am Anfang haben, desto größer sind Ihre Chancen, Ihre Konkurrenten in den kommenden Jahren zu überholen.

Der Autor dieser Zeilen ist überzeugt, dass der „klassische“ moderne Ansatz zum Aufbau hochlastiger Webdienste eine Reihe schwerwiegender Mängel aufweist. Lassen Sie uns herausfinden, warum. Schauen wir uns zunächst ein typisches modernes Schema an:

Klassischer Ansatz zum Aufbau eines hochlastigen Webdienstes

1. Viele Server sind in Rollen unterteilt.

2. Ein Teil der Server (Frontend-Rolle) dient dazu, statische Ressourcen (Bilder, CSS, JS-Dateien) bereitzustellen und die Front des eingehenden Datenverkehrs auf untergeordnete Knoten zu „verteilen“. Die Hauptsoftware ist normalerweise Nginx.

3. Downstream-Knoten (Backend-Rolle) sind an dynamischen Berechnungen beteiligt. Einfach ausgedrückt könnte dies eine typische Apache+PHP-Kombination sein.

4. Eine weitere Gruppe von Servern ist für die Datenspeicherung vorgesehen. Dies sind MySQL, Memcache, Redis und so weiter.

5. Der Webservice-Code selbst (in in diesem Beispiel– PHP-Code) wird gleichermaßen auf alle Knoten kopiert, auf denen sich Apache+PHP befindet, und verarbeitet die Anfragen, die bei einem bestimmten Knoten „ankommen“, gleichermaßen.

6. Mithilfe einer Form des Shardings werden Datenbanken über ihre Servergruppe „verteilt“ und die Belastung auf ihnen wird auf ähnliche Weise ausgeglichen.

Ein aufmerksamer Leser wird feststellen, dass DNS-Balancing und CDN im Diagramm nicht erwähnt werden, der Autor sie jedoch ausdrücklich weggelassen hat, um das Schema nicht zu komplizieren. Das Funktionsprinzip dieser Dinge ist den oben aufgeführten sehr ähnlich.

Nachteile des klassischen Ansatzes

1. Der Haupttrend sind Komplikationen. Im Laufe der Projektlaufzeit wird das „klassische“ Schema immer komplizierter. Wenn Sie jeden neuen Server hinzufügen, müssen Sie ihn in das Verkehrsverteilungsschema eintragen. Natürlich ist die Einführung eines Servers in eine Rolle bei großen Projekten eine „Ein-Knopf“-Aktion, aber dennoch stellt dies eine Erweiterung der Infrastruktur dar, die unterstützt werden muss. Dies gilt umso mehr für den Fall, dass die aktuelle Kapazität der Datenbank nicht mehr ausreicht und Sie die Datenbank „lebend“ (ohne den Dienst zu stoppen) übertragen oder auf neue Server verteilen müssen.

Das wichtigste Problem besteht jedoch darin, dass die Erweiterung des Traffic-Verteilungsclusters nicht zu einer Verringerung der Komplexität des Programmcodes führt. Sie können einen Cluster problemlos erstellen, der Code bleibt jedoch derselbe.

2. Die Bereitstellung ist nicht atomar. Im Klartext: Auslegen neue Version Das Projekt auf Produktionsservern dauert einige Zeit. Sie müssen Dateien physisch auf N-twenty-Maschinen herunterladen, Änderungen an der Datenbank vornehmen, Caches zurücksetzen (viele verschiedene Caches) und gleichzeitig auf allen Servern die Verarbeitung von Anforderungen mit dem „alten Code“ beenden und mit der Verarbeitung mit dem „neuen“ beginnen Code". Andernfalls kann es zu vielen kleinen Konflikten kommen, wenn ein Teil der Anfrage des Benutzers auf die alte Weise verarbeitet wird, ein Teil auf eine neue Weise, ein Teil nach dem „alten Schema“ in die Datenbank gelangt ist, ein Teil nach dem „neuen“ und so weiter An.

Daher möchte jeder im Idealfall den Dienst für die Dauer des Updates (einige Sekunden oder Dutzende Sekunden) pausieren und ihn dann wieder einschalten. In Wirklichkeit tut dies bei einem Fluss von mindestens 1000 Anfragen pro Sekunde niemand und zieht es vor, kleinere Kollisionen regelmäßig „von Hand“ zu korrigieren oder sie mit einer Schutzprogrammierung abzudecken, die die Abwärtskompatibilität „bis zur siebten Generation“ unterstützt. Ein erfahrener Leser kann selbst darüber nachdenken, wie die regelmäßige Unterstützung der Abwärtskompatibilität das Leben von Programmierern erschwert (und die Kosten des Projekts insgesamt erhöht).

3. HTTP wird verwendet. Das HTTP-Protokoll ist offensichtlich moralisch und technisch veraltet, und wenn Sie (zum Beispiel) folgen mobile Entwicklung– Sie wissen, dass es weitgehend durch einfachere Protokolle ersetzt wird. Der Hauptnachteil ist jedoch ein anderer: Das HTTP-Protokoll „im Browser“ erfordert den Abschluss der Schleife – es erfordert eine Antwort in einer begrenzten Zeit. Dies verpflichtet den Dienst dazu, eine Antwort streng innerhalb der vom Browser zugelassenen kurzen Zeitspanne zu berechnen und vorzubereiten. Wenn der Dienst aktuell überlastet ist, geht die Anfrage für immer verloren.

Daher greifen sie in „typischen Webprojekten“ auf verschiedene Tricks wie Long Polling oder andere Formen periodischer Anfragen zurück, was nicht nur die Architektur verkompliziert, sondern auch den Dienst mit unnötigem „verschwendetem Zucken“ überlastet.

4. Initialisierung des Skripts für jede Anfrage. Dies ist eine Folge der Verwendung von HTTP und Skriptsprachen wie PHP, die einer alten Tradition zufolge bei jeder Anfrage neu gestartet werden. Ja, ja, bei jeder der 1000 Anfragen pro Sekunde startet das PHP-Skript neu, initialisiert alle Variablen erneut und stellt erneut Verbindungen zur Datenbank her. In der Praxis kommt es vor, dass die Bearbeitung einer Anfrage 0,005 Sekunden dauert, das Skript jedoch in etwa 0,05 Sekunden initialisiert wird – zehnmal länger!

Mit anderen Worten: 90 % der Zeit sind Ihre Server damit beschäftigt, keine Client-Anfragen zu verarbeiten, sondern nutzlose Initialisierungsskripts zu verwenden. Versuchen Sie, dies in Geld umzusetzen. Daher wurden viele Workarounds erfunden, wie z. B. OPcode-Caching, dauerhafte Verbindungen zur Datenbank, lokale Caches wie Memcache oder Redis, um diesen unangenehmen Effekt abzumildern.

5. Monolithische Anwendung. Egal wie Sie die Anwendung in Module unterteilen, egal wie sehr Sie versuchen, den Code über eine komplexe Verzeichnisstruktur zu verteilen, egal welche Art von verzögertem Autoloading Sie verwenden, es gibt nur ein Kriterium: ob Sie die gesamte Anwendung hochladen müssen Um mindestens eine Änderung zu veröffentlichen, haben Sie eine monolithische Anwendung. Es gibt keine andere Möglichkeit.

Die Nachteile monolithischer Anwendungen werden in der Literatur ausführlich beschrieben. Einen der wichtigsten Punkte können wir kurz erwähnen: Wenn Sie möchten, dass selbst das kleinste Feature innerhalb einer Stunde in Produktion geht, müssen Sie die gesamte Produktionskette in einer Stunde unterbringen. Problemformulierung, Implementierung, Überprüfung der Abwärtskompatibilität, Schreiben von Tests, Schreiben von Dokumentationen, Durchlaufen der manuellen Testabteilung, Beheben von Fehlern – alles innerhalb einer Stunde.

Denn wenn Sie die gesamte Anwendung genau zu 00 Minuten jeder Stunde hochladen, sollten Sie am Ende jeder Stunde ein Ergebnis erhalten gesamte Anwendung in einen stabilen Zustand.

6. Die Weboberfläche wird vom Backend gerendert. In einem typischen Fall ist Aussehen(und dementsprechend der HTML-Code) der Projektseiten werden in der Regel als Antwort auf jede Anfrage auf der Backend-Seite gerendert. Dies ist ein übermäßiger, ungerechtfertigter Aufwand an Ressourcen und Geld.

7. Politische Aufteilung der Abteilungen. Abteilung Systemadministratoren ist dafür verantwortlich, dass der eingehende Datenverkehr auf eine Reihe von Servern „verteilt“ wird, auf denen der PHP-Code ausgeführt wird. Für den PHP-Code ist die Programmierabteilung zuständig. Wenn der PHP-Code keine Zeit hatte, eine bestimmte Anfrage zu verarbeiten, ist unklar, wer dafür verantwortlich ist – entweder der Administrator, der zu viel Datenverkehr an den Server „gesendet“ und ihn überlastet hat, oder der Programmierer, der ein suboptimales Skript geschrieben hat. Wenn die Datenbank langsamer wird, ist auch unklar, wer am Extrem bleibt: der Administrator, der nicht rechtzeitig herausgefunden hat, wie er es „herausfindet“, oder der Programmierer, der es auch hätte herausfinden können.

Wenn Ihnen die Beispiele übertrieben erscheinen und „nicht aus echtes Leben„Denken Sie daran, dass der Autor an einem Projekt gearbeitet hat, das auf 200 Servern gehostet wurde und 60 davon für Datenbanken verwendet wurden. Es ist beängstigend, sich daran zu erinnern, wie viele Leute beschäftigt waren, um dieses Projekt aufrechtzuerhalten.

Hauptnachteil

Wiederholen wir die obige Idee: Der Hauptnachteil des klassischen Schemas besteht laut Autor darin, dass technische Spezialisten das Falsche optimieren. Sie optimieren die eingehenden Anfragen, indem sie sie tatsächlich auf eine große Gruppe von Maschinen „verteilen“, anstatt das eigentliche Wesentliche zu optimieren – den Rechenteil. Und das ist viel einfacher als es scheint.

Theoretisches Ideal

Oh, wir wünschten, wir könnten:

1. Verzichten Sie auf eine Reihe teurer Server und nutzen Sie ein oder zwei kleine Gruppen.

2. Geben Sie das Nginx->Apache->PHP-Schema mit seinem wilden „Overhead“ in Bezug auf verbrauchte Ressourcen auf, der Geld kostet.

3. Eliminieren Sie aus dem gleichen Grund die enormen Kosten für die Initialisierung von PHP-Skripten.

4. Eliminieren Sie die Notwendigkeit, Seiten im Backend zu „rendern“. Es wäre ein absoluter Traum, wenn ein Webdienst funktionieren könnte, wenn die Internetverbindung instabil ist oder fehlt (z. B. bei der Nutzung von Mobilfunknetz auf der Straße).

5. Beseitigen Sie die HTTP-Timeout-„Schleife“ und liefern Sie die Antwort erst dann an den Client, wenn diese Antwort bereit ist, und mit einer Zustellungsgarantie.

6. Aktualisieren Sie das Projekt in kleinen Teilen, ohne anzuhalten und ohne eine einzige Kundenanfrage zu verlieren.

7. Machen Sie sich keine Sorgen über verlorene Anfragen, wenn ein Teil des Projekts (eine Komponente) „abstürzt“ oder zu Debugzwecken vorübergehend deaktiviert wurde.

Unwirklich? Leicht!

Erste Schritte zum Ideal

Lassen Sie uns zunächst verstehen Was Wir müssen es tun, also lasst uns darüber diskutieren – Wie.

1. Entwerfen Sie das gesamte System als SOA (serviceorientierte Architektur) mit einem ESB (Enterprise Messaging Bus) und geben Sie dabei den monolithischen Ansatz auf, sodass jeder unabhängige Teil der Geschäftslogik von einem separaten „Dienst“ verarbeitet wird und mit ihm kommuniziert untereinander über einen unabhängigen Austauschbus.

2. Synchronizität ablehnen. In einem synchronen „Anfrage-Verarbeitung-Antwort“-Schema handelt es sich beispielsweise um eine HTTP-Schleife, deren Abschluss nicht streng kontrolliert wird und die leicht unterbrochen werden kann. Im asynchronen Prozess gibt es drei separate Prozesse: Anfrage (gesendet und bestätigt), Verarbeitung (mit Wiederholung im Fehlerfall), Antwortzustellung (mit Garantie).

3. Teilen Sie das Projekt in zwei Anwendungen auf – Frontend und Backend. Im Falle eines Webservices ist das Frontend (normalerweise) eine JavaScript-Anwendung. Die Idee besteht darin, dass Anwendungen asynchron und voneinander entkoppelt arbeiten und Nachrichten über ein bidirektionales Kommunikationsprotokoll austauschen.

4. Geben Sie HTTP zugunsten von WebSocket auf. Das WebSocket-Protokoll verfügt im Vergleich zu HTTP über eine fantastische Geschwindigkeit, hat keine „Schleifen mit Zeitüberschreitungen“ und ermöglicht die Übertragung beliebiger Daten (einschließlich Binärdaten) in beide Richtungen.

5. Stellen Sie „Speicher“ für laufende Anfragen bereit. Sobald die Anfrage vom Kunden angenommen wurde, sagen Sie ihm „Bestätigen“ und speichern Sie diese Anfrage. Sobald das Backend vom vorherigen Verarbeitungszyklus befreit ist, übergeben Sie die Anfrage an dieses. Während die Anfrage zwischen Back-End-Knoten „geht“, speichern Sie sie ab dem Moment, in dem sie den Knoten „betritt“, und schreiben Sie sie fest, sobald sie den Knoten „verlässt“. Wenn also ein Knoten „abfällt“, verliert das System die Anfrage nicht und sendet sie sofort zur Verarbeitung zurück. Nach Abschluss der Verarbeitung senden Sie das Ergebnis an den Kunden und speichern es, bis der Kunde „Bestätigen“ sagt.

6. Stellen Sie Parallelität für diejenigen Vorgänge sicher, die aus geschäftslogischer Sicht parallel ausgeführt werden können, und Konsistenz für diejenigen, die streng sequentiell ausgeführt werden müssen. Dieser Absatz wurde nicht mit dem Anspruch geschrieben, „Captain Offensichtlichkeit“ zu sein, sondern um zu zeigen, dass nicht jeder Geschäftsprozess „blind“ auf Multithread-Code übertragen werden kann.

7. Geben Sie das Schreiben von Drehbüchern zugunsten von „Dämonen“ auf. Ein Daemon ist ein Prozess, der einmal ausgeführt wird und dann ständig im Speicher „hängt“. Da es kontinuierlich ausgeführt wird, muss keine Zeit mit der Neuinitialisierung für jede Anfrage verschwendet werden. Mit Blick auf die Zukunft möchte ich sagen, dass der PHP-Daemon (bei Verwendung moderner PHP-Versionen) keine grundlegenden Unterschiede zu einem normalen PHP-Skript aufweist.

SOA-Designansatz

SOA – serviceorientierte Architektur – ist kein neuer Trend. Dieser modulare Ansatz zur Softwareentwicklung wurde bereits im letzten Jahrhundert von IBM initiiert und wird derzeit von Branchenführern unterstützt und gefördert, hauptsächlich bei Produkten der „Unternehmensklasse“ in den Sprachen .NET und JAVA.

Im klassischen Ansatz zur Programmierung von Webdiensten in PHP-Sprachen und ähnliches – Design beginnt mit Modellen, ihren Eigenschaften und Operationen auf ihnen. Modelle repräsentieren Objekte der realen Welt und Operationen repräsentieren Aktionen an Objekten. Wie die Praxis zeigt, ist die reale Welt jedoch viel vielfältiger und komplexer und lässt sich in der Sprache der Ereignisse und Reaktionen darauf viel effektiver beschreiben (der Beitrag ist diesem Thema ausführlicher gewidmet). #1593 mit Beschreibung und Beispielen).

Die reale Welt besteht aus Ereignissen, die gleichzeitig (in der Programmiersprache „parallel“) und weitgehend ohne unsere Beteiligung stattfinden und auf die verschiedene Reaktionen stattfinden oder nicht stattfinden. Reaktionen wiederum können Anlass für weitere Ereignisse sein. Die SOA-Architektur ist ideal für die „Real-World-Programmierung“, da es am bequemsten ist, mit Ereignissen, Verbindungen zwischen ihnen und Reaktionen darauf zu arbeiten. Außerdem, wann der richtige Ansatz zur Organisation der Architektur, und Ereignisse und Reaktionen darauf finden parallel statt, selbst wenn Sie eine „Single-Threaded“-Programmiersprache wie PHP verwenden.

Sie müssen ein SOA-Produkt entwerfen, das darauf basiert, welche Ereignisse in Ihrer Geschäftslogik auftreten, wie sie miteinander in Beziehung stehen oder aufeinander folgen sollten, welche Reaktionen als Reaktion auf bestimmte Ereignisse erfolgen sollten und wer genau diese oder andere Ereignisse verarbeiten wird. andere Veranstaltungen. Die Implementierung von Datenmodellen und Aktionen auf ihnen tritt in den Hintergrund (gekapselt in einem „Service“), und die Liste der „Services“ und der Interaktionsplan zwischen ihnen (Inter-Service-API) treten in den Vordergrund.

Implementierung: erste Näherung

1. Frontend als eigenständige Anwendung. Zur Implementierung eignet sich jedes gängige JavaScript-MVC-Framework. Aus der Praxis stelle ich jedoch fest, dass es für Sie schwierig werden wird, wenn Ihre Zähne zu schmerzen beginnen, wenn Sie die Wörter „JavaScript, MVC und Framework“ in einem Satz kombinieren. Die Anwendung muss in der Lage sein, alle ihre „Bildschirme“ ohne Zugriff auf das Backend darzustellen, dem Benutzer Navigation (Übergänge zwischen „Bildschirmen“) auch ohne Zugriff auf das Backend bereitzustellen und einen bidirektionalen Kommunikationskanal mit dem Backend aufrechtzuerhalten.

2. Einstiegspunkt für die WebSocket-Verbindung zum Backend. In NodeJS gibt es eine Reihe vorgefertigter Lösungen, die auch Fallback-Anfragen für Long-Polling und Ajax unterstützen ideologisch veraltet Browser. Ich werde für die Zukunft darauf hinweisen, dass auf diesen Knoten auch über reines HTTP zugegriffen werden kann, wenn Sie einige Gateways mit den Diensten anderer Leute schreiben müssen. Zur Vereinfachung wird es jedoch möglich sein, einen separaten „reinen HTTP“-Knoten zu schreiben.

Der Einstiegspunkt stellt einen bidirektionalen Kommunikationskanal mit der Frontend-Anwendung (also mit dem Browser) bereit, nimmt Anfragen von dieser entgegen und gibt Antworten an sie zurück.

3. Speicherung laufender Anfragen im System. Ideal hierfür ist der beliebte AMQP-Server, der Nachrichtenwarteschlangen und das Routing zwischen ihnen bereitstellt. Sobald die nächste Anfrage des Clients eintrifft, stellen Sie diese in die „eingehende“ Warteschlange. Als nächstes wird es vom Daemon aus dieser Warteschlange abgerufen, der den Inhalt der Anfrage analysiert und sie zum „Routing“ durch das System sendet (was eigentlich bedeutet, sie gemäß bestimmten Algorithmen an eine oder mehrere Warteschlangen zu übertragen). Jeder Daemon, der seinen Teil der Geschäftslogik verwaltet, empfängt die eine oder andere Nachricht aus „seiner“ Eingangswarteschlange, verarbeitet sie und platziert die Antwort in der „Ausgangswarteschlange“.

Ich stelle fest, dass es in der Terminologie des beliebten Brokers RabbitMQ kein Konzept für ausgehende Warteschlangen gibt. Nachrichten werden im Austausch (Exchanger) veröffentlicht, von wo aus der Broker sie gemäß den Routing-Regeln selbst an bestimmte Warteschlangen weiterleitet. Hier ist es zum bedingten Verständnis so geschrieben, dass die Antwort nicht direkt an den Anforderer gesendet wird.

4. Kontrolle von Dämonen (Supervisor). Um ein einfaches PHP-Skript als Daemon auszuführen, schließen Sie einfach den ausführbaren Code in while(true) (...) ein und geben Sie ein Befehlszeile so etwas wie „php your-script.php“. Es ist jedoch besser, hierfür einen geeigneten Vorgesetzten zu verwenden, der im Wesentlichen das Gleiche tut, aber auch den erforderlichen Zustand der Umgebung sicherstellt, den Zustand des Prozesses überwacht und einige andere nützliche Dinge tut.

Im wirklichen Leben ist ein PHP-Daemon etwas komplizierter: Er muss Steuersignale und Neukonfigurationsnachrichten empfangen, er darf keinen Speicher verlieren, er muss Verbindungen zur Datenbank aufrechterhalten (oder fehlgeschlagene wiederherstellen) – aber im Allgemeinen ist es nicht komplizierter als die PHP-Skripte, die Sie gewohnt sind.

Der Realität einen Schritt näher: ein ereignisgesteuerter SOA-Ansatz

Einige (nach Meinung des Autors veraltete) Ansätze zum Aufbau modularer Anwendungen basieren auf dem RPC-Prinzip (Remote Procedure Calling), das einen direkten Aufruf bestimmter Methoden oder Prozeduren in einer Remote-Projektkomponente impliziert. Dieser Ansatz macht alle Vorteile von SOA völlig zunichte, da es sich meist um eine direkte und starre Verbindung zwischen sendendem und ausführendem Knoten handelt. Beim Entwerfen und Implementieren eines komplexen Produkts sollten Sie sich so weit wie möglich an das Prinzip der lose gekoppelten Komponenten halten, da die Komplexität der Architektur und des Codes letztendlich die Betriebskosten (nachträgliche Korrekturen und Änderungen am Produkt) bestimmt seine Markteinführung).

Der ereignisorientierte Ansatz in SOA geht davon aus, dass Komponenten (Dienste) durch das Senden asynchroner Ereignisse („Ereignisse“, abgeleitet vom Wort „Event“) miteinander kommunizieren. Ein Ereignis ist eine Nachricht (z. B. in der AMQP-Terminologie), die einen Titel (Namen) und eine Reihe von Parametern hat. Ein Ereignis soll dem System mitteilen, dass etwas passiert ist, oder dem System „eine Frage stellen“. Im Allgemeinen werden Ereignisse unadressiert „an das System“ (genauer gesagt an den gemeinsamen ESB-Bus) gesendet, d. h. ohne spezifische Absicht zur Übermittlung an bestimmte Knoten oder Ausführer.

Im Gegenteil, bestimmte Knoten (Komponenten, Dienste) lauschen auf dem gemeinsamen Bus auf bestimmte Ereignisse, auf die sie reagieren können. Dies kann bedeuten, dass der Dienst bereit ist, ein Ereignis zu hören und die entsprechende Aktion auszuführen. Oder der Dienst verfügt über gewisse Kenntnisse (z. B. besitzt er eine Datenbank mit Informationen über Benutzer) und ist bereit, diese als Antwort auf eine „Anfrage“ bereitzustellen. In beiden Fällen generiert das Ergebnis der Reaktion auf das Ereignis ein neues Ereignis (mit anderem Namen und anderen Parametern), das auch von anderen daran interessierten Diensten gehört werden kann.

Bei richtiger Organisation des gemeinsamen ESB-Busses senden und empfangen Dienste Ereignisse asynchron, ohne aufeinander zu warten. Dies bedeutet, dass der Senderdienst ohne Zeitverzögerung beliebig viele Ereignisse an den ESB senden und mit der Lösung der nächsten Aufgaben fortfahren kann. Vergleichen Sie dies mit klassischem HTTP, bei dem im aktuellen Verarbeitungszyklus auf eine Antwort gewartet wird, und Sie werden die Vorteile verstehen. Und die empfangenden Dienste erhalten auch asynchron und unabhängig voneinander neue Ereignisse, unmittelbar nach Abschluss der Verarbeitung des vorherigen Ereignisses.

SOA-Ereignismodell im Programmcode

Kurz gesagt: Sie müssen Ihren Code nicht als Klassen mit Funktionen (Methoden) betrachten, sondern als Ereignisse und Aktionen, die als Reaktion auf diese Ereignisse stattfinden. Darüber hinaus sind die Ergebnisse von Handlungen auch Ereignisse. In Bezug auf die diskutierte Architektur können wir sagen, dass lokale Ereignisse Ereignisse sind, die innerhalb eines bestimmten PHP-Skripts aufgetreten sind, und Remote-Ereignisse Ereignisse sind, die aus der AMQP-Warteschlange in dieses Skript gelangten (oder als Ergebnis dorthin gesendet wurden). Wenn Sie Ihren gesamten Code auf diese Weise behandeln, führt dies sofort zu einem überraschenden und sehr wichtigen Effekt:

Wenn lokale und entfernte Ereignisse dasselbe sind, dann sind lokale und entfernte Handler dasselbe!

Warum ist das so wichtig? Weil die Programmierer in Ihrem Team weiterhin normalen PHP-Code schreiben, ohne darüber nachzudenken, wo dieses oder jenes Ereignis verarbeitet wird – genau dort in diesem oder einem benachbarten PHP-Skript oder irgendwo am anderen Ende des Systems, sogar in einem anderen Daemon auf einer anderen Programmiersprache. Wenn Sie ein Projekt mit einer öffentlichen API erstellen, kann jeder Drittteilnehmer seinen Code für Ihre Ereignisse „abonnieren“ (und sie verarbeiten) oder umgekehrt – Ihnen seinen Code senden, damit Sie seine Ereignisse als Anfragen verarbeiten (und dafür Geld erhalten, wenn Sie ein Pay-per-Use-SAAS-Geschäftsmodell nutzen, wie Amazon).

Denken Sie daran, was wir als den Hauptnachteil klassischer großer Webprojekte bezeichnet haben: kontinuierlich wachsend Komplexität und damit auch die Betriebskosten, die Kosten für Support und Änderungen. Im Fall einer ereignisgesteuerten SOA-Architektur – Der Schwierigkeitsgrad nimmt kontinuierlich ab, da „komplexe Knoten“ leicht in unabhängige Dienste (in diesem Fall Daemons) unterteilt werden können, während die Prinzipien des Systems unverändert bleiben und seine Leistung nur steigt.

Stellen Sie eine neue Version bereit, ohne aktuelle Prozesse zu verlieren

Da Sie kein monolithisches System mehr haben, müssen Sie nicht das gesamte System bereitstellen. Darüber hinaus kann die Bereitstellung einer Komponente (Dienst, Daemon) natürlich innerhalb angemessener Grenzen jederzeit Zeit in Anspruch nehmen. Wichtig ist, dass während der Bereitstellungszeit (diese wenigen Sekunden oder mehrere zehn Sekunden) der Komponente das gesamte Projekt keinen Moment lang den Dienst unterbricht. Wie wird das gemacht?

Sie schalten einfach den Dienst aus, der aktualisiert werden muss. Aktualisieren Sie den Code und die Datenbankstruktur (falls erforderlich) und führen Sie es dann erneut aus. Alle aktuellen Anfragen an diesen Dienst warten in der AMQP-Warteschlange, bis der Dienst verfügbar ist. Ich stelle fest, dass dies viel schneller geschieht als die Bereitstellung einer gesamten monolithischen Anwendung, da die Dienste klein sind (eine kleine Menge Code ist erforderlich, um nur einen kleinen Teil der Geschäftslogik zu lösen). Aber auf jeden Fall wird es keine Verluste geben.

Probleme mit der Weboberfläche

Eine schnelle, reaktionsfähige Weboberfläche ist Voraussetzung für ein Projekt mit hoher Auslastung. Lassen Sie uns herausfinden, warum das Webinterface mit dem klassischen Implementierungsansatz generell langsamer werden kann:

1. Die Schnittstelle wird im Backend gerendert, das überlastet ist und langsam rendert. Das Navigieren zwischen den Seiten ist langsam. Selbst mit AJAX werden Blöcke zu langsam neu gezeichnet.

2. Der Quellcode der Schnittstelle (HTML, CSS, JS) ist redundant und wird langsam über Kommunikationskanäle übertragen, insbesondere wenn dies beim Laden jeder Seite geschieht, während der Benutzer durch die Schnittstelle navigiert.

3. Die Schnittstelle enthält eine große Menge nicht optimierter JavaScript-Logik, die auf schwachen Geräten (hauptsächlich Mobilgeräten) langsam funktioniert.

Versuchen wir, diese Probleme zu lösen:

So erstellen Sie eine schnelle und reaktionsfähige Weboberfläche

1. Erstens und am wichtigsten: Quellcode Die Schnittstelle darf höchstens einmal an den Client übergeben werden. Der einzige zivilisierte Weg, dies zu erreichen, besteht darin, eine vollwertige JavaScript-Anwendung zu erstellen. Es wird einmal auf den Client heruntergeladen (gleichzeitig können Sie einen schönen animierten Preloader anzeigen), und dann muss der Client während der gesamten Zeit, in der Sie mit dem Dienst arbeiten, nicht mehr auf den Download warten.

2. Alle Übergänge zwischen „Bildschirmen“ der Weboberfläche müssen innerhalb der JavaScript-Anwendung und auf keinen Fall als separate Anfragen an das Backend erfolgen. Es gibt einen entsprechenden Begriff – „Single-Page-Webanwendung“, bei der die Navigation im Wesentlichen durch Wechseln von „Bildschirmen“ erfolgt, während sich der Inhalt der Adressleiste dynamisch ändert, wodurch das volle Gefühl einer klassischen „Seitennavigation“ entsteht.

3. Das Senden von Nachrichten (Ereignissen) an das Backend und das Empfangen von Antworten müssen voneinander und von der Benutzernavigation entkoppelt (asynchron) sein. Der oben erwähnte WebSocket „empfiehlt“ grundsätzlich eine solche Implementierung. Auch alle lang andauernden Vorgänge sollten die Schnittstelle nicht blockieren, sofern dies nicht ausdrücklich geschieht.

Somit benötigt der Benutzer nur für den ersten Download der Anwendung (einige Sekunden) eine Internetverbindung. Dann kann er auch bei vorübergehendem Verbindungsausfall (z. B. von einem mobilen Gerät in der U-Bahn, außerhalb der Stadt, in einem überfüllten Hotel im Ausland usw.) mit dem Dienst arbeiten – die Anwendung zeichnet Anfragen auf und versucht es sobald das Internet verfügbar ist, zu versenden und so auch Antworten zu erhalten.

Dies entbindet den Entwickler natürlich nicht von der Notwendigkeit, den Code zu optimieren und zu minimieren. Wie die Praxis zeigt (zum Beispiel der Trello-Dienst), ist diese Aufgabe jedoch nicht schwieriger als andere.

Hinweis für zweifelnde Entwickler von Webdiensten für mobile Geräte: Nach der Praxis des Autors funktionierten im Jahr 2013 Single-Page-JavaScript-Anwendungen, die den WebSocket-Transport nutzen, erfolgreich auf dem iPad.

Benutzererfahrung auf mehreren Geräten

Er nutzt Ihren Dienst von der Arbeit aus Desktop-Computer, auf dem Heimweg holt er sein iPhone heraus, und zu Hause schaltet er das Tablet ein. Wenn der Benutzer über die Schnittstelle einen Befehl an den Dienst gesendet hat, wartet er auf eine Antwort zu den Verarbeitungsergebnissen. Es ist leicht zu verstehen, dass die Antwort genau an das Gerät gesendet werden muss, das der Benutzer gerade verwendet (entschuldigen Sie das Wortspiel), wenn die Verarbeitung einige merkliche Zeit in Anspruch genommen hat Antwortlieferung und nicht zum Zeitpunkt der Anfrage.

Das Problem besteht darin, dass es unmöglich ist, eindeutig zu sagen, ob der Benutzer dies oder jenes nicht mehr verwendet (tut mir leid). spezifisches Gerät. Vielleicht hat er den Browser geschlossen. Vielleicht ist seine Batterie leer. Vielleicht ist er in einen U-Bahn-Tunnel gegangen, wo es keine Verbindung gibt, und in einer halben Minute wird er wieder auftauchen. Es gibt viele Möglichkeiten und der Autor ist unbekannt bester Weg Definitionen. Folgendes könnte jedoch für Sie nützlich sein:

1. Erfassen Sie (im Backend) alle Benutzergeräte und den Zeitpunkt der letzten Aktivität von jedem von ihnen.

2. Klassifizieren Sie Systemereignisse, die dem Benutzer gemeldet werden müssen, in solche, die nur an aktive Geräte übermittelt werden müssen, und solche, die per „Broadcast“ (an alle Geräte) übermittelt werden müssen.

3. Führen Sie eine zusätzliche Abstraktionsebene ein – einen Dienst, der bestimmte für den Benutzer interessante Ereignisse abfängt und daraus Nachrichten generiert. So können Sie ganz einfach die gleiche Nachricht über den Erfolg der Operation senden – in verschiedenen Formen: kurze Benachrichtigung in mobiles Gerät, etwas länger - im Browser, eine ausführliche Nachricht - rein E-Mail.

4. Stellen Sie jedem Benutzer auf jedem einzelnen Kommunikationskanal (Webschnittstelle, Mobilgerät, E-Mail) Sendewarteschlangen zur Verfügung. Die standardmäßige AMQP-Funktionalität hilft Ihnen auch bei Nachrichtenablauf-Timeouts, damit diese nicht länger als eine bestimmte Zeit dort liegen bleiben und das System nicht verstopfen. Wenn ein Benutzer über einen bestimmten Kanal „verbunden“ wird, werden ihm neue ausstehende Nachrichten eines bestimmten Typs zugestellt.

Der Autor kann hinzufügen, dass es auf der Grundlage desselben Systems möglich ist, einen verzögerten Versand von Benachrichtigungen aufzubauen (die frühestens versendet werden). bestimmtes Datum) und sogar das Versenden regelmäßiger Korrespondenz in Papierform (Gesetze, Zahlungen usw.), aber dies ist das Thema eines separaten Artikels.

Lassen Sie mich das Wichtigste betonen: Betrachten Sie zugestellte Nachrichten nicht als Benachrichtigungen, die Sie von Facebook oder Vkontakte kennen. Zustellbare Nachrichten sind genau das. Abfrageergebnisse Benutzer! Alle Benutzeraktionen in der Schnittstelle, die irgendeine Art von Anfrage an das Backend implizieren, erhalten Antworten in der einheitlichen Form von „zugestellten Nachrichten“ über einen einzigen einheitlichen Kommunikationskanal. Und dann findet der Webanwendungsalgorithmus heraus, was mit dieser oder jener Nachricht zu tun ist – eine Benachrichtigung mit Text zeichnen, eine Zeile in die Tabelle einfügen, etwas in der Benutzeroberfläche ändern und so weiter.

Paralleles und sequentielles Rechnen

Es wäre sinnlos, eine schnelle Weboberfläche im Frontend zu entwerfen, wenn Ihr Backend langsam ist. Nein, wir werden nicht über Threads, nicht über Forks und nicht über Erlang sprechen. Wir verwenden weiterhin normales PHP, das für jeden Anfänger/Fortgeschrittenen Programmierer zugänglich ist.

Warum wird überhaupt Parallelität benötigt? Auch wenn wir nicht über die Nachteile von Single-Threaded-Sprachen im Allgemeinen sprechen, beschleunigt die Parallelität die Berechnung einer Aufgabe erheblich, was bedeutet, dass sie den Bedarf an Hardwareressourcen (Hardware) erheblich reduziert und die Benutzerzufriedenheit bei der Arbeit erhöht die Schnittstelle (sie erhalten schneller Ergebnisse).

Nehmen Sie einen beliebigen ziemlich komplexen Geschäftsprozess in Ihrem Webprojekt und zeichnen Sie ihn als Schrittkette. Sie erhalten im System eine Abfolge von Aktionen von der Anfrage bis zur Antwort. Höchstwahrscheinlich wird es zunächst einige Prüfungen geben, dann die Ausführung der Hauptaufgabe, dann sekundäre Unteraufgaben und schließlich die Ausgabe des Ergebnisses. Schauen Sie genau hin: Können einige der Aktionen parallel ausgeführt werden?

Lassen Sie mich ein Beispiel nennen: Nehmen wir an, ein Benutzer möchte einen Dienst erwerben, der als zusätzliche kostenpflichtige Option in seinem Tarifplan enthalten ist. Die Anzahl der Optionen ist begrenzt. Wenn die Option erfolgreich aktiviert wurde, müssen Sie im Browser eine Benachrichtigung an den Benutzer senden, eine doppelte E-Mail senden, Geld von seinem Rechnungskonto abbuchen und die Kundenabteilung benachrichtigen. Zeichnen wir eine Kette:

1. Das System hat eine Anfrage zur Aktivierung der Option erhalten.
2. Autorisieren Sie den Benutzer und erfahren Sie seinen Tarifplan.
3. Prüfen Sie, ob diese Option überhaupt aktiviert werden kann Tarifplan Benutzer.
4. Wir prüfen, ob der Benutzer genügend Geld auf seinem Konto hat.
5. Prüfen Sie, ob diese Option nicht im Widerspruch zu anderen Einstellungen steht.
6. Wenn alles in Ordnung ist, aktivieren Sie die Option.
7. Senden Sie eine Benachrichtigung an den Browser.
8. Wir versenden eine Benachrichtigung per Post.
9. Buchen Sie Geld in der Abrechnung ab.
10. Wir benachrichtigen die Kundenabteilung.

Ein aufmerksamer Leser mag die Abfolge der Aktionen bemängeln, aber der Autor wird Sie daran erinnern, dass es sich hierbei um ein ungefähres Beispiel handelt.

Was sehen wir? Beachten Sie, dass es keinen Grund gibt, alle Schritte nacheinander auszuführen. Es wäre viel richtiger, 3,4,5 in drei Threads und am Ende 7,8,9,10 in vier Threads zu „parallelisieren“.

Denken Sie über Threads und Forks nach? Vergebens, Sie haben SOA!

So führen Sie paralleles Rechnen in SOA durch

Den Lesern, die gerade bis zu diesem Punkt im Artikel gescrollt haben, möchte ich erklären, dass es sich nicht um die Parallelisierung derselben Aufgabe in SOA handelt – dafür reicht es im Allgemeinen aus, den Daemon in N Instanzen zu starten und sich darum zu kümmern Streit um den Zugriff auf die Datenbank.

In diesem Beispiel haben wir also drei oder vier oder mehrere verschiedene Aufgaben, die von verschiedenen Diensten ausgeführt werden und die wir parallel ausführen möchten. Sie zur parallelen Verarbeitung zu senden ist nicht schwierig: Es reicht aus, ein Ereignis „Kann Benutzer Benutzername Option

Das Problem besteht gerade darin, diese resultierenden Ereignisse zu sammeln, wenn wir das Gesamtergebnis ihrer Arbeit benötigen, um weiterzumachen. In der obigen Liste benötigen wir beispielsweise das Ergebnis 3+4+5, aber 7+8+9+10 kann ignoriert werden.

Tatsächlich ist es angesichts der hohen Anforderungen an den obligatorischen Abschluss von Transaktionen notwendig, jede Kette bis zum Ende zu kontrollieren, aber wir werden später darauf eingehen.

Wenn unser Daemon „hängt und wartet“ und Ressourcen verbraucht (der sogenannte „Leerlauf“), kann natürlich kein hoch ausgelasteter Dienst auf diese Weise erstellt werden. Es geht darum, dass der Daemon andere Probleme löst und andere Anfragen von anderen Clients bedient, während drei separate „Threads“ (3,4,5) ihre Unteraufgaben lösen. Erschwerend kommt hinzu, dass die resultierenden Ereignisse in zufälliger Reihenfolge eintreten können. All dies lässt sich jedoch einfach und unkompliziert lösen:

Soweit dem Autor bekannt ist, ermöglicht keine der heute „out of the box“ existierenden AMQP-Implementierungen das Abwarten und „Zusammenfügen“ mehrerer Ereignisse zu einem, um nur dieses zu erhalten – eine Resultierende. Sie müssen sich also selbst darum kümmern, und zwar so:

1. Bevor Sie ein Ereignis an AMQP senden, zeichnen Sie im schnellen Speicher (verwenden Sie einen geeigneten In-Memory-Speicher) eine Liste mit Namen der resultierenden Ereignisse auf, die der Dienst erwartet, sowie den Namen des Ereignisses (nennen wir es „ R“), das mit der Summe der Ergebnisse versendet werden muss.

2. Danach beendet der Dienst den Zyklus der Verarbeitung des aktuellen Ereignisses und ist für die nächsten Aufgaben freigegeben.

3. Sobald ein Ereignis aus der Liste eintrifft, die wir im Speicher gespeichert haben, „entpackt“ der Dienst es, speichert seinen Inhalt im Speicher und weist ihn dem Ereignisnamen zu. Gleichzeitig wird geprüft, ob es in dieser Liste noch Ereignisse gibt, für die keine Antwort eingegangen ist. Wenn ja, endet der Zyklus an dieser Stelle.

4. Andernfalls, d. h. wenn für alle Ereignisse in der Liste eine Antwort empfangen wurde, führt der Dienst sie zusammen und versendet das zusammengeführte Ergebnis unter dem Namen des zuvor gespeicherten Ereignisses „R“. Danach wird die Ergebnisliste einfach aus dem Speicher gelöscht, um Speicherplatz zu sparen – sie wird nicht mehr benötigt.

5. Derselbe Dienst oder ein anderer (nach Ermessen des Systementwicklers) empfängt das resultierende Ereignis „R“ mit allen Ergebnissen der Parallelverarbeitung. Das nächste ist offensichtlich.

Wenn Sie aufgrund der Beschreibung den Eindruck hatten, dass dies lang ist, dann lassen Sie es mich erklären: Wir sprechen von Tausenden und Zehntausenden Ereignissen pro Sekunde (!) auf einem durchschnittlichen Server.

Die Verwendung von In-Memory-Speicher bedeutet, dass der aktuelle Geschäftsprozess auch dann nicht verloren geht, wenn der Dienst gestoppt wird (Absturz, Aktualisierung). Nachdem der Dienst erneut gestartet wurde, empfängt er weiterhin Ereignisse vom ESB und verarbeitet sie gemäß dem oben beschriebenen Algorithmus.

Transaktionalität, Rollback und Fail-Szenarien in SOA

Da Peer-Ereignisse in SOA durch den ESB „wandern“, benötigen Sie eine Art Zeichen, das anzeigt, dass sich „diese Antwort“ auf „diese Anfrage“ bezieht. Hier müssen Sie das Rad nicht neu erfinden – in den Spezifikationen aller gängigen Protokolle finden Sie einen Parameter namens „korrelation_id type“. Normalerweise ist dies eine Zeichenfolge. Es muss in den Parametern aller Ereignisse jedes einzelnen Geschäftsprozesses vom Input bis zum Output enthalten sein, um die zu diesem Geschäftsprozess gehörende Nachrichtenkette zu identifizieren. Von der Weboberfläche aus betrachtet speichert die Webanwendung aktuelle aktive (gesendete) Anfragen und „versteht“ anhand der Korrelations-ID, auf welche Anfrage die jeweilige konkrete Antwort kam.

Lassen Sie uns die Terminologie verstehen: Transaktionalität ist die Eigenschaft eines Systems, mehrere Aktionen als eine allgemeine Operation auszuführen, die sinnvoll ist und nur vollständig abgeschlossen werden kann. Da es in einem verteilten System mit parallelen Threads physikalisch unmöglich ist, mehrere Operationen atomar auszuführen, stellt das System sogenannte Fail-Scripts und Rollbacks zur Verfügung.

Ein Fehlerskript ist im Allgemeinen eine Aktion, die ausgeführt werden muss, wenn ein Fehler auftritt. Rollback ist in diesem Zusammenhang ein Skript von Aktionen, die ausgeführt werden müssen, um eine Serie „abzubrechen“. vorherige Aktionen was letztendlich zu dem Fehler führte. Grob gesagt handelt es sich bei Rollbacks um Prozesse, die das Gegenteil von normalen Geschäftsprozessen im System sind.

Rollbacks sind nicht immer notwendig und nicht immer möglich. Wenn Sie beispielsweise eine Option mit dem Benutzer verbunden haben und dann auf die Abrechnung „gefallen“ sind, kann die Option wieder deaktiviert werden. Dann können Sie es erneut versuchen, automatisch oder durch einen zweiten Befehl des Benutzers. Und wenn Sie den Inhalt physisch gelöscht haben und einige der nachfolgenden Vorgänge nicht funktioniert haben ... Die Situation ist nicht eindeutig.

Daher müssen Fehlerszenarien und Rollbacks mit Bedacht angegangen werden. Der Autor kann folgenden Weg empfehlen: Schreiben Sie immer Fail-Skripte, aber nicht immer Rollbacks. Ihre Dateien werden sofort in der Überwachung angezeigt – und das Support-Team kann die Situation schnell manuell beheben, Erfahrungen sammeln und technische Spezifikationen für Programmierer formulieren. Dabei kann es sehr, sehr schwierig sein, ein eindeutig korrektes Rollback von Grund auf zu schreiben.

Es sollte jedoch verstanden werden, dass Rollbacks, Rollbacks und die Behandlung von Fehlersituationen dieselben Geschäftsprozesse sind wie alles andere. Sie basieren auch auf Ereignissen, die das System durchlaufen. Es wäre schön, wenn man in jedem Event und jedem Handler zunächst zwei gegensätzliche Pfade (vorwärts und rückwärts, Erfolg und Misserfolg) vorgibt, um diese in konkreten Aufgaben umzusetzen.

SOA skalieren

Jedes System muss früher oder später erweitert werden. Im Fall von SOA geht das ganz einfach und selbstverständlich:

1. Doppelter Einstiegspunkt. Dies bezieht sich auf dasselbe WebSocket-Gateway, das wir am Anfang des Artikels betrachtet haben. Es kann unbegrenzt oft dupliziert werden, da die Kommunikation zwischen ihm und dem Client vereinheitlicht und von den Interna des Systems entkoppelt ist und die Kommunikation zwischen ihm und dem System wiederum von der Kommunikation mit dem Client entkoppelt ist.

2. Duplizierung von Instanzen (Instanzen) von Diensten. Dienste, die keine Datenbank benötigen oder nur daraus „lesen“, lassen sich leicht duplizieren. Und die Standardfunktionalität von RabbitMQ ermöglicht es Ihnen, N Instanzen in derselben Warteschlange zu abonnieren, von denen Nachrichten zufällig bei der einen oder anderen Instanz eintreffen. Beim Duplizieren von Diensten, die externe Anwendungen (Datenbanken, Software von Drittanbietern) verarbeiten, müssen Sie berücksichtigen, wie diese Anwendungen Transaktionsanforderungen von mehreren parallelen Clients sicherstellen.

3. Vervielfältigung der Datenspeicherung. Hier steht es Ihnen frei, jedes Ihnen bekannte Sharding zu verwenden. Wenn Sie es mit 10 Millionen Benutzern zu tun haben und Ihnen das viel erscheint, teilen Sie sie in 10 Basen pro Million auf (z. B. basierend auf dem CRC32 der Benutzeranmeldung oder einer anderen zyklischen Methode). Wenn die Datenbank eines Dienstes kontinuierlich wächst und komplexer wird, teilen Sie sie in zwei Dienste auf.

4. Duplizierung von AMQP-Broker und In-Memory-Speicher. Aus der Erfahrung des Autors erfüllen RabbitMQ und Redis ihre Rolle perfekt. Wenn Sie Geräte in mehr als einem DC-Rack haben, wählen Sie den Rabbit-Modus, der Netzwerkverbindungsfehler toleriert.

5. Vervielfältigung ganzer Maschinen. MIT moderne Technologien Virtualisierung (KVM) und Konfiguration (Chef) besteht die Aufgabe, „die gleiche Maschine hochzufahren“, auf das Drücken einer Taste.

Verschlüsselung des Datenverkehrs zwischen Frontend und Backend

Es wird empfohlen, eine WebSocket-Verbindung über SSL zu organisieren. Darüber hinaus erhöht sich dadurch die „Durchschlagskraft“ gegenüber rückständigen Office-Anbietern, die „jeden seltsamen Verkehr“ außer HTTP[S] blockieren.

Wenn Ihnen dies jedoch nicht ausreicht, können Sie für jede Client-Anmeldung Schlüsselpaare generieren (ein Paar am Frontend, das zweite am Backend), öffentliche Schlüssel austauschen und dann den gesamten Datenverkehr mit regulärem RSA verschlüsseln.

Schutz vor DDOS und ähnlichen Missbräuchen

Auf die Frage des „Low-Level“-Schutzes bei SYN-Flooding und Flooding von Kanälen mit Hunderten von Gigabit verzichtet der Autor bewusst, da darüber bereits Hunderte Fachliteratur geschrieben wurde. Lassen Sie uns darüber sprechen, wie Sie das System intern auf seinen logischen Ebenen schützen können, wenn ein Angreifer einen Weg gefunden hat, Ihr System (SOA+ESB) mit Tausenden von Ereignissen zu „überfluten“.

1. Erste Regel: Nichts sollte verarbeitet werden, bis seine Gültigkeit bestätigt ist. Wenn Sie als Eingabe einen kleinen Text in JSON, verpackt in BASE64, erwarten, sollte eine eingehende Zeichenfolge, die länger als ein Megabyte ist, eindeutig verworfen werden – versuchen Sie nicht, sie zu entpacken. Ähnlich verhält es sich mit einer Zeichenfolge, die „nicht-lateinische“ Zeichen enthält. Wenn Sie die Zeichenfolge entpackt haben, versuchen Sie nicht, json_decode sofort auszuführen, sondern überprüfen Sie zunächst die Anzahl und Paarung der Klammern. Und so weiter.

Es scheint Paranoia zu sein, aber sonst kann man sich leicht „merken“, das heißt, es kann zu einem Dienstausfall führen, der ihn dazu zwingt, den gesamten verfügbaren RAM zu belegen.

2. Der Dienst, der eingehende Nachrichten verarbeitet, sollte nichts in den Arbeitsspeicher, die Datenbank oder andere Speicher schreiben. Der Grund ist derselbe. Stellen Sie zunächst sicher, dass die Nachricht als Ganzes gültig ist, und geben Sie sie erst dann „tiefer“ in das System weiter.

3. Ein Dienst, der gezwungen werden kann, häufig „die Datenbank zu besuchen“, muss durch Caching geschützt werden. Ein einfaches Beispiel ist der Benutzerautorisierungsdienst. Wenn es nicht geschützt ist, kann ein Angreifer Tausende von Autorisierungsanfragen hintereinander senden und dadurch die Datenbank überlasten.

4. Am Eingang des Systems benötigen Sie einen Dienst, der Anfragen von „verdächtigen“ Quellen ablehnt, beispielsweise von IP-Adressen aus einer „schwarzen Liste“.

Es scheint ein klassischer Rat zu sein, also was ist los? Das Hauptproblem besteht darin, dass, wenn wir am Eingang des Systems einen Analyse-Filter-Dienst installieren (wie es in klassischen Webprojekten der Fall ist), die Leistung des gesamten Systems nicht höher ist als die Leistung dieses Analyse-Filters. Jede Nachricht „in Echtzeit“ auf Verdacht zu überprüfen, ist extrem kostspielig. Sie können und sollten dies im Nachhinein tun, und so geht's:

1. Erstellen Sie einen Dienst, der alle Nachrichten im System „abhört“. In RabbitMQ wird dies durch das Abonnieren des Routing-Schlüssels „#“ erreicht.

2. Bringen Sie diesem Dienst bestimmte Regeln bei, anhand derer er „verdächtige“ Absender diagnostizieren kann. Dies sind beispielsweise Absender, die über einen längeren Zeitraum hinweg zu viele ähnliche Nachrichten senden, wiederholt Nachrichten senden oder Nachrichten im Namen desselben Benutzers von verschiedenen IP-Adressen aus senden. Es gibt viele Optionen. Nutzen Sie Ihre Vorstellung. Dabei spielt es keine Rolle, wie schnell ein solcher Dienst arbeitet (natürlich in vertretbaren Grenzen) – die Geschwindigkeit des Gesamtsystems wird dadurch nicht beeinträchtigt.

3. Sobald der Dienst zu dem Schluss kommt, dass dieser oder jener Absender verdächtig ist, sendet er dieses Ereignis an das System und geht seiner Arbeit weiter nach.

4. Installieren Sie am Eingang einen sehr einfachen und schnell agierenden Daemon – einen Filterdienst, dessen Aufgabe lediglich darin besteht, verdächtige Absender „dumm“ zu blockieren. Keine Analyse, keine Analyse, keine zusätzlichen Kosten. Es ist leicht zu erraten, wer als verdächtig gilt: Der Dienst wird durch die im vorherigen Absatz beschriebenen Ereignisse davon erfahren und sie zu seiner internen schwarzen Liste hinzufügen.

Ende des ersten Teils. Fortsetzung: SOA: Verteilte Architektur und ihre Wartung .

Ich bin Mentor in IT-Projekten. Das bedeutet, dass ich Ihnen als Eigentümer oder Manager dabei helfen kann, Ihr Unternehmen zu neuen Höhen zu führen. Bringen Sie Ordnung, verstehen Sie die Motivation des Teams, implementieren Sie Tools und erreichen Sie bestimmte Ziele. Ich bringe Ihnen nicht bei, wie man Geschäfte macht, ich helfe Ihnen nur, die großzügig verteilten Rechen auf Ihrem Weg zu umgehen. .

Das Material richtet sich hauptsächlich an Webprogrammierer-Anfänger.

Einführung. Ich werde oft von Kunden angesprochen, die selbst geschriebene CMS oder von unerfahrenen Webprogrammierern geschriebene Module installiert haben, die nicht verstehen, was zum Schutz von Daten erforderlich ist, und oft Filterfunktionen kopieren, ohne darüber nachzudenken, wie sie funktionieren und was genau damit gemacht werden muss .

Hier werde ich versuchen, häufige Fehler beim Filtern von Daten so detailliert wie möglich zu beschreiben PHP-Skript und geben Sie einfache Tipps, wie Sie Daten richtig filtern.

Es gibt viele Artikel im Internet zum Thema Datenfilterung, diese sind jedoch nicht vollständig und enthalten keine detaillierten Beispiele.

Nachbesprechung.Filterung. Fehler Nr. 1 Für numerische Variablen wird die folgende Prüfung verwendet:
$number = $_GET["input_number"]; if (intval($number)) ( ... die SQL-Abfrage ausführen... )
Warum führt es zur SQL-Injection? Tatsache ist, dass der Benutzer den Wert in der Variablen input_number angeben kann:
1"+UNION+SELECT
In solchen Fällen wird die Prüfung erfolgreich abgeschlossen, weil Die Funktion intval ruft den ganzzahligen Wert einer Variablen ab, d. h. 1, aber an der Variablen $number selbst hat sich nichts geändert, sodass der gesamte Schadcode an die SQL-Abfrage übergeben wird.
Richtige Filterung:
$number = intval($_GET["input_number"]); if ($number) ( ... SQL-Abfrage ausführen... )
Natürlich kann sich die Bedingung ändern, wenn Sie beispielsweise nur eine bestimmte Reichweite benötigen:
if ($number >= 32 AND $number dir = MAIN_DIR . "/template/" . $config["skin"];
In diesem Fall können Sie den Wert der Variablen $_COOKIE["skin"] ersetzen und einen Fehler verursachen, wodurch Sie den absoluten Pfad zum Site-Ordner sehen.
Wenn Sie den Cookie-Wert zum Speichern in der Datenbank verwenden, verwenden Sie eine der oben beschriebenen Filterungen. Dies gilt auch für die Filterung der Variable $_SERVER. Fehler Nr. 5. Die Anweisung register_globals ist aktiviert. Schalten Sie es unbedingt aus, wenn es eingeschaltet ist.
In manchen Situationen können Sie den Wert einer Variablen übergeben, die nicht hätte übergeben werden sollen. Wenn die Site beispielsweise über Gruppen verfügt, sollte die Variable $group für Gruppe 2 leer oder gleich 0 sein, aber das reicht zur Fälschung aus das Formular durch Hinzufügen des Codes:

In einem PHP-Skript ist die Variable $group gleich 5, wenn sie im Skript nicht mit einem Standardwert deklariert wurde. Fehler Nr. 6. Überprüfen Sie Ihre heruntergeladenen Dateien.
Überprüfen Sie folgende Punkte:
  • Dateierweiterung. Es wird empfohlen, das Herunterladen von Dateien mit den Erweiterungen php, php3, php4, php5 usw. zu verbieten.
  • Ist die auf den Server hochgeladene Datei move_uploaded_file
  • Dateigröße
  • Prüfung. Fehler Nr. 1. Ich bin auf Fälle gestoßen, in denen für eine AJAX-Anfrage (zum Beispiel: Reputationssteigerung) der Name oder die ID des Benutzers übergeben wurde (an den die Reputation erhöht wurde), in PHP selbst jedoch keine Prüfung auf die Existenz eines solchen Benutzers durchgeführt wurde.
    Zum Beispiel:
    $user_id = intval($_REQUEST["user_id"]); ... INSERT INTO REPLOG SET uid = "($user_id)", plus = "1" ... ... UPDATE Users SET meaning = meaning+1 WHERE user_id = "($user_id)" ...
    Es stellt sich heraus, dass wir einen Eintrag in der Datenbank erstellen, der für uns völlig nutzlos ist. Fehler Nr. 2. Vergessen Sie beim Durchführen verschiedener Arten von Aktionen (Hinzufügen, Bearbeiten, Löschen) mit Daten nicht, die Rechte des Benutzers für den Zugriff auf diese Funktion zu überprüfen zusätzliche Funktionen(Verwendung von HTML-Tags oder die Möglichkeit, Material ohne Überprüfung zu veröffentlichen).

    Vor langer Zeit habe ich einen ähnlichen Fehler in einem Forummodul behoben, bei dem jeder Benutzer eine Nachricht an die Administration bearbeiten konnte.

    Prüfung. Fehler Nr. 3. Bei Verwendung mehrerer PHP-Dateien Führen Sie eine einfache Überprüfung durch.
    Schreiben Sie in die Datei index.php (oder in eine andere Hauptdatei) die folgende Zeile, bevor Sie andere PHP-Dateien hinzufügen:
    define("READFILE", true);
    Schreiben Sie am Anfang anderer PHP-Dateien:
    if (! definiert ("READFILE")) ( exit ("Fehler, falscher Weg zur Datei.
    Gehen Sie zum Hauptmenü."); }
    Auf diese Weise schränken Sie den Zugriff auf Dateien ein. Fehler Nr. 4. Verwenden Sie Hashes für Benutzer. Dadurch wird verhindert, dass diese oder jene Funktion über XSS aufgerufen wird.
    Ein Beispiel für das Kompilieren eines Hashs für Benutzer:
    $secret_key = md5(strtolower("http://site.ru/" . $member["name"] . sha1($password) . date("Ymd"))); // $secret_key ist unser Hash
    Als nächstes ersetzen Sie in allen wichtigen Formularen die Eingabe durch den Wert des aktuellen Hashs des Benutzers:

    Überprüfen Sie beim Ausführen des Skripts Folgendes:
    if ($_POST["secret_key"] !== $secret_key) ( exit ("Error: Secret_key!"); ) Überprüfen. Fehler Nr. 5. Nehmen Sie bei der Ausgabe von SQL-Fehlern eine einfache Einschränkung des Zugriffs auf Informationen vor. Legen Sie beispielsweise das Passwort für die GET-Variable fest:
    if ($_GET["passsql"] == "password") ( ... SQL-Fehlerausgabe... ) else ( ... Nur Informationen zum Fehler, keine Details... )
    Auf diese Weise können Sie Informationen vor dem Hacker verbergen, die ihm bei der Überprüfung der Website helfen können. Fehler Nr. 5. Versuchen Sie, Dateien nicht einzuschließen, indem Sie Dateinamen von außen beziehen.
    Zum Beispiel:
    if (isset($_GET["file_name"])) ( include $_GET["file_name"] .".php"; )
    Benutzen Sie den Schalter