Olvasás angol nyelven Szerkesztés

Megosztás a következőn keresztül:


Teljesítmény finomhangolása – Eseménystreamelés

Azure Functions
Azure IoT Hub
Azure Cosmos DB

Ez a cikk azt ismerteti, hogy egy fejlesztői csapat hogyan használt metrikákat a szűk keresztmetszetek megtalálásához és az elosztott rendszerek teljesítményének javításához. A cikk egy mintaalkalmazás tényleges terheléstesztjén alapul.

Ez a cikk egy sorozat része. Olvassa el az első részt itt.

Forgatókönyv: Események adatfolyamának feldolgozása Azure Functions használatával.

Eseménystreamelési architektúra diagramja

Ebben a forgatókönyvben egy drónflotta valós időben küld pozícióadatokat Azure IoT Hub. A Functions-alkalmazások megkapják az eseményeket, GeoJSON formátumba alakítja át az adatokat, és az átalakított adatokat az Azure Cosmos DB-be írják. Az Azure Cosmos DB natív támogatást nyújt a térinformatikai adatokhoz, és az Azure Cosmos DB-gyűjtemények indexelhetők a hatékony térbeli lekérdezésekhez. Egy ügyfélalkalmazás például lekérdezheti az összes drónt egy adott hely 1 km-es körzetében, vagy megkeresheti az összes drónt egy adott területen belül.

Ezek a feldolgozási követelmények elég egyszerűek ahhoz, hogy ne igényelnek teljes körű streamfeldolgozó motort. A feldolgozás különösen nem kapcsolja össze a streameket, az összesítő adatokat és a folyamatokat az időablakokban. Ezen követelmények alapján a Azure Functions alkalmas az üzenetek feldolgozására. Az Azure Cosmos DB a nagyon magas írási átviteli sebesség támogatásához is méretezhető.

Átviteli sebesség figyelése

Ez a forgatókönyv érdekes teljesítménnyel kapcsolatos kihívást jelent. Az eszközönkénti adatsebesség ismert, de az eszközök száma ingadozhat. Ebben az üzleti forgatókönyvben a késési követelmények nem különösebben szigorúak. A drón jelentett pozíciójának csak egy percen belül pontosnak kell lennie. Ez azt jelenti, hogy a függvényalkalmazásnak lépést kell tartania az átlagos betöltési gyakorisággal az idő múlásával.

IoT Hub naplóstreamben tárolja az üzeneteket. A bejövő üzenetek a stream széléhez vannak fűzve. A stream egy olvasója – ebben az esetben a függvényalkalmazás – szabályozza a stream bejárásának sebességét. Az olvasási és írási útvonalak leválasztása nagyon hatékonyan IoT Hub, de azt is jelenti, hogy egy lassú olvasó lemaradhat. A feltétel észleléséhez a fejlesztői csapat hozzáadott egy egyéni metrikát az üzenet késésének méréséhez. Ez a metrika rögzíti a különbözetet, amikor egy üzenet megérkezik IoT Hub, és amikor a függvény megkapja az üzenetet feldolgozásra.

var ticksUTCNow = DateTimeOffset.UtcNow;

// Track whether messages are arriving at the function late.
DateTime? firstMsgEnqueuedTicksUtc = messages[0]?.EnqueuedTimeUtc;
if (firstMsgEnqueuedTicksUtc.HasValue)
{
    CustomTelemetry.TrackMetric(
                        context,
                        "IoTHubMessagesReceivedFreshnessMsec",
                        (ticksUTCNow - firstMsgEnqueuedTicksUtc.Value).TotalMilliseconds);
}

A TrackMetric metódus egyéni metrikát ír az Application Insightsba. Az Azure-függvények használatával TrackMetric kapcsolatos információkért lásd: Egyéni telemetria a C#-függvényben.

Ha a függvény lépést tart az üzenetek mennyiségével, ennek a metrikának alacsony állandó állapotban kell maradnia. Bizonyos késés elkerülhetetlen, ezért az érték soha nem lesz nulla. Ha azonban a függvény lemarad, a lekért idő és a feldolgozási idő közötti eltérés felfelé fog menni.

1. teszt: Alapkonfiguráció

