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.
Eszközök
- .NET Web API alkalmazás (teszt cél)
- 2 Azure SQL Databases
- 2 Azure App Service Windows rendszeren (az alkalmazást tárolja)
- Azure App Insights (mérőszámok gyűjtéséhez)
- sáska keretrendszer (a felhasználói terhelés szimulálására).
Konfiguráció
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:
- Az 1. sáskahoszt felhasználója eléri az 1. App Service szinkron végpontját , megkapja a választ, és 0,5–1 másodpercig tétlen marad (a pontos késleltetés véletlenszerű). Ismétlés a kísérlet végéig.
- A 2-es sáskahoszt felhasználója pontosan ugyanúgy viselkedik, egyetlen különbséggel – eléri az App Service 2 aszinkron végpontját .
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.
Mérések
- kérések percenként – megmutatja azoknak a kéréseknek a számát, amelyeket az alkalmazás ténylegesen feldolgozott, és állapotkódot adott vissza.
- szálak száma – az alkalmazásszolgáltatás által felhasznált szálak számát mutatja.
- medián válaszidő, ms
A piros vonalak az aszinkron, a kék vonalak pedig a szinkron végpontra utalnak.
Ennyit az elméletről. Kezdjük.
1. kísérlet
- felhasználók száma : 75 (szolgáltatásonként)
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.
2. kísérlet
- felhasználók száma : 150
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.
3. kísérlet
- felhasználók száma : 200
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ó.
Következtetés
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.
Függelék #1. Műszaki részletek
- Azure App Service: B1, 100 ACU , 1,75 Gb memória, A sorozatú számítási megfelelője.
- Azure SQL Database: Standard S4: 200 DTU, 500 Mb tárhely.
- SQL kapcsolat beállításai: Max Pool Size=200.
2. számú melléklet. Megjegyzések
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.
3. számú melléklet. Bónusz kísérlet
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ó)?