Üzenetkezelők AWS-ben: SQS FIFO Queue
|

Ez a cikk egy, az elosztott rendszerek és a mikroszervíz-alapú alkalmazások komponensei közötti üzenetküldési lehetőségekkel foglalkozó cikksorozat második része. Az első rész az SQS Standard Queue-ról szólt, a sorozat további részeiben tervezzük az SNS, a Kinesis és az Amazon MQ tárgyalását.

Összefoglalva az előző részt, elmondjuk, hogy az SQS-ben definiált standard üzenetsorok olyanok,

  • amibe több producer is küldhet üzeneteket
  • amikben az üzenetek várakoznak, amíg ki nem veszi őket egy consumer feldolgozásra (consumerekből szintén lehet több),
  • vagy amíg el nem évülnek (az idő állítható).
  • amikből a kivett üzenetek láthatatlanok lesznek, de a sorban maradnak a láthatatlansági időablak leteltéig, és
  • a consumer dolga a kivett (és így láthatatlanná tett) üzenetek törlése a feldolgozás végével, de
  • a láthatatlan üzenet a láthatatlansági időablak leteltével újra megjelenik, ha nem törli az őt feldolgozó consumer (így biztosítható, hogy ha feldolgozás kellős közepén elhalálozik a consumer, az üzenet ne vesszen el),
  • és amikbe legfeljebb 256 kByte-nyi, szövegként kezelt üzenetek kerülhetnek.

A szolgáltatás maga automatikusan skálázódik (nem kell semmiféle erőforrást befoglalnod) és magas rendelkezésre állású, azaz neked ezzekkel nem kell foglalkoznod, a producereid nyomhatják, ami a csövön befér. Ennek a jóféle rugalmasságnak ára van:

  • az üzenetek nem feltétlen olyan sorrendben jönnek ki a csövön, mint ahogy bementek, és
  • lesznek dupla üzeneteid: ezek akkor is többször jönnek ki a csövön, ha csak egyszer tetted be őket.

A dupla üzenetek problémája kezelhető (például kapnak azonosítót, és te a consumer oldalon tárolod, hogy mit dolgoztál már fel), de a sorrend felborulása ellen semmit nem tudsz tenni. Van, amikor ez nem baj,

  • mert mindegy, hogy melyik gép/melós/akármi jelezte két másodperccel később, hogy elkészült a feladattal,
  • mert nem érdekes, hogy melyik megrendelést kapod meg egy másodperccel korábban a webshopodban (ha jól csinálod a webshopodat),
  • mert kit érdekel, hogy melyik felhasználó készül el később a tiktok-konkurens alkalmazásodban,
  • és főleg nem érdekes, hogy a megtekintettségi statisztikák milyen sorrendben frissülnek.

De van, amikor az üzenetek sorrendje életbevágó, mert

  • a bankodban 1000 forintos folyószámlával bíró emberke esetében nagyon nem mindegy, hogy melyik 600 forintos számlát akarta először kifizetni a kettő közül,
  • nem mindegy, hogy a webes sakkjátékodban előbb a gyaloggal lépett a világos, vagy a bástyával,
  • a hőerőműved rendesen 132 fokos nyomottcsövű részében nem mindegy, hogy először esett a hőmérséklet 10 fokot, és az automatának szenet kell bepakolnia, vagy először emelkedett 10 fokot, és ki kell nyitni egy biztonsági szelepet.

Ilyenek az SQS FIFO sorok

A fent vázolt nem-mindegy kérdést - immáron öt esztendeje, 2016-ban - az AWS is elég fontosnak vélte ahhoz, hogy előálljon az SQS új variánsával, az SQS FIFO-val. FIFO annyit tesz, mint First-In, First-Out, azaz aki elsőnek ment be, az elsőnek is jön ki. Sorrendiség megtartása megoldva. És - mintegy mellékesen - az ilyen SQS-sorok biztosítják azt is, hogy minden üzenetet pontosan egyszer kapj meg.

A FIFO sorok használata némileg bonyolultabb a Standard sorokénál, és nem csak azért, mert a FIFO sorok neve kötelezően .fifo-ra végződik. Ha létrehozunk egy FIFO sort, aminek az URL-je mondjuk https://sqs.eu-central-1.amazonaws.com/121212121212/sor.fifo, akkor a múlkor jól működő

