So stellen Sie die Idempotenz der Schnittstelle sicher

Warum müssen wir die Idempotenz der Schnittstelle sicherstellen?

Denn in vielen Geschäftsszenarien kommt es zur Gewährleistung der Geschäftskorrektheit häufig zu wiederholten Anfragen oder wiederholten Nachrichten. Insbesondere in verteilten Systemen kann es aufgrund von Netzwerkinstabilität oder Lastausgleich zu wiederholten Anfragen kommen. Wenn die Schnittstelle nicht idempotent ist, führt dies dazu, dass derselbe Vorgang viele Male wiederholt wird, was zu fehlerhaften Datenaktualisierungen oder anderen Problemen führt

Szenariobeispiel

  1. Als wir einige Formulare ausfüllten, klickten wir versehentlich zweimal schnell auf die Schaltfläche „Speichern“ und es wurden zwei doppelte Daten in der Tabelle generiert, die jedoch unterschiedliche IDs hatten.
  2. Um das Schnittstellen-Timeout-Problem in unserem Projekt zu lösen, wurde ein Wiederholungsmechanismus eingeführt. Beim ersten Timeout der Anforderungsschnittstelle konnte der Anforderer das zurückgegebene Ergebnis nicht rechtzeitig abrufen (möglicherweise war dies zu diesem Zeitpunkt erfolgreich), um die Rückgabe des falschen Ergebnisses zu vermeiden (ist es in diesem Fall unmöglich, einen Fehler direkt zurückzugeben?) , also wird die Anfrage wiederholt. Versuchen Sie es ein paar Mal, auch dies führt zu doppelten Daten.
  3. Wenn MQ-Konsumenten Nachrichten lesen, lesen sie manchmal doppelte Nachrichten. Wenn MQ Strict ExactOnce nicht aktiviert, führt dies zu doppelten Daten.

Die Idempotenz der Schnittstelle bedeutet, dass die Ergebnisse einer oder mehrerer vom Benutzer für denselben Vorgang initiierter Anforderungen konsistent sind und es keine Nebenwirkungen aufgrund mehrerer Klicks gibt.

Diese Art von Problem tritt hauptsächlich in der Schnittstelle auf:

Einfügevorgang. In diesem Fall können mehrere Anforderungen doppelte Daten generieren.
Wenn es sich bei der Aktualisierungsoperation lediglich um die Aktualisierung von Daten handelt, zum Beispiel: update user set status=1 where id=1, gibt es kein Problem. Wenn noch Berechnungen vorhanden sind, wie zum Beispiel: Benutzersatzstatus=Status+1 aktualisieren, wobei ID=1 ist, können in diesem Fall mehrere Anfragen zu Datenfehlern führen.

Lösung

Vor dem Einfügen auswählen

Um doppelte Daten zu vermeiden, wählen wir in der Schnittstelle zum Speichern von Daten normalerweise Daten vor dem Einfügen entsprechend dem Namen oder dem Codefeld aus. Wenn die Daten bereits vorhanden sind, wird der Aktualisierungsvorgang ausgeführt. Wenn die Daten nicht vorhanden sind, wird der Einfügevorgang ausgeführt.

Diese Lösung ist möglicherweise die Lösung, die wir am häufigsten verwenden, wenn es darum geht, die Generierung doppelter Daten zu verhindern. In gleichzeitigen Szenarien sollte es jedoch in Verbindung mit anderen Lösungen verwendet werden, da sonst auch doppelte Daten generiert werden.

Pessimistische Sperre hinzufügen

Im Zahlungsszenario beträgt der Kontostand von Benutzer A 150 Yuan und er möchte 100 Yuan überweisen. Unter normalen Umständen beträgt der Kontostand von Benutzer A nur 50 Yuan. Im Allgemeinen sieht SQL so aus:

Benutzerbetrag aktualisieren = Betrag-100, wobei ID = 123;
Wenn dieselbe Anforderung mehrmals auftritt, kann der Saldo von Benutzer A negativ werden. In diesem Fall könnte Benutzer A weinen. Gleichzeitig können Systementwickler auch weinen, weil es sich um einen sehr schwerwiegenden Systemfehler handelt.

