Compartir vía


Procedimientos recomendados para implementaciones de dispositivos IoT a gran escala

El escalado de una solución IoT a millones de dispositivos puede ser complicado. A menudo, las soluciones a gran escala se deben diseñar de acuerdo con los límites del servicio y la suscripción. Cuando los clientes usan Azure IoT Device Provisioning Service, lo usan en combinación con otros servicios y componentes de plataforma de Azure IoT, como los SDK de dispositivo IoT Hub y Azure IoT. En este artículo se describen los procedimientos recomendados, los patrones y el código de ejemplo que puede incorporar en el diseño para aprovechar estos servicios y permitir que las implementaciones se escalen horizontalmente. Al seguir estos patrones y prácticas a partir de la fase de diseño del proyecto, puede maximizar el rendimiento de los dispositivos IoT.

Aprovisionar dispositivos nuevos

El aprovisionamiento por primera vez es el proceso de incorporación de un dispositivo por primera vez como parte de una solución IoT. Al trabajar con implementaciones a gran escala, es importante programar el proceso de aprovisionamiento para evitar las situaciones de sobrecarga causadas por todos los dispositivos que intentan conectarse al mismo tiempo.

Usar una programación de aprovisionamiento escalonada

En el caso de la implementación de dispositivos en la escala de millones, el registro de todos los dispositivos a la vez puede provocar que la instancia de DPS se sobrecargue debido a la limitación (código de respuesta HTTP 429, Too Many Requests) y se produzca un error al registrar los dispositivos. Para evitar esta limitación, use una programación de registro escalonada para los dispositivos. Configure los tamaños de lote de registro de dispositivos de acuerdo con las cuotas y límites de DPS. Por ejemplo, si la velocidad de registro es de 200 dispositivos por minuto, el tamaño del lote para la incorporación sería de 200 dispositivos por lote.

Reintentar operaciones

Si se producen errores transitorios debido a que un servicio está ocupado, la lógica de reintento permite a los dispositivos conectarse correctamente a la nube de IoT. Sin embargo, un gran número de reintentos podría degradar aún más un servicio ocupado que está funcionando cerca o al límite de su capacidad. Al igual que con cualquier servicio de Azure, debe implementar un mecanismo de reintento inteligente con retroceso exponencial. Puede encontrar más información sobre diferentes patrones de reintento en el patrón de diseño de reintentos y el control de errores transitorios.

En lugar de volver a intentar inmediatamente una implementación cuando se limita, espere hasta el tiempo especificado en el encabezado retry-after. Si no hay ningún encabezado de reintento disponible en el servicio, este algoritmo puede ayudar a lograr una experiencia de incorporación de dispositivos más fluida:

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

Con esta lógica, los dispositivos retrasan la reconexión durante una cantidad aleatoria de tiempo, entre min_retry_delay_msec y max_retry_delay_msec. El retraso máximo de reintento se calcula con las siguientes variables:

  • <load> es un factor configurable con valores > 0, lo que indica que la carga se realizará en un promedio de tiempo de carga multiplicado por el número de conexiones por segundo
  • <T> es el tiempo mínimo absoluto para el arranque en frío de los dispositivos (calculados como T = N / cps donde N es el número total de dispositivos y cps es el límite de servicio para el número de conexiones por segundo).

Para más información sobre la temporización de las operaciones de reintento, consulte Temporización de los reintentos.

Reaprovisionamiento de dispositivos

El reaprovisionamiento es el proceso en el que un dispositivo debe aprovisionarse en una instancia de IoT Hub después de haberse conectado correctamente anteriormente. Puede haber muchas razones por las que se necesita un dispositivo para volver a conectarse a una instancia de IoT Hub, como:

  • Un dispositivo se podría reiniciar debido a la interrupción de la alimentación, la pérdida de la conectividad de red, la reubicación geográfica, las actualizaciones de firmware, el restablecimiento de los valores de fábrica o la rotación de claves de certificados.
  • La instancia de IoT Hub podría no estar disponible debido a una interrupción de IoT Hub no planeada.

No debe tener que pasar por el proceso de aprovisionamiento cada vez que se reinicie un dispositivo. La mayoría de los dispositivos que se vuelven a aprovisionar terminan conectados al mismo centro de IoT. En su lugar, un dispositivo debe intentar conectarse a su centro de IoT directamente mediante la información almacenada en caché desde una conexión correcta anterior.

Dispositivos que pueden almacenar una cadena de conexión

