Bästa praxis för storskaliga IoT-enhetsutplaceringar

Det kan vara svårt att skala en IoT-lösning till miljontals enheter. Storskaliga lösningar måste ofta utformas i enlighet med tjänst- och prenumerationsgränser. När kunder använder Azure IoT Device Provisioning Service använder de den i kombination med andra Azure IoT-plattformstjänster och -komponenter, till exempel IoT Hub och Azure IoT-enhets-SDK:er. Den här artikeln beskriver metodtips, mönster och exempelkod som du kan använda i din design för att dra nytta av dessa tjänster och göra det möjligt för dina distributioner att skala ut. Genom att följa dessa mönster och metoder från designfasen i projektet kan du maximera prestanda för dina IoT-enheter.

Konfigurera nya enheter

Nyetablering är processen att installera en enhet för första gången som en del av en IoT-lösning. När du arbetar med storskaliga distributioner är det viktigt att schemalägga etableringsprocessen för att undvika överbelastningssituationer som orsakas av alla enheter som försöker ansluta samtidigt.

Använd ett stegvis etableringsschema

För distribution av enheter i miljonskala kan registrering av alla enheter samtidigt leda till att DPS-instansen överbelastas på grund av strypning (HTTP-svarskod 429, Too Many Requests) och att dina enheter inte registreras. Om du vill förhindra sådana begränsningar använder du ett stegvis registreringsschema för enheterna. Konfigurera batchstorlekarna för enhetsregistrering i enlighet med DPS-kvoter och -gränser. Om registreringsfrekvensen till exempel är 200 enheter per minut är batchstorleken för registrering 200 enheter per batch.

Försök igen

Om tillfälliga fel inträffar på grund av att en tjänst är upptagen kan enheter ansluta till IoT-molnet med hjälp av omprövningslogik. Ett stort antal återförsök kan dock ytterligare försämra en belastad tjänst som drivs nära sin kapacitet eller på kapacitetsgränsen. Precis som med alla Azure-tjänster bör du implementera en intelligent återförsöksmekanism med exponentiell backoff. Mer information om olika återförsöksmönster finns i designmönstret för återförsök och övergående felhantering.

I stället för att omedelbart försöka utföra en distribution igen när den begränsas väntar du tills den tid som anges i retry-after rubriken. Om det inte finns någon återförsöksrubrik tillgänglig från tjänsten kan den här algoritmen hjälpa dig att få en smidigare enhetsregistreringsupplevelse:

min_retry_delay_msec = 1000
max_retry_delay_msec = (1.0 / <load>) * <T> * 1000
max_random_jitter_msec = max_retry_delay_msec

Med den här logiken fördröjer enheterna återanslutningen under en slumpmässig tid, mellan min_retry_delay_msec och max_retry_delay_msec. Den maximala fördröjningen för återförsök beräknas med följande variabler:

  • <load> är en konfigurerbar faktor med värden > 0, vilket indikerar att belastningen utförs med ett genomsnitt av belastningstiden multiplicerat med antalet anslutningar per sekund
  • <T> är den absoluta minsta tiden för att kallstarta enheterna (beräknas som T = N / cps var N är det totala antalet enheter och cps är tjänstgränsen för antalet anslutningar per sekund).

Mer information om tidpunkten för återförsök finns i Tid för återförsök.

Omprovisionera enheter

Omkonfigurering är den process där en enhet måste konfigureras igen till en IoT-hubb efter att tidigare ha varit framgångsrikt ansluten. Det kan finnas många orsaker till att en enhet behöver återansluta till en IoT-hubb, till exempel:

  • En enhet kan startas om på grund av strömavbrott, förlust av nätverksanslutning, geo-flytt, uppdateringar av inbyggd programvara, fabriksåterställning eller rotation av certifikatnyckel.
  • IoT Hub-instansen kan vara otillgänglig på grund av ett oplanerat IoT Hub-avbrott.

Du bör inte behöva gå igenom etableringsprocessen varje gång en enhet startas om. De flesta enheter som återskapas ansluts till samma IoT-hubb. I stället bör en enhet försöka ansluta till sin IoT-hubb direkt med hjälp av den information som cachelagrades från en tidigare lyckad anslutning.

Enheter som kan lagra en anslutningssträng