aws sqs send-message --queue-url https://sqs.eu-west-1.amazonaws.com/121212121212/sor --message-body "Szevasz!"
paranccsal az AWS nem lesz elégedett, és a következő hibaüzenetet olvashatjuk: The request must contain the parameter MessageGroupId. A hibaüzenet utolsó szavában elrejtve ott van a lényegi különbség: a FIFO sorokban létre kell hoznunk üzenetcsoportokat (message group). Az üzenetcsoportok létrehozása nem bonyolult: egyszerűen csak megadunk egy újabb paramétert a parancssorban:
aws sqs send-message --queue-url https://sqs.eu-central-1.amazonaws.com/121212121212/sor.fifo --message-group-id "csoport01" --message-body "Szevasz!"
Ha alapértelmezett beállításokkal hoztuk létre a sorunkat, akkor most megint panaszkodás következik, hiszen The queue should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly, azaz vagy kapcsoljuk be a tartalomalapú deduplikációt, vagy adjunk meg deduplikációs azonosítót. Ez a Deduplication ID külön történet, majd lentebb sort kerítünk még rá. Most hozzunk létre egy új sort magunknak a webes konzolon, és tegyünk pipát a Content-based deduplication lehetőség mellé. Ha ezt a lehetőséget használjuk, akkor nem kötelező (de lehetséges) a Deduplication ID használata, és mi a következő példában nem is használunk ilyet.

Üzenetcsoportok (Message Groups)

SQS FIFO Queue működése

Hogy mi az üzenetcsoport, ábránkból aliganem egyértelműen kiderül, de azért le is írjuk, hogy egyetlen FIFO soron belül lényegében több sorunk van, mindegyik üzenetet egy üzenetcsoportba kell tennünk.

Az üzenetcsoportok létjogosultságát egy bankos példával világítjuk meg. Monjuk hogy mi vagyunk a bank, és vannak ügyfeleink, akik fizetgetnek a kártyájukkal. Azt már korábban átgondoltuk, hogy - hacsak nem végtelen egy ügyfél bankszámlája és/vagy hitelkerete -, nagyon nem mindegy, hogy mit fizetett először, hiszen mi ennek fényében fogadjuk el vagy utasítjuk el a fizetést. Namármost, a FIFO soroknál úgy van, hogy egy adott üzenetcsoportból egyszerre egy üzenetet ad ki az AWS feldolgozásra, és ha nekünk hat consumerünk van, akkor amíg az első fizetést feldolgozza az első consumer, a maradék öt tétlen. Az üzenetek folyamatosan érkeznek, hiszen más ügyfeleink is használják közben a kártyájukat. A megoldás például az lehet, hogy ügyfélazonosítónként külön üzenetsorba tesszük az üzeneteket, és ilyenkor egyszerre sok üzenetcsoportunk van. A hat consumer egyszerre hat üzenetet dolgoz fel párhuzamosan. Az AWS úgy mondja, hogy az SQS egy-egy üzenet elkérésekor mindig a "next best" üzenetet adja, ami józan paraszti ész szerint valami időbeliséget feltételez. Mármint az üzenetek beérkezésének ideje alapján - így megvalósul, hogy amikor valamelyik cég egymillió kártyájával egyszerre fizet az egymillió alkalmazott, közben azért a mi kis szerény kávénk is kifizetődik. Egyébiránt az üzenetek elérésekor meg sem tudjuk mondani, hogy melyik üzenetcsoportból kérjük a következőt.

Ha az üzenetcsoportokra nincs szükségünk, mert mondjuk egyetlen producerünk és consumerünk van, akkor is használnunk kell legalább egy csoportot, és minden üzenet abba kerül. És hogy miért jó az, hogy egy csoportból egyszerre csak egy üzenet jön ki? Például azért, mert így a te consumer rendszereden belül is megmarad az üzenetek sorrendje anélkül, hogy figyelnél rá, nem lesz az, hogy a gyorsabb consumered beelőzi egy később kapott üzenettel a lassabb consumered korábban kapott üzenetét.

Ha létrehozunk egy olyan sort, amiben kipipáltuk a Content-based deduplication lehetőséget, akkor adjuk ki az

aws sqs send-message --queue-url https://sqs.eu-central-1.amazonaws.com/121212121212/sor-contentbased-deduplication.fifo --message-group-id "csoport01" --message-body "Szevasz!"

parancsot, mondjuk ötször (természetesen a saját sorunk URL-jével helyettesítve a példában szereplő URL-t). A parancsból ismét látszik, hogy az üzenetcsoportot nem kell előre létrehozni, elég, ha csak megadjuk a nevét, és dinamikusan létrejön. Ha az öt parancs kiadása után (és most feltételezzük, hogy az első és az utolsó parancs kiadása után nem telt még el öt perc), a

aws sqs get-queue-attributes --queue-url https://sqs.eu-central-1.amazonaws.com/596217928582/sor-contentbased-deduplication.fifo --attribute-names All

parancsot is kiadjuk, akkor látjuk, hogy:

{
    "Attributes": {
        "QueueArn": "arn:aws:sqs:eu-central-1:121212121212:sor-contentbased-deduplication.fifo",
        "ApproximateNumberOfMessages": "1",
...

azaz az üzenet nem kerül be újra a sorba, hiszen a tartalmuk megegyezik. Megjegyzendő, hogy attól még elfogadja az SQS a másik négy üzenetet, és csöndben lenyeli őket. Hogy ez meg miért jó? Mert ha pont akkor megy el a hálózati kapcsolat, amikor az SQS már megkapta az üzenetet, de az alkalmazásod erről még nem értesült (nem ér vissza a HTTP 200), akkor csuklás nélkül újraküldheti, és nem kell törődnie vele, hogy most akkor ez az üzenet volt-e már vagy sem. Mindez az öt percen belüli újraküldésekre igaz.

A Deduplication ID

Az előző példánkban a tartalom alapján deduplikáló sort használtunk - ez a sor az elsőtől számított öt percen belül érkező, ugyanolyan tartalmú üzenetet lenyeli. Ha öt perc elteltével küldünk ugynolyan tartalommal üzenetet, az már bekerül a sorba.

Ha nem állítottuk be a Content-based deduplication lehetőséget a FIFO sor létrehozásakor, akkor pedig - mint azt már egy hibaüzenetből megtudtuk - szigorúan meg kell adnunk az üzenet dedup-azonosítóját is az üzenet feladásával egyszerre. Ha kiadjuk az alábbi két parancsot (figyelve az azonos dedup-azonosítóra, de a különböző üzenetszövegre)

aws sqs send-message --queue-url https://sqs.eu-central-1.amazonaws.com/121212121212/sor.fifo --message-group-id "csoport01" --message-deduplication-id "valami" --message-body "Szevasz!"
aws sqs send-message --queue-url https://sqs.eu-central-1.amazonaws.com/121212121212/sor.fifo --message-group-id "csoport01" --message-deduplication-id "valami" --message-body "Bonjour!"

majd ezt követően lekérdezzük az üzenetek számát:

aws sqs get-queue-attributes --queue-url https://sqs.eu-central-1.amazonaws.com/121212121212/sor.fifo --attribute-names All
{
    "Attributes": {
        "QueueArn": "arn:aws:sqs:eu-central-1:121212121212:sor.fifo",
        "ApproximateNumberOfMessages": "1",

akkor látjuk, hogy bizony csak egy üzenet van a sorban. De melyik? Az én logikám azt sugallaná, hogy a második, mert az felülírja az elsőt az azonos dedup-azonosító miatt. Nos, ennyit a logikámról:

aws sqs receive-message --queue-url https://sqs.eu-central-1.amazonaws.com/121212121212/sor.fifo
{
    "Messages": [
        {
            "MessageId": "1076afe1-a490-4f0d-ae31-ce5d6c65f70f",
            "ReceiptHandle": "zzQf87!+(W...HaM)i",
            "MD5OfBody": "a543601bcf9a1e177e3eb57ce9d15d78",
            "Body": "Szevasz!"
        }
    ]
}

A Content-based deduplication pedig úgy működik, hogy a színfalak mögött azért ott is van dedup-azonosító, amit az SQS saját maga generál a tartalom alapján. Úgy érhető remekül tetten ez a viselkedés, hogy egy tartalomalapú deduplikációt végző sorba feladunk egy üzenetet a --message-deduplication-id kapcsoló nélkül, meg egy másikat, ugyanazzal a tartalommal, de a kapcsolónak valami értéket adva. Ilyenkor bekerül mindkét üzenet a sorba.

Megjegyzendő, hogy a dedup-azonosítóknak nem üzenetcsoportonként, hanem soronként kell egyedinek lennie.

A FIFO sorok korlátja

Míg a Standard sorok másodpercenként végtelen üzenetet tudnak feldolgozni (az AWS mondja, a Föld többi lakójához hasonlóan mi sem próbáltuk ki), a FIFO soroknál ez bizony nem így van. Alapvetően három műveletet végezhetünk egy üzenettel: elküldhetjük (send), elkérhetjük (receive) és törölhetjük (delete) őket. Mindhárom utasításból másodpercenkét 300-at adhatunk ki, ami végső soron 300 üzenet feldolgozását teszi lehetővé. Létezik olyan, hogy az SQS FIFO sorának üzeneteit kötegelve kezeljük, így ez a szám 3000-re tornázható fel. Ha ennél több üzenet kell, akkor elgondolkodhatunk, hogy egyszerűbb-e több SQS FIFO sort használatba venni, vagy trükközünk a Standard sorokkal. Házi feladat: legfeljebb hány üzenet lehet egy kötegben?

Cikksorozatunk következő részében az SQS-nek búcsút mondva a Kinesis-szel kezdünk ismerkedni behatóan.

[Vissza a bejegyzésekhez]