Um dieses Problem zu lösen, kann eine pessimistische Sperre hinzugefügt werden, um die Datenzeile von Benutzer A zu sperren. Gleichzeitig darf nur eine Anforderung die Sperre erhalten und die Daten aktualisieren, während andere Anforderungen warten.

Normalerweise wird eine einzelne Datenzeile durch die folgende SQL gesperrt:

Wählen Sie * aus Benutzer-ID=123 für die Aktualisierung aus.

Der spezifische Prozess ist wie folgt:

Mehrere Anfragen fragen gleichzeitig Benutzerinformationen basierend auf der ID ab.
Stellen Sie fest, ob der Kontostand weniger als 100 beträgt, und geben Sie den unzureichenden Kontostand direkt zurück, wenn der Kontostand nicht ausreicht.
Wenn das Guthaben ausreicht, fragen Sie die Benutzerinformationen zur Aktualisierung erneut ab und versuchen Sie, die Sperre zu erhalten.
Nur die erste Anforderung kann die Zeilensperre erhalten, und die übrigen Anforderungen, die die Sperre nicht erhalten, warten auf die nächste Gelegenheit, die Sperre zu erhalten.
Nachdem die erste Anforderung die Sperre erhalten hat, wird beurteilt, ob der Kontostand weniger als 100 beträgt. Wenn der Kontostand ausreicht, wird der Aktualisierungsvorgang ausgeführt.
Wenn das Guthaben nicht ausreicht, bedeutet dies, dass die Anforderung wiederholt wird und direkt ein Erfolg zurückgegeben wird.

Besondere Aufmerksamkeit ist erforderlich: Wenn Sie die MySQL-Datenbank verwenden, muss die Speicher-Engine innodb verwenden, da sie nur Transaktionen unterstützt. Darüber hinaus muss das ID-Feld hier ein Primärschlüssel oder ein eindeutiger Index sein, andernfalls wird die gesamte Tabelle gesperrt.

Fügen Sie eine optimistische Sperre hinzu

Beim pessimistischen Sperren muss eine Datenzeile während desselben Transaktionsvorgangs gesperrt werden. Wenn die Transaktion lange dauert, führt dies dazu, dass eine große Anzahl von Anforderungen wartet und die Schnittstellenleistung beeinträchtigt wird.

Um die Schnittstellenleistung zu verbessern, können wir optimistisches Sperren verwenden. Der Tabelle muss ein Zeitstempel- oder Versionsfeld hinzugefügt werden. Hier nehmen wir das Versionsfeld als Beispiel.

Fragen Sie die Daten ab, bevor Sie die Daten aktualisieren:

Wählen Sie ID, Menge und Version aus Benutzer-ID = 123 aus.

Wenn die Daten vorhanden sind, gehen Sie davon aus, dass die gefundene Version gleich 1 ist, und verwenden Sie dann die Felder id und version als Abfragebedingungen, um die Daten zu aktualisieren:

Benutzersatz aktualisieren: Betrag = Betrag + 100, Version = Version + 1, wobei ID = 123 und Version = 1;

Beim Aktualisieren der Daten wird Version + 1 verwendet, und dann wird festgestellt, dass die Anzahl der vom Aktualisierungsvorgang betroffenen Zeilen größer als 0 ist, was darauf hinweist, dass die Aktualisierung erfolgreich war, und wenn sie gleich 0 ist, bedeutet dies, dass die Aktualisierung fehlgeschlagen ist.

Da die erste Anfrage mit Version gleich 1 erfolgreich sein kann, wird die Version nach erfolgreichem Vorgang zu 2. Wenn zu diesem Zeitpunkt gleichzeitige Anforderungen eingehen, führen Sie die folgende SQL aus:

Benutzersatz aktualisieren: Betrag = Betrag + 100, Version = Version + 1, wobei ID = 123 und Version = 1;