Enheter som har möjlighet att lagra sina anslutningssträngar efter den första etableringen bör göra det och försöka återansluta direkt till IoT Hub efter omstart. Det här mönstret minskar svarstiden vid anslutning till lämplig IoT-hubb. Det finns två möjliga fall här:

  • Den IoT-hubb som ska anslutas vid omstart av enheten är densamma som den tidigare anslutna IoT-hubben.

    Den anslutningssträng som hämtas från cacheminnet bör fungera bra och enheten kan återansluta till samma slutpunkt. Inget behov av en nystart för etableringsprocessen.

  • Den IoT-hubb som ska anslutas vid omstart av enheten skiljer sig från den tidigare anslutna IoT-hubben.

    Den anslutningssträng som lagras i minnet är felaktig. Det går inte att ansluta till samma slutpunkt, så återförsöksmekanismen för IoT Hub-anslutningen utlöses. När tröskelvärdet för IoT Hub-anslutningsfelet har nåtts utlöser återförsöksmekanismen automatiskt en ny start på etableringsprocessen.

Enheter som inte kan lagra en anslutningssträng

Vissa enheter har inte tillräckligt med utrymme eller minne för att kunna cachelagra anslutningssträngen från en tidigare lyckad IoT-hubanslutning. Dessa enheter måste omkonfigureras via DPS efter att de startats om. Använd DPS-registrerings-API:et för att registrera igen. Tänk på att antalet omregistreringar per minut är begränsat baserat på gränsen för DPS-enhetsregistrering.

Exempel på nykonfigurering

Kodexemplen i det här avsnittet visar en klass för att läsa till och skriva från enhetens cache. Därefter visas kod som försöker återansluta en enhet till IoT-hubben om en anslutningssträng hittas, och reprovision via DPS om den inte gör det.

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace ProvisioningCache
{
  public class ProvisioningDetailsFileStorage : IProvisioningDetailCache
  {
    private string dataDirectory = null;

    public ProvisioningDetailsFileStorage()
    {
      dataDirectory = Environment.GetEnvironmentVariable("ProvisioningDetailsDataDirectory");
    }

    public ProvisioningResponse GetProvisioningDetailResponseFromCache(string registrationId)
    {
      try
        {
          var provisioningResponseFile = File.ReadAllText(Path.Combine(dataDirectory, registrationId));

          ProvisioningResponse response = JsonConvert.DeserializeObject<ProvisioningResponse>(provisioningResponseFile);

          return response;
        }
      catch (Exception ex)
      {
        return null;
      }
    }

    public void SetProvisioningDetailResponse(string registrationId, ProvisioningResponse provisioningDetails)
    {
      var provisioningDetailsJson = JsonConvert.SerializeObject(provisioningDetails);

      File.WriteAllText(Path.Combine(dataDirectory, registrationId), provisioningDetailsJson);
    }
  }
}

Du kan använda kod som liknar följande för att avgöra hur du fortsätter med att återansluta en enhet när du har fastställt om det finns anslutningsinformation i cacheminnet:

IProvisioningDetailCache provisioningDetailCache = new ProvisioningDetailsFileStorage();

var provisioningDetails = provisioningDetailCache.GetProvisioningDetailResponseFromCache(registrationId);

// If no info is available in cache, go through DPS for provisioning
if(provisioningDetails == null)
{
  logger.LogInformation($"Initializing the device provisioning client...");
  using var transport = new ProvisioningTransportHandlerAmqp();
  ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(dpsEndpoint, dpsScopeId, security, transport);
  logger.LogInformation($"Initialized for registration Id {security.GetRegistrationID()}.");
  logger.LogInformation("Registering with the device provisioning service... ");

  // This method will attempt to retry in case of a transient fault
  DeviceRegistrationResult result = await registerDevice(provClient);
  provisioningDetails = new ProvisioningResponse() { iotHubHostName = result.AssignedHub, deviceId = result.DeviceId };
  provisioningDetailCache.SetProvisioningDetailResponse(registrationId, provisioningDetails);
}