Az első terhelési teszt azonnali problémát észlelt: A függvényalkalmazás folyamatosan HTTP 429-es hibákat kapott az Azure Cosmos DB-től, ami azt jelzi, hogy az Azure Cosmos DB szabályozta az írási kéréseket.

Az Azure Cosmos DB szabályozott kéréseinek grafikonja

A csapat válaszul skálázta az Azure Cosmos DB-t a gyűjteményhez lefoglalt kérelemegységek számának növelésével, de a hibák folytatódtak. Ez furcsának tűnt, mert a borítékok hátoldali számítása azt mutatta, hogy az Azure Cosmos DB-nek nem lehet gondja az írási kérelmek mennyiségével.

A nap folyamán az egyik fejlesztő a következő e-mailt küldte a csapatnak:

Megnéztem az Azure Cosmos DB-t a meleg úthoz. Van egy dolog, amit nem értek. A partíciókulcs deliveryId, de nem küldünk kézbesítési azonosítót az Azure Cosmos DB-nek. Hiányzik valami?

Ez volt a nyom. A partíció hőtérképét vizsgálva kiderült, hogy az összes dokumentum ugyanazon a partíción landolt.

Az Azure Cosmos DB partíció hőtérképének grafikonja

Amit látni szeretne a hőtérképen, az az összes partíció egyenletes eloszlása. Ebben az esetben, mivel minden dokumentum ugyanarra a partícióra lett írva, a kérelemegységek hozzáadása nem segített. Kiderült, hogy a probléma hiba a kódban. Bár az Azure Cosmos DB-gyűjtemény rendelkezik partíciókulcsokkal, az Azure-függvény valójában nem tartalmazta a partíciókulcsot a dokumentumban. A partíció hőtérképével kapcsolatos további információkért lásd : Az átviteli sebesség eloszlásának meghatározása a partíciók között.

2. teszt: Particionálási probléma megoldása

Amikor a csapat üzembe helyezett egy kódjavítást, és újra futtatta a tesztet, az Azure Cosmos DB leállt a szabályozással. Egy darabig minden jól nézett ki. Egy bizonyos terhelésnél azonban a telemetriai adatok azt mutatták, hogy a függvény kevesebb dokumentumot írt, mint kellene. Az alábbi grafikonon IoT Hub és az Azure Cosmos DB-be írt dokumentumokból érkező üzenetek láthatók. A sárga vonal a fogadott üzenetek száma kötegenként, a zöld pedig a kötegenként írt dokumentumok száma. Ezeknek arányosnak kell lenniük. Ehelyett az adatbázis-írási műveletek száma kötegenként jelentősen csökken 07:30-kor.

Elvetett üzenetek grafikonja

A következő grafikon azt mutatja, hogy az üzenet IoT Hub eszközről való érkezésekor és az üzenet a függvényalkalmazás által feldolgozásakor eltelt késést mutatja. Láthatja, hogy ugyanabban az időpontban a késés drámaian megugrik, leesik és csökken.

Az üzenet késésének grafikonja

Ennek az az oka, hogy az érték 5 percnél nagyobb, majd nullára csökken, az az, hogy a függvényalkalmazás elveti az 5 percnél hosszabb üzeneteket:

foreach (var message in messages)
{
    // Drop stale messages,
    if (message.EnqueuedTimeUtc < cutoffTime)
    {
        log.Info($"Dropping late message batch. Enqueued time = {message.EnqueuedTimeUtc}, Cutoff = {cutoffTime}");
        droppedMessages++;
        continue;
    }
}

Ezt akkor láthatja a grafikonon, ha a késési metrika nullára csökken. Időközben az adatok elvesztek, mert a függvény üzeneteket küldött.

Mi történt? Ehhez az adott terhelési teszthez az Azure Cosmos DB-gyűjteményhez szükséges kérelemegységek voltak megkímélve, így a szűk keresztmetszet nem volt az adatbázisban. A probléma inkább az üzenetfeldolgozási hurokban volt. Egyszerűen fogalmazva, a függvény nem írt elég gyorsan dokumentumokat ahhoz, hogy lépést tartson a bejövő üzenetek mennyiségével. Idővel egyre tovább csökkent.

3. teszt: Párhuzamos írások

