Az egyik kedvenc interjúkérdésem: „Mit mondanak neked az olyan szavak, mint az async és a wait ?” mert ez lehetőséget ad egy érdekes beszélgetésre egy interjúalannyal… Vagy nem, mert lebegnek ebben a témában. Véleményem szerint drasztikusan fontos megérteni, miért használjuk ezt a technikát.
Úgy érzem, sok fejlesztő szívesebben hagyatkozik az „ez a legjobb gyakorlat” kijelentésre, és vakon használ aszinkron módszereket.
Ez a cikk bemutatja az aszinkron és a szinkron módszerek közötti különbséget a gyakorlatban.
A benchmarkot a következő módon fogom lefuttatni. Két független sáska-példány fut két gépen. A Locust példányok olyan felhasználót szimulálnak, aki a következőket teszi:
A motorháztető alatt minden App Service csatlakozik a saját adatbázisához, és végrehajt egy SELECT lekérdezést, amely öt másodpercig tart, és néhány adatsort ad vissza. Lásd alább a vezérlő kódját a hivatkozásokért. Dapperrel hívom fel az adatbázist. Szeretném felhívni a figyelmet arra, hogy az aszinkron végpont aszinkron módon is hívja az adatbázist ( QueryAsync<T> ).
Érdemes hozzátenni, hogy ugyanazt a kódot telepítem mindkét alkalmazásszolgáltatásra.
A teszt során a felhasználók száma egyenletesen nő a célszámra ( Users Number ). A növekedés sebességét a Spawn Rate paraméter szabályozza (másodpercenként csatlakozó egyedi felhasználók száma) – minél nagyobb ez a szám, annál gyorsabban kerülnek hozzáadásra a felhasználók. A megjelenési sebesség 10 felhasználó/s minden kísérletnél.
Minden kísérlet 15 percre korlátozódik.
A gép konfigurációs részleteit a cikk Műszaki részletek szakaszában találja.
A piros vonalak az aszinkron, a kék vonalak pedig a szinkron végpontra utalnak.
Ennyit az elméletről. Kezdjük.
Láthatjuk, hogy mindkét végpont hasonlóan teljesít – percenként körülbelül 750 kérést kezel, 5200 ms medián válaszidővel.
Ebben a kísérletben a leglenyűgözőbb grafikon egy száltrend. A szinkron végpontnál lényegesen magasabb számokat láthat (kék grafikon) – több mint 100 szál!
Ez azonban várható, és megfelel az elméletnek – amikor beérkezik egy kérés, és az alkalmazás felhívja az adatbázist, a szál blokkolva lesz, mert meg kell várnia a körút befejezését. Ezért amikor újabb kérés érkezik, az alkalmazásnak új szálat kell létrehoznia a kezeléséhez.
A piros grafikon – az aszinkron végponti szálak száma – eltérő viselkedést bizonyít. Amikor beérkezik egy kérés, és az alkalmazás felhívja az adatbázist, a szál blokkolása helyett visszatér egy szálkészletbe. Ezért, amikor újabb kérés érkezik, ez az ingyenes szál újrafelhasználásra kerül. Annak ellenére, hogy a bejövő kérések száma növekszik, az alkalmazás nem igényel új szálakat, így azok száma változatlan marad.
Érdemes megemlíteni a 3. mérőszámot – a medián válaszidőt . Mindkét végpont ugyanazt az eredményt mutatta – 5200 ms. Tehát teljesítményben nincs különbség.
Most itt az ideje felhúzni a tétet.
Megdupláztuk a terhelést. Az aszinkron végpont sikeresen kezeli ezt a feladatot – percenkénti kérése 1500 körül mozog. A szinkron testvér végül elérte a hasonló számot, 1410-et. De ha megnézi az alábbi grafikont, látni fogja, hogy 10 percig tartott!
Ennek az az oka, hogy a szinkron végpont egy új felhasználó érkezésére egy másik szál létrehozásával reagál, de a felhasználókat gyorsabban adják hozzá a rendszerhez (csak emlékeztetve arra, hogy a Spawn Rate 10 felhasználó/s), mint ahogy a webszerver alkalmazkodni tud. Ez az oka annak, hogy a kezdet kezdetén sok kérést állított sorba.
Nem meglepő módon a szálszám mutatója továbbra is 34 körül van az aszinkron végpontnál, míg a szinkronnál 102-ről 155-re nőtt. A medián válaszidő a percenkénti kérés sebességéhez hasonlóan csökkent – a szinkron válaszidő sokkal magasabb volt a kísérlet elején. Ha 24 órán keresztül megtartottam volna a tesztet, a medián számok párosak lettek volna.
A harmadik kísérlet célja a második során feltárt trendek bizonyítása – a szinkron végpont további romlása látható.
A szinkron műveletek helyett aszinkron használata nem javítja közvetlenül a teljesítményt vagy a felhasználói élményt. Először is növeli a stabilitást és a kiszámíthatóságot nyomás alatt. Más szavakkal, megemeli a terhelési küszöböt, így a rendszer többet tud feldolgozni, mielőtt leromlik.
A legtisztább teszteredmény eléréséhez teszteket kellett volna futtatnom 2 virtuális gépről, amelyek ugyanabban a hálózatban találhatók, ahol a cél App Services található.
Feltételeztem azonban, hogy a hálózati késés többé-kevésbé hasonló módon hat mindkét alkalmazásra. Ezért nem veszélyeztetheti a fő célt – az aszinkron és a szinkron módszerek viselkedésének összehasonlítását.
Mit törtem fel, hogy a szinkron végpontot majdnem ugyanolyan aszinkron működésre kényszerítsem, és ábrázoljam az alábbi grafikont (a kísérlet feltételei ugyanazok, mint a harmadiknál – 200 felhasználó)?