Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Ontwerp berichtgebruikers zodat het verwerken van hetzelfde bericht meerdere keren dezelfde effecten heeft als de verwerking ervan. Berichtsystemen die garanderen dat een bericht ten minste één keer wordt afgeleverd, kunnen hetzelfde bericht meerdere keren afleveren. Tolerantie tegen duplicaten zorgt ervoor dat het opnieuw verwerken van een bericht geen dubbele records maakt, een klant dubbele kosten in rekening brengt of andere ongewenste effecten heeft.
Context en probleem
Gedistribueerde toepassingen wisselen werk vaak uit via een berichtenbroker in plaats van directe synchrone aanroepen te doen. De meeste brokers, waaronder Azure Service Bus, Azure Event Hubs, Apache Kafka en RabbitMQ, bieden ten minste één keer levering. Deze garantie zorgt ervoor dat een bericht een consument bereikt, zelfs wanneer er fouten optreden, maar ook dat de broker hetzelfde bericht meer dan één keer kan bezorgen.
Het is onhaalbaar om exactly-once-levering binnen een gedistribueerd systeem te garanderen. Zelfs brokers die exact eenmaal semantiek gebruiken, kunnen alleen bewerkingen garanderen die ze rechtstreeks beheren, zoals het leveren van berichten aan consumenten of het terugschrijven van gegevens naar de broker. Ze kunnen de bijwerkingen die consumenten in externe systemen implementeren niet controleren. De duurzame oplossing is niet om dubbele levering te elimineren, maar om het consumentenproces correct te laten verlopen. Wanneer u ten minste één keer afleveren combineert met een consument die duplicaten negeert, krijgt u effectief eenmalige verwerking.
Duplicaten kunnen ontstaan uit verschillende bronnen:
Nieuwe pogingen van de producent. Een producent verzendt een bericht, ontvangt geen bevestiging vanwege een tijdelijke netwerkfout of time-out en verzendt het bericht opnieuw. De broker bevat nu twee kopieën, ook al is het verzenden de eerste keer geslaagd.
Opnieuw afleveren na een ontbrekende ontvangstbevestiging. Een consument ontvangt en verwerkt een bericht, maar kan het bericht niet bevestigen omdat de consument vastloopt, de vergrendeling verloopt of de bevestiging verloren gaat. De broker gaat ervan uit dat het bericht niet is verwerkt en opnieuw wordt bezorgd.
Consumentenfouten tijdens de verwerking. Een consument voltooit een schrijfactie naar de database, maar crasht voordat het bericht wordt bevestigd. Een andere instantie pakt het bericht op en herhaalt de schrijfbewerking.
Oplossing
Creëer een idempotente verbruiker door deze een overzicht te laten bijhouden van berichten die hij succesvol verwerkt en berichten die al eerder zijn verwerkt over te slaan, op basis van een stabiele identifier die behouden blijft bij herlevering. De ontvanger controleert de opslag voor persistente id's om te bepalen of die id al is verwerkt, en verwerkt vervolgens het bericht of verwerpt het als duplicaat.
De kernstroom bestaat uit de volgende stappen:
- Lees het bericht en haal de ontdubbelingssleutel eruit.
- Controleer het ontdubbelingsarchief voor die sleutel.
- Als de sleutel al in de opslag bestaat, behandel het bericht dan als een duplicaat. Bevestig het bericht en stop met het verwerken ervan, optioneel het eerder vastgelegde resultaat te retourneren.
- Als de sleutel nog niet in de opslag bestaat, verwerkt u het bericht en legt u de sleutel vast in één atomaire bewerking; bevestig daarna de ontvangst van het bericht.
De volgende secties geven richtlijnen voor het idempotent maken van consumenten:
Een stabiele ontdubbelingssleutel kiezen
De ontdubbelingssleutel moet uniek en consistent het logische bericht voor elke herlevering identificeren. Gebruik een door een producent toegewezen bericht-id of een idempotentiesleutel op bedrijfsniveau die de specifieke logische bewerking identificeert, niet een gedeelde correlatiecontext die meerdere berichten kunnen bevatten.
Stel bijvoorbeeld de eigenschap Service Bus MessageId in op een waarde die het logische bericht uniek identificeert. Gebruik deze niet CorrelationId als de sleutel, omdat deze verwijst naar groepen gerelateerde berichten, zoals een aanvraag en de bijbehorende antwoorden. Voor gebeurtenissen die de CloudEvents-specificatie volgen, identificeert de combinatie van de kenmerken source en id een gebeurtenis op unieke wijze en blijft deze stabiel bij opnieuw afleveren.
Baseer je niet op ID's op transportniveau die de broker bij herlevering opnieuw genereert, of op waarden die zijn afgeleid van afleverpogingen. Deze waarden veranderen tussen leveringen en verslaan dubbele detectie. Vermijd ook het afleiden van de sleutel uit vluchtige velden, zoals het ontvangen van tijdstempels.
Wanneer meer dan één onafhankelijke consument hetzelfde kanaal verwerkt, zoals meerdere abonnees in een ontwerp voor publiceren-abonneren, ontvangt elke consument een eigen kopie van een bericht en moet de voltooiing van de verwerking onafhankelijk bijhouden. Als de consumenten een deduplicatieopslag delen, moeten de records worden gebaseerd op een combinatie van de identiteit van de consument en de identiteit van het bericht. Een opslag die alleen is gebaseerd op de berichtidentiteit, zou ervoor zorgen dat de eerste consument de verwerking voor alle andere consumers onderdrukt.
Bepalen waar verwerkte sleutels moeten worden opgeslagen
De volgende opslagopties zijn gebruikelijk voor verwerkte sleutels:
Een speciale ontdubbelingstabel. De afnemer onderhoudt een afzonderlijke tabel, soms een inbox genoemd, met één rij per verwerkte sleutel. Deze benadering houdt de ontdubbelingsproblemen gescheiden van bedrijfsgegevens en werkt goed als veel berichttypen hetzelfde mechanisme delen.
De bedrijfsentiteit zelf. De consument slaat de sleutel op in de record die door het bericht wordt gemaakt of bijgewerkt. Deze benadering voorkomt een afzonderlijke tabel, maar koppelt ontdubbeling aan het type zakelijke gegevens.
Leg de verwerkte sleutel en de bijwerkingen atomisch vast
Een stroom waarbij eerst wordt gecontroleerd en daarna verwerkt, heeft een foutvenster. Als een consument een bericht verwerkt en vervolgens de sleutel in een afzonderlijke stap vastlegt, kan een crash tussen die twee bewerkingen ertoe leiden dat de neveneffecten al zijn doorgevoerd, maar de sleutel niet is vastgelegd, waardoor de consument de eerstvolgende herlevering opnieuw verwerkt.
Vermijd dit foutvenster door de ontdubbelingsmarkering en de zakelijke neveneffecten in dezelfde transactie te schrijven. Als een consument beide bewerkingen samen commit, of helemaal niet, vindt die bij herlevering óf de markering en slaat het bericht over, óf geen markering omdat de transactie niet is voltooid en verwerkt het bericht veilig opnieuw. Deze transactionele variant is het Inbox-pattern en vormt aan de consumentzijde de tegenhanger van het Transactional Outbox-pattern aan de producentzijde.
Bescherming tegen gelijktijdige duplicaten
Bij ten minste één levering met gelijktijdige concurrerende consumenten kunnen twee exemplaren tegelijkertijd kopieën van hetzelfde bericht ontvangen. Beide instanties kunnen de bestaanscontrole doorstaan voordat een van beide de wijziging vastlegt, dus de controle alleen voorkomt geen dubbele verwerking.
Dwing de integriteit af in de gegevensopslag in plaats van via de applicatielogica door de volgende stappen te volgen:
Gebruik een uniekheidsbeperking voor de ontdubbelingssleutel, zodat twee transacties een sleutel kunnen invoegen, maar slechts één kan slagen. De andere transactie mislukt de beperking en behandelt het bericht als een duplicaat. Deze benadering maakt de database de individuele arbiter van het conflict.
Vermijd racecondities van het type check-then-set in caches. Een patroon dat een sleutel controleert en deze vervolgens in twee afzonderlijke bewerkingen instelt, heeft een venster waarmee gelijktijdige nieuwe pogingen de sleutel kunnen claimen. Gebruik een atomaire voorwaardelijke schrijfbewerking, zoals een insertbewerking die mislukt bij een conflict of een bewerking die alleen wordt uitgevoerd als de sleutel nog niet bestaat, om het claimen van de sleutel tot één atomaire stap te maken.
Neveneffecten verwerken die niet aan de transactie kunnen deelnemen
Sommige processen kunnen niet deelnemen aan de databasetransactie van de consument, zoals het aanroepen van een API van derden of het schrijven naar een externe winkel. Gebruik de volgende tweefasige benadering voor deze processen:
- Registreer de sleutel met de status in uitvoering en voer vervolgens de externe actie uit.
- Werk de record bij naar voltooid en sla het resultaat op.
Bij herlevering geeft een voltooid record aan dat de afnemer de aanroep niet opnieuw moet uitvoeren. Een voortgangsrecord geeft aan dat een eerdere poging gedeeltelijk kan worden voltooid of door een andere consument wordt verwerkt. De afnemer moet verouderde records afstemmen of onopgeloste gevallen doorsturen voor interventie voordat de herlevering wordt bevestigd.
Problemen en overwegingen
Houd rekening met de volgende punten wanneer u besluit hoe u dit patroon implementeert:
Gebruik bij voorkeur van nature idempotente bewerkingen. Sommige bewerkingen zijn inherent idempotent en hebben geen ontdubbelingsboekhouding nodig. Een upsert-sleutel voor een bedrijfs-id, een schrijfbewerking waarmee een absolute waarde wordt ingesteld in plaats van een verhoging, of een HTTP
PUTnaar een resource-id produceren dezelfde resultaten, ongeacht of deze eenmaal of meerdere keren worden uitgevoerd.Soms kunt u een bewerking natuurlijk idempotent maken met behulp van door gebeurtenissen overgedragen statusoverdracht. Het bericht bevat de resulterende absolute toestand, zoals de nieuwe status van een bestelling, zodat de afnemer deze als een upsert verwerkt in plaats van als een relatieve wijziging.
Tip
Ontwerp indien mogelijk voor natuurlijke idempotentie en gebruik technieken voor ontdubbeling alleen voor bewerkingen die niet van nature idempotent kunnen worden gemaakt.
Gebruik een berichtenframework in plaats van ontdubbeling te configureren. Het correct implementeren van ontdubbelingsopslag, doorvoeren en opschonen is foutgevoelig. Frameworks op basis van berichten bieden dit patroon als een ingebouwde functie.
NServiceBus ontdubbelt bijvoorbeeld binnenkomende berichten op basis van hun bericht-id's en biedt configureerbare retentie en opschoning voor ontdubbelingsgegevens. De MassTransit consumer outbox houdt ontvangen berichten bij aan de hand van hun bericht-id's om exactly-once-verwerking door consumers te bieden.
De levenscyclus van ontdubbelingsrecords beheren. Ontdubbelingsrecords stapelen zich op, tenzij u instelt dat ze verlopen. Bewaar elke record ten minste zolang de broker het oorspronkelijke bericht nog steeds opnieuw kan verzenden. De grootte van dit venster hangt af van het maximale aantal afleverpogingen van de broker, de time-out voor vergrendeling of zichtbaarheid en de levensduur van het bericht.
Stel een bewaartermijn in voor deduplicatierecords die langer is dan dit venster, zodat een late herlevering de marker nog steeds vindt. Als u records te vroeg verwijdert, wordt het venster voor duplicaten opnieuw geopend. Houd rekening met berichten die operators opnieuw indienen vanuit wachtrijen met dode letters, omdat deze nieuwe aanvragen lang kunnen optreden nadat het normale venster voor opnieuw afspelen is gesloten.
Gebruik brokerdeduplicatie niet ter vervanging van idempotente consumerlogica. Sommige platforms filteren duplicaten op de transportlaag. Bijvoorbeeld negeert Service Bus duplicaatdetectie berichten waarin een
MessageIdbinnen een geconfigureerd tijdvenster wordt herhaald, waardoor nieuwe verzendpogingen van dubbele producenten worden onderdrukt.Deze functie werkt aan de verzendzijde en in een begrensd venster, zodat een consument niet twee keer hetzelfde bericht kan verwerken na een herlevering. U hebt nog steeds idempotente consumentenlogica nodig. Gebruik platformfuncties om het dubbele volume te verminderen, niet als vervanging voor het Idempotent Consumer-patroon.
Houd rekening met berichtvolgorde. Deduplicatie verwijdert duplicaten, maar garandeert de volgorde niet. Als de consument afhankelijk is van de verwerkingsorder, combineert u dit patroon met een bestelmechanisme, zoals Service Bus berichtsessies, of neemt u reeks- of versiegegevens op, zodat de consument verouderde berichten kan weigeren.
Instrument voor waarneembaarheid. De ontdubbelingssleutel en een correlatie-id verzenden in gestructureerde logboeken en een metrische waarde bijhouden voor gedetecteerde duplicaten. Een stijgend duplicaatpercentage kan wijzen op een onjuiste configuratie van de producer, een te klein bevestigings- of vergrendelingsvenster, of niet goed functionerende consumers. Gebruik gedistribueerde tracering en correlatie om een bericht in verschillende services te volgen.
Geef idempotentie door aan onderliggende aanroepen. Het idempotent maken van de berichtconsumer beschermt de services die deze aanroept niet. Wanneer een consument downstreamservices aanroept als onderdeel van de verwerking, geeft u de idempotentiesleutel door, zodat elke servicelaag zijn eigen werk kan ontdubbelen.
Wanneer gebruikt u dit patroon?
Gebruik dit patroon wanneer:
U ontvangt berichten van een broker die garandeert dat berichten ten minste één keer worden afgeleverd, wat de standaard is voor de meeste brokers.
Het opnieuw verwerken van een bericht kan onjuiste resultaten opleveren, zoals dubbele financiële transacties, het maken van dubbele resources of herhaalde meldingen.
Meerdere concurrerende consumenten verwerken hetzelfde kanaal, waardoor gelijktijdige dubbele levering waarschijnlijker wordt.
Dit patroon is mogelijk niet geschikt wanneer:
De bewerkingen die de consument uitvoert, zijn al van nature idempotent, dus herverwerking is ongevaarlijk en ontdubbelingsboekhouding voegt kosten toe zonder voordeel.
De workload kan de effecten van incidentele dubbele verwerking tolereren en de kosten van een ontdubbelingsopslag wegen op tegen het effect van een duplicaat.
Idempotente verwerking buiten berichtenverkeer
Dit patroon past idempotentie toe op berichtgebruikers, maar idempotente verwerking is een breder betrouwbaarheidsprincipe dat kan profiteren van elke bewerking die meer dan één keer wordt uitgevoerd ten opzichte van een identieke taak. Dit principe omvat ETL-transformaties (extract, transform, load) waarmee opnieuw afgespeelde gegevens worden verwerkt, stroomverwerking die wordt hervat vanaf een controlepunt, geplande taken die overlappen of opnieuw worden opgestart, en webhook- of HTTP-eindpunten die dubbele leveringen ontvangen.
Dezelfde kerntechniek is in elk geval van toepassing.
- Gebruik een stabiele sleutel om de werkeenheid te identificeren.
- Leg vast wat u verwerkt.
- Dubbele uitvoeringen overslaan of absorberen, zodat het herhalen van het werk het resultaat niet wijzigt.
De mechanismen in dit artikel, zoals stabiele sleutels, atomische markeringen en uniekheidsbeperkingen, worden overgedragen naar deze contexten, zelfs als er geen berichtbroker betrokken is.
Ontwerp van werkbelasting
Evalueer hoe u het Idempotent Consumer-patroon gebruikt in het ontwerp van een workload om de doelstellingen en principes te verhelpen die worden behandeld in de pijlers van het Azure Well-Architected Framework. De volgende tabel bevat richtlijnen over hoe dit patroon de doelstellingen van elke pijler ondersteunt.
| Pijler | Hoe dit patroon ondersteuning biedt voor pijlerdoelen |
|---|---|
| betrouwbaarheid ontwerpbeslissingen helpen uw workload tolerant te worden defect te raken en ervoor te zorgen dat deze herstelt naar een volledig functionerende status nadat er een storing is opgetreden. | Met dit patroon kan een workload ten minste eenmaal levering en veilige pogingen gebruiken zonder beschadigde gegevens, waardoor dubbele levering van een correctheidsrisico wordt omgezet in een getolereerde voorwaarde. - RE:07 Zelfbehoud - tijdelijke fouten |
Als dit patroon compromissen binnen een pijler introduceert, moet u deze tegen de doelstellingen van de andere pijlers overwegen.
Example
In het volgende voorbeeld wordt een idempotente consument beschreven die orders van Service Bus verwerkt en de status behoudt in Azure Cosmos DB voor NoSQL.
- Een producent stelt de Service Bus
MessageIdin op een order-id op bedrijfsniveau. - De consument ontvangt het bericht in de PeekLock-modus , waardoor het bericht opnieuw kan worden uitgevoerd als de consument het bericht niet vereffent voordat de vergrendeling verloopt.
- De Azure Cosmos DB-container van de consumer partitioneert op de
/orderIdorder-id en stelt documentidin op dezelfde order-id, zodat elke kopie van een bepaalde order in dezelfde logische partitie terechtkomt en de orderidzelf als deduplicatiemarkering dient.
De consument voert de volgende stappen uit om elk bericht te verwerken:
- Lees het bericht en gebruik de
MessageIdals sleutel voor deduplicatie. - Probeer het orderdocument te maken waarbij zowel de
idals de partitiesleutel zijn ingesteld op de orderidentifier. - Als het maken is voltooid, voltooit u het bericht zodat Service Bus het uit de wachtrij verwijdert.
- Als het aanmaken mislukt met de HTTP 409-statuscode (Conflict) omdat een document met dat
idal bestaat, leest u het bestaande document en vergelijkt u het met het huidige bericht. - Als de opgeslagen aanvraag-hash of onveranderbare zakelijke velden overeenkomen, behandelt u het bericht als een duplicaat, voltooit u het en slaat u verdere verwerking over.
- Als de hash van de opgeslagen aanvraag of de onwijzigbare bedrijfsvelden niet overeenkomen, stuur het bericht dan naar een dead-letter-queue en genereer een waarschuwing, in plaats van het bericht stilzwijgend te verwijderen. De producent heeft de id mogelijk opnieuw gebruikt voor verschillende inhoud of de berichtgegevens zijn mogelijk gewijzigd sinds de order voor het eerst is verwerkt.
- Als de verwerking om een tijdelijke reden mislukt, moet u het bericht verlaten zodat Service Bus het opnieuw verzendt of de vergrendeling laat verlopen zodat een andere consument het kan ontvangen.
De maakbewerking is atomisch, dus deze fungeert als zowel de ontdubbelingscontrole als de schrijfbewerking. Twee consumenten die kopieën van hetzelfde bericht ontvangen, kunnen niet beide de bestelling maken. De ene aanmaakpoging slaagt, en de andere leidt tot een conflict en verwerpt het duplicaat veilig.
Wanneer de verwerking meer dan één document moet schrijven, gebruikt u een transactionele batch die zowel de ontdubbelingssleutel als de zakelijke documenten binnen dezelfde partitiesleutel bevat. Omdat een transactionele batch binnen één logische partitie plaatsvindt, kiest u een partitiesleutel die door alle documenten voor één bericht wordt gedeeld. De batch voert alle documenten samen of helemaal niet door, dus een crash tussen verwerking en bevestiging kan de ontdubbelingsmarkering en de bedrijfsgegevens niet synchroon laten. Een batch die probeert een document te maken dat al bestaat, retourneert de status 409 (Conflict), waarmee het duplicaat wordt geïdentificeerd.
Om deze idempotente consument ook bestand te maken tegen dubbele nieuwe verzendpogingen, schakelt u duplicaatdetectie in op de wachtrij. In een Standard- of Premium-wachtrij onderdrukt duplicaatdetectie herhaalde verzendingen in het geschiedenisvenster. De idempotente consument verwerkt nog steeds alle duplicaten die buiten dat venster vallen of ontstaan door opnieuw afgeleverde berichten.
Volgende stap
- Het ontwerpen van Azure Functions voor identieke invoer biedt richtlijnen voor het bouwen van idempotente functies die dubbele aanroepen tolereren.
Gerelateerde bronnen
Asynchrone opties voor berichten in Azure beschrijft de opties voor de berichteninfrastructuur die uw leveringsgaranties en vereisten voor dubbele verwerking bepalen.
Het Transactional Outbox-patroon, dat berichten betrouwbaar publiceert door ze op te slaan in dezelfde transactie als de bedrijfsgegevens, is de publisher-kant van het Idempotent Consumer-patroon.
Met het opnieuw-proberen-patroon kunnen toepassingen tijdelijke fouten afhandelen door bewerkingen opnieuw te proberen, waardoor idempotente verwerking noodzakelijk is omdat herhaalde pogingen dubbele aflevering kunnen veroorzaken.
Tolerant Azure Event Hubs en Azure Functions ontwerp past dit patroon toe op functies die Event Hubs activeert, inclusief ontdubbelingstechnieken voor gebeurtenisstromen.