Los dispositivos que tienen la capacidad de almacenar su cadena de conexión después del aprovisionamiento inicial deben hacerlo e intentar volver a conectarse directamente a IoT Hub después del reinicio. Este patrón reduce la latencia para conectarse correctamente a la instancia de IoT Hub adecuada. Hay dos casos posibles:

  • El centro de IoT al que conectarse tras el reinicio del dispositivo es el mismo que el centro de IoT conectado anteriormente.

    La cadena de conexión recuperada de la memoria caché debe funcionar correctamente y el dispositivo puede volver a conectarse al mismo punto de conexión. No es necesario un inicio nuevo para el proceso de aprovisionamiento.

  • El centro de IoT al que conectarse tras el reinicio del dispositivo es distinto del centro de IoT conectado anteriormente.

    La cadena de conexión almacenada en memoria es inexacta. El intento de conectarse al mismo punto de conexión no se realizará correctamente y, por tanto, se desencadena el mecanismo de reintento para la conexión de IoT Hub. Una vez alcanzado el umbral del error de conexión de IoT Hub, el mecanismo de reintento desencadena automáticamente un nuevo inicio en el proceso de aprovisionamiento.

Dispositivos que no pueden almacenar una cadena de conexión

Algunos dispositivos no tienen una superficie o memoria lo suficientemente grande como para acomodar el almacenamiento en caché de la cadena de conexión desde una conexión de IoT Hub correcta pasada. Estos dispositivos deben volver a aprovisionar a través de DPS después de reiniciarse. Use la API de registro de DPS para volver a registrarse. Tenga en cuenta que la cantidad de reinscripciones por minuto está limitada según el límite de registro del dispositivo DPS.

Ejemplo de reaprovisionamiento

En los ejemplos de código de esta sección se muestra una clase para leer y escribir desde la memoria caché del dispositivo, seguida de código que intenta volver a conectar un dispositivo a IoT Hub si se encuentra una cadena de conexión y se vuelve a aprovisionar a través de DPS si no lo está.

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);
    }
  }
}

Puede usar código similar al siguiente para determinar cómo continuar con la reconexión de un dispositivo después de determinar si hay información de conexión en la memoria caché:

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.");
}

Consideraciones sobre la conectividad de IoT Hub

Cualquier centro de IoT único está limitado a 1 millón de dispositivos más módulos. Si planea tener más de un millón de dispositivos, limite el número de dispositivos a 1 millón por centro y agregue centros según sea necesario al aumentar la escala de la implementación. Para más información, consulte las cuotas de IoT Hub. Si tiene planes para más de un millón de dispositivos y necesita admitirlos en una región específica (por ejemplo, en una región de la UE por los requisitos de residencia de datos), puede ponerse en contacto con nosotros para asegurarse de que la región en la que va a implementar tenga la capacidad de admitir la escala actual y futura.

Al conectarse a IoT Hub a través de DPS, los dispositivos deben usar la siguiente lógica en respuesta a los códigos de error al conectarse:

  • Al recibir cualquiera de las respuestas de error de servidor de la serie 500, vuelva a intentar la conexión con las credenciales almacenadas en caché o los resultados de una llamada API de búsqueda del estado del registro del dispositivo.
  • Al recibir un error 401, Unauthorized, 403, Forbidden o 404, Not Found, realice un nuevo registro completo mediante una llamada a la API de registro de DPS.

En cualquier momento, los dispositivos deben ser capaces de responder a un comando de reaprovisionamiento iniciado por el usuario.

Si los dispositivos se desconectan de IoT Hub, los dispositivos deben intentar volver a conectarse directamente a la misma instancia de IoT Hub durante 15-30 minutos antes de intentar volver a DPS.

Otros escenarios de IoT Hub al usar DPS:

  • Conmutación por error del centro de IoT: los dispositivos deberían seguir funcionando porque la información de conexión no debería cambiar y hay lógica en funcionamiento para reintentar la conexión una vez que el centro esté disponible de nuevo.
  • Cambio de centro de IoT: la asignación de dispositivos a otro centro de IoT se debe realizar mediante una directiva de asignación personalizada.
  • Reintentar la conexión de IoT Hub: no debe usar una estrategia de reintento agresiva. En su lugar, permita un intervalo de al menos un minuto antes de un reintento.
  • Particiones de IoT Hub: si la estrategia de dispositivos se basa en gran medida en la telemetría, se debe aumentar el número de particiones del dispositivo a la nube.

Supervisión de dispositivos

Una parte importante de la implementación general es supervisar la solución de forma integral para asegurarse de que el sistema funciona correctamente. Hay varias maneras de supervisar el estado de un servicio para la implementación a gran escala de dispositivos IoT. Los siguientes patrones han demostrado ser eficaces en la supervisión del servicio:

  • Cree una aplicación para consultar cada grupo de inscripción en una instancia de DPS, obtener el número total de dispositivos registrados en ese grupo y, a continuación, agregar las cifras de los distintos grupos de inscripción. Esta cifra proporciona un recuento exacto de los dispositivos que están registrados actualmente mediante DPS y se puede usar para supervisar el estado del servicio.
  • Supervise los registros de dispositivos durante un período específico. Por ejemplo, supervise las velocidades de registro de una instancia de DPS durante los cinco días anteriores. Tenga en cuenta que este enfoque solo proporciona una cifra aproximada y también se limita a un período de tiempo.

Pasos siguientes