Ha az üzenet feldolgozásának ideje szűk keresztmetszet, az egyik megoldás, ha több üzenetet dolgoz fel párhuzamosan. Ebben a forgatókönyvben:

  • Növelje a IoT Hub partíciók számát. Minden IoT Hub partícióhoz egyszerre egy függvénypéldány lesz hozzárendelve, ezért elvárjuk, hogy az átviteli sebesség lineárisan skálázható a partíciók számával.
  • Párhuzamosítsa a dokumentumírásokat a függvényen belül.

A második lehetőség megismeréséhez a csapat módosította a függvényt a párhuzamos írások támogatásához. A függvény eredeti verziója az Azure Cosmos DB kimeneti kötését használta. Az optimalizált verzió közvetlenül meghívja az Azure Cosmos DB-ügyfelet, és párhuzamosan hajtja végre az írásokat a Task.WhenAll használatával:

private async Task<(long documentsUpserted,
                    long droppedMessages,
                    long cosmosDbTotalMilliseconds)>
                ProcessMessagesFromEventHub(
                    int taskCount,
                    int numberOfDocumentsToUpsertPerTask,
                    EventData[] messages,
                    TraceWriter log)
{
    DateTimeOffset cutoffTime = DateTimeOffset.UtcNow.AddMinutes(-5);

    var tasks = new List<Task>();

    for (var i = 0; i < taskCount; i++)
    {
        var docsToUpsert = messages
                            .Skip(i * numberOfDocumentsToUpsertPerTask)
                            .Take(numberOfDocumentsToUpsertPerTask);
        // client will attempt to create connections to the data
        // nodes on Azure Cosmos DB clusters on a range of port numbers
        tasks.Add(UpsertDocuments(i, docsToUpsert, cutoffTime, log));
    }

    await Task.WhenAll(tasks);

    return (this.UpsertedDocuments,
            this.DroppedMessages,
            this.CosmosDbTotalMilliseconds);
}

Vegye figyelembe, hogy a versenyfeltételek megközelítéssel lehetségesek. Tegyük fel, hogy ugyanabból a drónból két üzenet érkezik ugyanabban az üzenetkötegben. Ha párhuzamosan írja őket, a korábbi üzenet felülírhatja a későbbi üzenetet. Ebben az esetben az alkalmazás elviselheti az alkalmi üzenetek elvesztését. A drónok 5 másodpercenként küldenek új pozícióadatokat, így az Azure Cosmos DB adatai folyamatosan frissülnek. Más esetekben azonban fontos lehet szigorúan a sorrendben feldolgozni az üzeneteket.

A kódmódosítás üzembe helyezése után az alkalmazás több mint 2500 kérést tudott betölteni egy 32 partíciót tartalmazó IoT Hub használatával.

Ügyféloldali szempontok

Az általános ügyfélélményt csökkentheti a kiszolgálóoldali agresszív párhuzamosítás. Fontolja meg az Azure Cosmos DB tömeges végrehajtói kódtár használatát (ez a megvalósítás nem jelenik meg), ami jelentősen csökkenti az Azure Cosmos DB-tárolóhoz lefoglalt átviteli sebesség telítettségéhez szükséges ügyféloldali számítási erőforrásokat. A tömeges importálási API-val adatokat író egyetlen szálon futó alkalmazás majdnem tízszer nagyobb írási átviteli sebességet ér el egy többszálú alkalmazáshoz képest, amely párhuzamosan írja az adatokat az ügyfélszámítógép processzorának telítésével.

Összefoglalás

Ebben a forgatókönyvben a következő szűk keresztmetszeteket azonosítottuk:

  • Gyakori elérésű írási partíció az írott dokumentumokban hiányzó partíciókulcs-érték miatt.
  • Dokumentumok írása sorosan IoT Hub partíciónként.

A problémák diagnosztizálásához a fejlesztői csapat a következő metrikákra támaszkodott:

  • Szabályozott kérések az Azure Cosmos DB-ben.
  • Partíció hőtérképe – Partíciónkénti maximális felhasznált kérelemegységek.
  • A kapott üzenetek és a létrehozott dokumentumok.
  • Üzenet késése.

Következő lépések

Teljesítményelhárítók áttekintése