Durch den Aktualisierungsvorgang werden die Daten nicht tatsächlich aktualisiert. Am Ende beträgt die Anzahl der vom SQL-Ausführungsergebnis betroffenen Zeilen 0, da die Version 2 geworden ist und die Version = 1 die Bedingung nicht erfüllen darf. Um jedoch die Idempotenz der Schnittstelle sicherzustellen, kann die Schnittstelle direkt einen Erfolg zurückgeben, da der Versionswert geändert wurde, sodass der vorherige Erfolg einmal durchgeführt werden muss und die nachfolgenden Anforderungen wiederholt werden.

Der spezifische Prozess ist wie folgt:

Spezifische Schritte:

Fragen Sie zunächst die Benutzerinformationen gemäß der ID ab, einschließlich des Versionsfelds. Verwenden Sie
die ID- und Versionsfeldwerte als Parameter der Where-Bedingung, um die Benutzerinformationen zu aktualisieren. Gleichzeitig
bestimmt Version + 1 die Anzahl der Zeilen von der Operation betroffen. Wenn eine Zeile davon betroffen ist, bedeutet dies, dass es sich um eine Anfrage handelt und Sie andere Datenmanipulationen durchführen können.
Wenn 0 Zeilen betroffen sind, bedeutet dies, dass die Anforderung wiederholt wird und der Erfolg direkt zurückgegeben wird.

Fügen Sie einen eindeutigen Index hinzu

In den meisten Fällen fügen wir der Tabelle einen eindeutigen Index hinzu, um doppelte Daten zu verhindern.

Tabelle ändern, orderUNIQUE KEY hinzufügen un_code( code);

Nach dem Hinzufügen eines eindeutigen Index können die ersten Anforderungsdaten erfolgreich eingefügt werden. Aber für die gleiche Anfrage später, beim Einfügen von Daten, wird eine Ausnahme beim doppelten Eintrag „002“ für den Schlüssel „order.un_code“ gemeldet, was darauf hinweist, dass ein Konflikt im eindeutigen Index vorliegt. Obwohl das Auslösen einer Ausnahme keine Auswirkungen auf die Daten hat, führt es nicht zu falschen Daten. Um jedoch die Idempotenz der Schnittstelle sicherzustellen, müssen wir die Ausnahme abfangen und Erfolg zurückgeben.

Wenn es sich um ein Java-Programm handelt, müssen Sie Folgendes erfassen: DuplicateKeyException-Ausnahme. Wenn Sie das Spring-Framework verwenden, müssen Sie außerdem Folgendes erfassen: MySQLIntegrityConstraintViolationException-Ausnahme.

Spezifische Schritte:

Der Benutzer initiiert eine Anfrage über den Browser und der Server sammelt Daten.
Fügen Sie die Daten in MySQL ein
, um festzustellen, ob die Ausführung erfolgreich ist, und führen Sie bei Erfolg andere Daten (und möglicherweise andere Geschäftslogik) aus.
Wenn die Ausführung fehlschlägt, wird die eindeutige Indexkonfliktausnahme abgefangen und der Erfolg wird direkt zurückgegeben.

Anti-Schwer-Uhr

Manchmal dürfen nicht alle Szenarien in der Tabelle doppelte Daten generieren, einige Szenarios sind jedoch nicht zulässig. Derzeit ist es offensichtlich nicht angebracht, der Tabelle direkt einen eindeutigen Index hinzuzufügen.

Als Reaktion auf diese Situation können wir das Problem lösen, indem wir einen Anti-Heavy-Tisch erstellen.

Die Tabelle darf nur zwei Felder enthalten: ID und eindeutiger Index. Der eindeutige Index kann eine eindeutige Kennung in Kombination mit mehreren Feldern wie Name, Code usw. sein, zum Beispiel: susan_0001.

Das spezifische Flussdiagramm lautet wie folgt: Spezifische Schritte:

Der Benutzer initiiert eine Anfrage über den Browser und der Server sammelt Daten.
Fügen Sie die Daten in die MySQL-Anti-Heavy-Tabelle ein
, um festzustellen, ob die Ausführung erfolgreich ist. Führen Sie bei Erfolg andere Datenoperationen auf MySQL aus (möglicherweise andere Geschäftslogik).
Wenn die Ausführung fehlschlägt, wird die eindeutige Indexkonfliktausnahme abgefangen und der Erfolg wird direkt zurückgegeben.
Besonderes Augenmerk sollte darauf gelegt werden: Die Antiduplikationstabelle und die Geschäftstabelle müssen sich in derselben Datenbank befinden und die Vorgänge müssen in derselben Transaktion erfolgen.

Laut Staatsmaschine

In vielen Fällen ist die Geschäftstabelle zustandsbehaftet. Beispielsweise hat die Bestelltabelle: 1-Bestellung, 2-bezahlt, 3-abgeschlossen, 4-storniert und andere Zustände. Wenn die Werte dieser Zustände regelmäßig sind, können wir damit die Idempotenz der Schnittstelle entsprechend der Tatsache sicherstellen, dass die Geschäftsknoten von klein nach groß sind.

Wenn der Bestellstatus mit der ID=123 „Bezahlt“ lautet, wird die Bestellung nun abgeschlossen.

Update- orderSet-Status=3 mit ID=123 und Status=2;

Bei der ersten Anforderung wird der Status der Bestellung bezahlt und der Wert beträgt 2, sodass die Aktualisierungsanweisung die Daten normal aktualisieren kann. Die Anzahl der vom SQL-Ausführungsergebnis betroffenen Zeilen beträgt 1 und der Status der Bestellung wird 3.

Wenn dieselbe Anforderung später eintrifft und dieselbe SQL ausgeführt wird, wird der Auftragsstatus zu 3 und Status = 2 wird als Bedingung verwendet. Die zu aktualisierenden Daten können nicht abgefragt werden, sodass die Anzahl der Zeilen, die von der endgültigen SQL-Ausführung betroffen sind, nicht abgefragt werden kann Das Ergebnis ist 0. Das heißt, die Daten werden nicht tatsächlich aktualisiert. Um jedoch die Idempotenz der Schnittstelle sicherzustellen, kann die Schnittstelle auch direkt Erfolg zurückgeben, wenn die Anzahl der betroffenen Zeilen 0 ist.

Das spezifische Flussdiagramm lautet wie folgt:

Spezifische Schritte:

Der Benutzer initiiert eine Anfrage über den Browser und der Server sammelt Daten.
Aktualisieren Sie basierend auf der ID und dem aktuellen Status als Bedingungen auf den nächsten Status.
Bestimmen Sie die Anzahl der von der Operation betroffenen Zeilen. Wenn eine Zeile betroffen ist, bedeutet dies, dass die aktuelle Operation erfolgreich ist und andere Datenoperationen ausgeführt werden können.
Wenn 0 Zeilen betroffen sind, bedeutet dies, dass die Anforderung wiederholt wird und der Erfolg direkt zurückgegeben wird.
Das Wichtigste, worauf Sie besonders achten sollten, ist, dass diese Lösung auf den Sonderfall beschränkt ist, in dem die zu aktualisierende Tabelle ein Statusfeld enthält und das Statusfeld nur aktualisiert werden muss. Nicht alle Szenarien sind anwendbar.

Verteilte Sperre hinzufügen

Tatsächlich wird beim Hinzufügen eines eindeutigen Index oder beim Hinzufügen einer Anti-Heavy-Tabelle im Wesentlichen die verteilte Sperre der Datenbank verwendet, bei der es sich ebenfalls um eine Art verteilte Sperre handelt. Da die Leistung der verteilten Datenbanksperre jedoch nicht sehr gut ist, können wir stattdessen Redis oder Zookeeper verwenden.

Angesichts der Tatsache, dass die verteilten Konfigurationszentren vieler Unternehmen auf Apollo oder Nacos umgestiegen sind und Zookeeper selten verwendet wird, werden wir Redis als Beispiel für die Einführung verteilter Sperren verwenden.

