Práticas recomendadas para implantações de dispositivo IoT em grande escala
A colocação em escala de uma solução de IoT para milhões de dispositivos pode ser desafiadora. Muitas vezes, as soluções em grande escala precisam ser projetadas de acordo com os limites do serviço e da assinatura. Quando os clientes usam o Serviço de Provisionamento de Dispositivos IoT do Azure, eles o usam em combinação com outros serviços e componentes da plataforma IoT do Azure, como o Hub IoT e os SDKs do dispositivo IoT do Azure. Esse artigo descreve as práticas recomendadas, os padrões e o código de exemplo que você pode incorporar em seu design para aproveitar esses serviços e permitir a expansão de suas implantações. Seguindo esses padrões e práticas desde a fase de design do projeto, você pode maximizar o desempenho dos seus dispositivos IoT.
Provisionar novos dispositivos
O provisionamento pela primeira vez é o processo de integração de um dispositivo pela primeira vez como parte de uma solução IoT. Ao trabalhar com implantações em grande escala, é importante agendar o processo de provisionamento para evitar sobrecarga causada por todos os dispositivos que tentam se conectar ao mesmo tempo.
Use um cronograma de provisionamento escalonado
Para a implantação de dispositivos na escala de milhões, o registro de todos os dispositivos de uma só vez pode fazer com que a instância DPS seja sobrecarregada devido à limitação (código de resposta HTTP 429, Too Many Requests
) e gerar uma falha ao registrar seus dispositivos. Para evitar esse estrangulamento, utilize um calendário de registo escalonado para os dispositivos. Configure os tamanhos dos lotes de registro de dispositivos de acordo com as cotas e limites do DPS . Por exemplo, se a taxa de registro for de 200 dispositivos por minuto, o tamanho do lote para a integração será de 200 dispositivos por lote.
Repetir operações
Se ocorrerem falhas transitórias devido à ocupação de um serviço, a lógica de nova tentativa permitirá que os dispositivos se conectem com êxito à nuvem IoT. No entanto, um número elevado de novas tentativas pode degradar ainda mais um serviço ocupado em execução próximo a ou no limite de sua capacidade. Assim como acontece com qualquer serviço do Azure, um mecanismo de repetição inteligente com retirada exponencial deve ser implementado. Mais informações sobre diferentes padrões de nova tentativa podem ser encontradas em padrão de design de nova tentativa e tratamento de falhas transitórias.
Em vez de tentar novamente uma implantação imediatamente quando estiver limitada, aguarde até o horário especificado no cabeçalho retry-after
. Se não houver cabeçalho de repetição disponível no serviço, esse algoritmo poderá ajudar a obter uma experiência de integração do dispositivo mais uniforme:
min_retry_delay_msec = 1000
max_retry_delay_msec = (1.0 / <load>) * <T> * 1000
max_random_jitter_msec = max_retry_delay_msec
Com essa lógica, os dispositivos atrasam a reconexão por um período de tempo aleatório, entre min_retry_delay_msec
e max_retry_delay_msec
. O atraso máximo de nova tentativa é calculado com as seguintes variáveis:
<load>
é um fator configurável com valores > 0, que indica que a carga funcionará em uma média de tempo de carregamento multiplicado pelo número de conexões por segundo<T>
é o tempo mínimo absoluto para inicialização a frio dos dispositivos (calculado comoT = N / cps
ondeN
é o número total de dispositivos ecps
é o limite de serviço para o número de conexões por segundo).
Para obter mais informações sobre o tempo das operações de repetição, consulte Tempo de repetição.
Reprovisionar dispositivos
O reprovisionamento é o processo em que um dispositivo precisa ser provisionado para um Hub IoT depois de ter sido conectado anteriormente com sucesso. Pode haver vários motivos que resultam na necessidade de um dispositivo se reconectar a um Hub IoT, como:
- Um dispositivo pode ser reinicializado devido à interrupção de energia, perda de conectividade de rede, realocação geográfica, atualizações de firmware, redefinição de fábrica ou rotação da chave de certificado.
- A instância do Hub IoT pode estar indisponível devido a uma interrupção não planejada no Hub IoT.
Você não precisará passar pelo processo de provisionamento sempre que um dispositivo for reinicializado. A maioria dos dispositivos reprovisionados acaba conectada ao mesmo hub IoT. Em vez disso, um dispositivo deve tentar ligar-se diretamente ao seu hub IoT utilizando as informações que foram armazenadas em cache de uma ligação anterior bem sucedida.
Dispositivos que podem armazenar uma cadeia de conexão
Os dispositivos que têm a capacidade de armazenar a sua cadeia de ligação após o provisionamento inicial devem fazê-lo e tentar reconectar-se diretamente ao Hub IoT do Azure após a reinicialização. Esse padrão reduz a latência na ligação com sucesso ao Hub IoT apropriado. Neste caso, há duas causas possíveis:
O Hub IoT a ser conectado após a reinicialização do dispositivo é o mesmo que o Hub IoT conectado anteriormente.
A cadeia de conexão recuperada do cache deve funcionar bem e o dispositivo pode se reconectar ao mesmo ponto de extremidade. Não há a necessidade de um novo início para o processo de provisionamento.
O Hub IoT a ser conectado após a reinicialização do dispositivo é diferente do Hub IoT conectado anteriormente.
A cadeia de conexão armazenada na memória é imprecisa. A tentativa de se conectar ao mesmo ponto de extremidade não terá êxito e, portanto, o mecanismo de repetição da conexão do Hub IoT é disparado. Depois que o limite da falha de conexão do Hub IoT for atingido, o mecanismo de repetição disparará automaticamente um novo início para o processo de provisionamento.
Dispositivos que não podem armazenar uma cadeia de conexão
Alguns dispositivos não têm uma área ocupada ou memória suficientemente grande para acomodar o cache da cadeia de ligação de uma ligação anterior bem sucedida ao Hub IoT do Azure. Esses dispositivos precisam ser reprovisionados por meio do DPS após a reinicialização. Use a API de registro DPS para se registrar novamente. Tenha em mente que o número de novos registros por minuto é limitado com base no limite de registro do dispositivo DPS.
Exemplo de reprovisionamento
Os exemplos de código nessa secção mostram uma classe para leitura e escrita a partir da cache do dispositivo, seguida de um código que tenta reconectar um dispositivo ao Hub IoT se uma cadeia de ligação for encontrada e reprovisionar através do DPS se não for.
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);
}
}
}
Você pode usar um código semelhante ao seguinte para determinar como proceder com a reconexão de um dispositivo após determinar se há informações de conexão no cache:
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.");
}
Considerações sobre conectividade do Hub IoT
Qualquer hub IoT é limitado a um milhão de dispositivos mais módulos. Se você planeja ter mais de um milhão de dispositivos, limite o número de dispositivos para um milhão por hub e adicione hubs conforme necessário ao aumentar a escala de sua implantação. Para obter mais informações, confira Cotas do Hub IoT. Se você tiver planos para mais de um milhão de dispositivos e precisar dar suporte a eles em uma região específica (como em uma região da Europa para requisitos de residência de dados), fale conosco para garantir que a região na qual você está implantando tenha a capacidade de dar suporte à sua escala atual e futura.
Ao ligarem-se ao Hub IoT do Azure através do DPS, os dispositivos devem utilizar a seguinte lógica em resposta aos códigos de erro durante a ligação:
- Ao receber qualquer uma das 500 séries de respostas de erro do servidor, tente novamente a conexão usando as credenciais armazenadas em cache ou os resultados de uma chamada à API de Pesquisa do Status de Registro do Dispositivo.
- Ao receber
401, Unauthorized
,403, Forbidden
ou404, Not Found
, execute um novo registro completo chamando a API de registro do DPS.
Os dispositivos devem ser capazes de responder a qualquer momento a um comando de reprovisionamento iniciado pelo usuário.
Se os dispositivos forem desconectados do Hub IoT do Azure, os dispositivos deverão tentar reconectar-se diretamente ao mesmo IoT Hub por 15 a 30 minutos antes de tentar voltar ao DPS.
Outros cenários do Hub IoT ao usar o DPS:
- Failover do Hub IoT: os dispositivos devem continuar funcionando, pois as informações de conexão não devem ser alteradas e a lógica está em vigor para repetir a conexão quando o hub estiver disponível novamente.
- Alteração do Hub IoT: a atribuição de dispositivos a uma Hub IoT diferente deve ser feita usando uma política de alocação personalizada.
- Tentar novamente a ligação ao Hub IoT do Azure: Não deve utilizar uma estratégia agressiva de nova tentativa. Em vez disso, deixe um intervalo de pelo menos um minuto antes de tentar novamente.
- Partições do Hub IoT: se sua estratégia de dispositivo se basear fortemente na telemetria, o número de partições de dispositivo para nuvem deverá ser aumentado.
Monitorar dispositivos
Uma parte importante da implantação geral é o monitoramento da solução de ponta a ponta para garantir que o sistema esteja sendo executado adequadamente. Há diversas maneiras de monitorar a integridade de um serviço para implantação em grande escala de dispositivos IoT. Os seguintes padrões são eficazes no monitoramento do serviço:
- Crie um aplicativo para consultar cada grupo de registro em uma instância do DPS, obter o total de dispositivos registrados nesse grupo e, em seguida, agregar os números entre vários grupos de registro. Esse número fornece uma contagem exata dos dispositivos atualmente registrados pelo DPS e pode ser usado para monitorar o estado do serviço.
- Monitore registros de dispositivo em um período específico. Por exemplo, monitore as taxas de registro de uma instância do DPS nos cinco dias anteriores. Observe que essa abordagem fornece apenas um número aproximado e também é limitada a um período específico.
Próximas etapas
- Provisionar dispositivos em Hubs IoT
- Tempo de repetição ao repetir operações