// If there was IoT Hub info from previous provisioning in the cache, try connecting to the IoT hub directly
// If trying to connect to the IoT hub returns status 429, make sure to retry operation honoring
//   the retry-after header
// If trying to connect to the IoT hub returns a 500-series server error, have an exponential backoff with
//   at least 5 seconds of wait-time
// For all response codes 429 and 5xx, reprovision through DPS
// Ideally, you should also support a method to manually trigger provisioning on demand
if (provisioningDetails != null)
{
  logger.LogInformation($"Device {provisioningDetails.deviceId} registered to {provisioningDetails.iotHubHostName}.");
  logger.LogInformation("Creating TPM authentication for IoT Hub...");
  IAuthenticationMethod auth = new DeviceAuthenticationWithTpm(provisioningDetails.deviceId, security);
  logger.LogInformation($"Testing the provisioned device with IoT Hub...");
  DeviceClient iotClient = DeviceClient.Create(provisioningDetails.iotHubHostName, auth, TransportType.Amqp);
  logger.LogInformation($"Registering the Method Call back for Reprovisioning...");
  await iotClient.SetMethodHandlerAsync("Reprovision",reprovisionDirectMethodCallback, iotClient);

  // Now you should start a thread into this method and do your business while the DeviceClient is still connected
  await startBackgroundWork(iotClient);
  logger.LogInformation("Wait until closed...");

  // Wait until the app unloads or is cancelled
  var cts = new CancellationTokenSource();
  AssemblyLoadContext.Default.Unloading += (ctx) => cts.Cancel();
  Console.CancelKeyPress += (sender, cpe) => cts.Cancel();

  await WhenCancelled(cts.Token);
  await iotClient.CloseAsync();
  Console.WriteLine("Finished.");
}

Anslutningsöverväganden för IoT Hub

En enskild IoT-hubb är begränsad till 1 miljon enheter plus moduler. Om du planerar att ha fler än en miljon enheter kan du begränsa antalet enheter till 1 miljon per hubb och lägga till hubbar efter behov när du ökar distributionens skala. Mer information finns i IoT Hub-kvoter och begränsning. Om du har planer för mer än en miljon enheter och du behöver stöd för dem i en viss region (till exempel i en EU-region för datahemvistkrav) kan du kontakta oss för att se till att den region som du distribuerar för har kapacitet att stödja din nuvarande och framtida skala.

När enheter ansluter till IoT Hub via DPS bör de använda följande logik som svar på felkoder vid anslutning:

  • När du tar emot något av serverfelsvaren i 500-serien försöker du igen med cachelagrade autentiseringsuppgifter eller resultatet av ett API-anrop för sökning efter enhetsregistreringsstatus.
  • När du tar emot 401, Unauthorized eller 403, Forbidden eller 404, Not Found utför du en fullständig omregistrering genom att anropa DPS-registrerings-API:et.

När som helst bör enheterna kunna svara på ett användarinitierat återetableringskommando.

Om enheter kopplas från från IoT Hub bör enheterna försöka återansluta direkt till samma IoT-hubb i 15–30 minuter innan de försöker gå tillbaka till DPS.

Andra IoT Hub-scenarier när du använder DPS:

  • Redundansväxling i IoT Hub: Enheter bör fortsätta att fungera eftersom anslutningsinformationen inte bör ändras och logiken finns för att försöka ansluta igen när hubben är tillgänglig igen.
  • Byte av IoT-hubb: Att tilldela enheter till en annan IoT-hubb bör göras med hjälp av en anpassad fördelningspolicy.
  • Försök igen IoT Hub-anslutning: Du bör inte använda en aggressiv strategi för återförsök. Tillåt i stället ett mellanrum på minst en minut innan ett nytt försök.
  • IoT Hub-partitioner: Om din enhetsstrategi lutar sig kraftigt mot telemetri bör antalet partitioner från enhet till moln ökas.

Övervaka enheter

En viktig del av den övergripande distributionen är att övervaka lösningen från slutpunkt till slutpunkt för att säkerställa att systemet fungerar korrekt. Det finns flera sätt att övervaka hälsotillståndet för en tjänst för storskalig distribution av IoT-enheter. Följande mönster visar sig vara effektiva vid övervakning av tjänsten:

  • Skapa ett program för att fråga varje registreringsgrupp på en DPS-instans, hämta det totala antalet enheter som registrerats i gruppen och aggregera sedan siffrorna från olika registreringsgrupper. Det här numret ger ett exakt antal enheter som för närvarande är registrerade via DPS och kan användas för att övervaka tjänstens tillstånd.
  • Övervaka enhetsregistreringar under en viss period. Övervaka till exempel registreringshastigheter för en DPS-instans under de senaste fem dagarna. Den här metoden ger bara en ungefärlig siffra och är också begränsad till en tidsperiod.

Nästa steg