Derzeit gibt es drei Hauptmethoden, um die verteilte Sperre von Redis zu realisieren:

setNx-Befehl
Satzbefehl
Redission-Framework
Jedes Schema hat seine eigenen Vor- und Nachteile. Ich werde nicht auf die spezifischen Implementierungsdetails eingehen. Freunde, die interessiert sind, können mich zu WeChat hinzufügen und privat mit mir chatten.

Das spezifische Flussdiagramm lautet wie folgt:

Spezifische Schritte:

Wenn der Benutzer eine Anfrage über den Browser initiiert, sammelt der Server die Daten und generiert den Bestellnummerncode als einziges Geschäftsfeld.
Verwenden Sie den Befehl set von redis, um den Bestellcode in redis festzulegen und gleichzeitig den Timeout-Zeitraum festzulegen.
Beurteilen Sie, ob die Einstellung erfolgreich ist. Wenn die Einstellung erfolgreich ist, bedeutet dies, dass es sich um die erste Anforderung handelt und die Datenoperation ausgeführt wird.
Wenn die Einstellung fehlschlägt, bedeutet dies, dass die Anforderung wiederholt wird und direkt einen Erfolg zurückgibt.
Besonders zu beachten ist: Verteilte Sperren müssen eine angemessene Ablaufzeit festlegen. Ist die Einstellung zu kurz, können wiederholte Anfragen nicht wirksam verhindert werden. Wenn die Einstellung zu lang ist, kann der Speicherplatz von Redis verschwendet werden, was entsprechend der tatsächlichen Geschäftssituation ermittelt werden muss.

bekomme Token

Zusätzlich zu den oben genannten Schemata gibt es ein letztes Schema, das Token verwendet. Diese Lösung unterscheidet sich ein wenig von allen vorherigen Lösungen und erfordert zwei Anfragen, um einen Geschäftsvorgang abzuschließen.

Die erste Anforderung zum Erhalten des Tokens
und die zweite Anforderung bringen dieses Token dazu, den Geschäftsvorgang abzuschließen.
Das spezifische Flussdiagramm lautet wie folgt:

Der erste Schritt besteht darin, zuerst den Token zu erhalten.

Der zweite Schritt besteht darin, spezifische Geschäftsvorgänge durchzuführen.

Spezifische Schritte:

Wenn ein Benutzer eine Seite besucht, initiiert der Browser automatisch eine Token-Anfrage.
Der Server generiert ein Token, speichert es in Redis und gibt es an den Browser zurück.
Das Token wird übertragen, wenn der Benutzer eine Anfrage über den Browser initiiert.
Fragen Sie ab, ob das Token in Redis vorhanden ist. Wenn es nicht vorhanden ist, bedeutet dies, dass es sich um die erste Anforderung handelt, und führen Sie dann nachfolgende Datenoperationen aus.
Wenn es vorhanden ist, bedeutet dies, dass die Anforderung wiederholt wird und direkt einen Erfolg zurückgibt.
In Redis wird das Token nach Ablauf der Ablaufzeit automatisch gelöscht.
Übrigens gibt es tatsächlich einen Unterschied zwischen Anti-Heavy-Design und idempotentem Design. Das Anti-Duplikations-Design dient hauptsächlich dazu, die Duplizierung von Daten zu vermeiden, und es gibt nicht viele Anforderungen an die Rückgabe der Schnittstelle. Das idempotente Design vermeidet nicht nur doppelte Daten, sondern erfordert auch, dass jede Anfrage das gleiche Ergebnis zurückgibt.

Das obige Schema ist für Idempotenz ausgelegt.

Wenn es sich um ein Anti-Heavy-Design handelt, kann das Flussdiagramm wie folgt geändert werden:

Besondere Aufmerksamkeit ist erforderlich: Token muss weltweit eindeutig sein.

Je suppose que tu aimes

Origine blog.csdn.net/qq798280904/article/details/130722335
conseillé
Classement