Agendar e transmitir trabalhos (Java)

Use o Hub IoT do Azure para agendar e rastrear trabalhos que atualizam milhões de dispositivos. Use trabalhos para:

  • Atualizar as propriedades pretendidas
  • Atualizar tags
  • Invocar métodos diretos

Um trabalho envolve uma dessas ações e rastreia a execução em relação a um conjunto de dispositivos. Uma consulta gêmea de dispositivo define o conjunto de dispositivos contra os quais o trabalho é executado. Por exemplo, um aplicativo back-end pode usar um trabalho para invocar um método direto em 10.000 dispositivos que reinicializa os dispositivos. Você especifica o conjunto de dispositivos com uma consulta gêmea de dispositivo e agenda o trabalho para ser executado em um momento futuro. O trabalho acompanha o progresso à medida que cada um dos dispositivos recebe e executa o método direto de reinicialização.

Para saber mais sobre cada um desses recursos, consulte:

Nota

Os recursos descritos neste artigo estão disponíveis somente na camada padrão do Hub IoT. Para obter mais informações sobre as camadas básica e padrão/gratuita do Hub IoT, consulte Escolha a camada certa do Hub IoT para sua solução.

Este artigo mostra como criar dois aplicativos Java:

  • Um aplicativo de dispositivo, simulated-device, que implementa um método direto chamado lockDoor, que pode ser chamado pelo aplicativo back-end.

  • Um aplicativo de back-end, schedule-jobs, que cria dois trabalhos. Um trabalho chama o método direto lockDoor e outro trabalho envia atualizações de propriedade desejadas para vários dispositivos.

Nota

Consulte SDKs do Azure IoT para obter mais informações sobre as ferramentas SDK disponíveis para criar aplicativos de dispositivo e back-end.

Pré-requisitos

  • Um hub IoT em sua assinatura do Azure. Se você ainda não tiver um hub, siga as etapas em Criar um hub IoT.

  • Um dispositivo registrado em seu hub IoT. Se você não tiver um dispositivo em seu hub IoT, siga as etapas em Registrar um dispositivo.

  • Kit de Desenvolvimento Java SE 8. Certifique-se de selecionar Java 8 em Suporte de longo prazo para obter downloads para JDK 8.

  • Maven 3

  • Verifique se a porta 8883 está aberta no firewall. O exemplo de dispositivo neste artigo usa o protocolo MQTT, que se comunica pela porta 8883. Essa porta pode estar bloqueada em alguns ambientes de rede corporativa e educacional. Para obter mais informações e maneiras de contornar esse problema, consulte Conectando-se ao Hub IoT (MQTT).

Nota

Para simplificar, este artigo não implementa uma política de novas tentativas. No código de produção, você deve implementar políticas de repetição (como um backoff exponencial), conforme sugerido no artigo, Tratamento de falhas transitórias.

Obter a cadeia de conexão do Hub IoT

Neste artigo, você cria um serviço de back-end que agenda um trabalho para invocar um método direto em um dispositivo, agenda um trabalho para atualizar o gêmeo do dispositivo e monitora o progresso de cada trabalho. Para executar essas operações, seu serviço precisa das permissões de leitura e gravação do Registro. Por padrão, cada hub IoT é criado com uma política de acesso compartilhado chamada registryReadWrite que concede essas permissões.

Para obter a cadeia de conexão do Hub IoT para a política registryReadWrite , siga estas etapas:

  1. No portal do Azure, selecione Grupos de recursos. Selecione o grupo de recursos onde o hub está localizado e, em seguida, selecione o hub na lista de recursos.

  2. No painel esquerdo do hub, selecione Políticas de acesso compartilhado.

  3. Na lista de políticas, selecione a política registryReadWrite .

  4. Copie a cadeia de conexão primária e salve o valor.

    Captura de tela que mostra como recuperar a cadeia de conexão

Para obter mais informações sobre políticas e permissões de acesso compartilhado do Hub IoT, consulte Controle de acesso e permissões.

Importante

Este artigo inclui etapas para se conectar a um serviço usando uma assinatura de acesso compartilhado. Esse método de autenticação é conveniente para teste e avaliação, mas autenticar em um serviço com ID do Microsoft Entra ou identidades gerenciadas é uma abordagem mais segura. Para saber mais, consulte Práticas > recomendadas de segurança Segurança na nuvem.

Criar o aplicativo de serviço

Nesta seção, você cria um aplicativo de console Java que usa trabalhos para:

  • Chame o método lockDoor direct em vários dispositivos.

  • Envie as propriedades desejadas para vários dispositivos.

Para criar o aplicativo:

  1. Em sua máquina de desenvolvimento, crie uma pasta vazia chamada iot-java-schedule-jobs.

  2. Na pasta iot-java-schedule-jobs, crie um projeto Maven chamado schedule-jobs usando o seguinte comando no prompt de comando. Tenha em atenção que se trata de um comando único, por extenso:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=schedule-jobs -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  3. No prompt de comando, navegue até a pasta schedule-jobs .

  4. Usando um editor de texto, abra o arquivo pom.xml na pasta schedule-jobs e adicione a seguinte dependência ao nó dependencies . Essa dependência permite que você use o pacote iot-service-client em seu aplicativo para se comunicar com seu hub IoT:

    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-service-client</artifactId>
      <version>1.17.1</version>
      <type>jar</type>
    </dependency>
    

    Nota

    Pode verificar a versão mais recente do iot-service-client utilizando a pesquisa Maven.

  5. Adicione o seguinte nó de compilação após o nó de dependências . Essa configuração instrui o Maven a usar o Java 1.8 para construir o aplicativo:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.3</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  6. Guarde e feche o ficheiro pom.xml.

  7. Usando um editor de texto, abra o arquivo schedule-jobs\src\main\java\com\mycompany\app\App.java .

  8. Adicione as seguintes declarações de importação ao ficheiro:

    import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceTwinDevice;
    import com.microsoft.azure.sdk.iot.service.devicetwin.Pair;
    import com.microsoft.azure.sdk.iot.service.devicetwin.Query;
    import com.microsoft.azure.sdk.iot.service.devicetwin.SqlQuery;
    import com.microsoft.azure.sdk.iot.service.jobs.JobClient;
    import com.microsoft.azure.sdk.iot.service.jobs.JobResult;
    import com.microsoft.azure.sdk.iot.service.jobs.JobStatus;
    
    import java.util.Date;
    import java.time.Instant;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.UUID;
    
  9. Adicione as seguintes variáveis de nível de classe à classe Aplicação. Substitua {youriothubconnectionstring} pela cadeia de conexão do hub IoT que você copiou anteriormente em Obter a cadeia de conexão do hub IoT:

    public static final String iotHubConnectionString = "{youriothubconnectionstring}";
    public static final String deviceId = "myDeviceId";
    
    // How long the job is permitted to run without
    // completing its work on the set of devices
    private static final long maxExecutionTimeInSeconds = 30;
    
  10. Adicione o seguinte método à classe App para agendar um trabalho para atualizar as propriedades desejadas Building e Floor no gêmeo do dispositivo:

    private static JobResult scheduleJobSetDesiredProperties(JobClient jobClient, String jobId) {
      DeviceTwinDevice twin = new DeviceTwinDevice(deviceId);
      Set<Pair> desiredProperties = new HashSet<Pair>();
      desiredProperties.add(new Pair("Building", 43));
      desiredProperties.add(new Pair("Floor", 3));
      twin.setDesiredProperties(desiredProperties);
      // Optimistic concurrency control
      twin.setETag("*");
    
      // Schedule the update twin job to run now
      // against a single device
      System.out.println("Schedule job " + jobId + " for device " + deviceId);
      try {
        JobResult jobResult = jobClient.scheduleUpdateTwin(jobId, 
          "deviceId='" + deviceId + "'",
          twin,
          new Date(),
          maxExecutionTimeInSeconds);
        return jobResult;
      } catch (Exception e) {
        System.out.println("Exception scheduling desired properties job: " + jobId);
        System.out.println(e.getMessage());
        return null;
      }
    }
    
  11. Para agendar um trabalho para chamar o método lockDoor , adicione o seguinte método à classe App :

    private static JobResult scheduleJobCallDirectMethod(JobClient jobClient, String jobId) {
      // Schedule a job now to call the lockDoor direct method
      // against a single device. Response and connection
      // timeouts are set to 5 seconds.
      System.out.println("Schedule job " + jobId + " for device " + deviceId);
      try {
        JobResult jobResult = jobClient.scheduleDeviceMethod(jobId,
          "deviceId='" + deviceId + "'",
          "lockDoor",
          5L, 5L, null,
          new Date(),
          maxExecutionTimeInSeconds);
        return jobResult;
      } catch (Exception e) {
        System.out.println("Exception scheduling direct method job: " + jobId);
        System.out.println(e.getMessage());
        return null;
      }
    };
    
  12. Para monitorar um trabalho, adicione o seguinte método à classe App :

    private static void monitorJob(JobClient jobClient, String jobId) {
      try {
        JobResult jobResult = jobClient.getJob(jobId);
        if(jobResult == null)
        {
          System.out.println("No JobResult for: " + jobId);
          return;
        }
        // Check the job result until it's completed
        while(jobResult.getJobStatus() != JobStatus.completed)
        {
          Thread.sleep(100);
          jobResult = jobClient.getJob(jobId);
          System.out.println("Status " + jobResult.getJobStatus() + " for job " + jobId);
        }
        System.out.println("Final status " + jobResult.getJobStatus() + " for job " + jobId);
      } catch (Exception e) {
        System.out.println("Exception monitoring job: " + jobId);
        System.out.println(e.getMessage());
        return;
      }
    }
    
  13. Para consultar os detalhes dos trabalhos executados, adicione o seguinte método:

    private static void queryDeviceJobs(JobClient jobClient, String start) throws Exception {
      System.out.println("\nQuery device jobs since " + start);
    
      // Create a jobs query using the time the jobs started
      Query deviceJobQuery = jobClient
          .queryDeviceJob(SqlQuery.createSqlQuery("*", SqlQuery.FromType.JOBS, "devices.jobs.startTimeUtc > '" + start + "'", null).getQuery());
    
      // Iterate over the list of jobs and print the details
      while (jobClient.hasNextJob(deviceJobQuery)) {
        System.out.println(jobClient.getNextJob(deviceJobQuery));
      }
    }
    
  14. Atualize a assinatura do método principal para incluir a seguinte throws cláusula:

    public static void main( String[] args ) throws Exception
    
  15. Para executar e monitorar dois trabalhos sequencialmente, substitua o código no método principal pelo seguinte código:

    // Record the start time
    String start = Instant.now().toString();
    
    // Create JobClient
    JobClient jobClient = JobClient.createFromConnectionString(iotHubConnectionString);
    System.out.println("JobClient created with success");
    
    // Schedule twin job desired properties
    // Maximum concurrent jobs is 1 for Free and S1 tiers
    String desiredPropertiesJobId = "DPCMD" + UUID.randomUUID();
    scheduleJobSetDesiredProperties(jobClient, desiredPropertiesJobId);
    monitorJob(jobClient, desiredPropertiesJobId);
    
    // Schedule twin job direct method
    String directMethodJobId = "DMCMD" + UUID.randomUUID();
    scheduleJobCallDirectMethod(jobClient, directMethodJobId);
    monitorJob(jobClient, directMethodJobId);
    
    // Run a query to show the job detail
    queryDeviceJobs(jobClient, start);
    
    System.out.println("Shutting down schedule-jobs app");
    
  16. Salve e feche o arquivo schedule-jobs\src\main\java\com\mycompany\app\App.java

  17. Crie o aplicativo schedule-jobs e corrija quaisquer erros. No prompt de comando, navegue até a pasta schedule-jobs e execute o seguinte comando:

    mvn clean package -DskipTests
    

Criar uma aplicação de dispositivo

Nesta seção, você cria um aplicativo de console Java que manipula as propriedades desejadas enviadas do Hub IoT e implementa a chamada direta do método.

Importante

Este artigo inclui etapas para conectar um dispositivo usando uma assinatura de acesso compartilhado, também chamada de autenticação de chave simétrica. Esse método de autenticação é conveniente para testes e avaliações, mas autenticar um dispositivo usando certificados X.509 é uma abordagem mais segura. Para saber mais, consulte Práticas > recomendadas de segurança Segurança de conexão.

  1. Na pasta iot-java-schedule-jobs, crie um projeto Maven chamado simulated-device usando o seguinte comando no prompt de comando. Tenha em atenção que se trata de um comando único, por extenso:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=simulated-device -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  2. No prompt de comando, navegue até a pasta de dispositivo simulado.

  3. Usando um editor de texto, abra o arquivo pom.xml na pasta simulated-device e adicione as seguintes dependências ao nó dependencies . Essa dependência permite que você use o pacote iot-device-client em seu aplicativo para se comunicar com seu hub IoT:

    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-device-client</artifactId>
      <version>1.17.5</version>
    </dependency>
    

    Nota

    Pode verificar a versão mais recente do iot-device-client utilizando a pesquisa Maven.

  4. Adicione a seguinte dependência ao nó dependências . Essa dependência configura um NOP para a fachada de log Apache SLF4J , que é usada pelo SDK do cliente de dispositivo para implementar o registro. Essa configuração é opcional, mas se você omiti-la, poderá ver um aviso no console quando executar o aplicativo. Para obter mais informações sobre como registrar no SDK do cliente de dispositivo, consulte Registrandoos exemplos para o arquivo Leiame do SDK do dispositivo IoT do Azure para Java .

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.28</version>
    </dependency>
    
  5. Adicione o seguinte nó de compilação após o nó de dependências . Essa configuração instrui o Maven a usar o Java 1.8 para construir o aplicativo:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.3</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  6. Guarde e feche o ficheiro pom.xml.

  7. Usando um editor de texto, abra o arquivo simulated-device\src\main\java\com\mycompany\app\App.java .

  8. Adicione as seguintes declarações de importação ao ficheiro:

    import com.microsoft.azure.sdk.iot.device.*;
    import com.microsoft.azure.sdk.iot.device.DeviceTwin.*;
    
    import java.io.IOException;
    import java.net.URISyntaxException;
    import java.util.Scanner;
    
  9. Adicione as seguintes variáveis de nível de classe à classe Aplicação. Substitua {yourdeviceconnectionstring} pela cadeia de conexão de dispositivo que você viu quando registrou um dispositivo no Hub IoT:

    private static String connString = "{yourdeviceconnectionstring}";
    private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
    private static final int METHOD_SUCCESS = 200;
    private static final int METHOD_NOT_DEFINED = 404;
    

    Esta aplicação de exemplo utiliza a variável de protocolo para instanciar um objeto DeviceClient.

  10. Para imprimir notificações gêmeas de dispositivo no console, adicione a seguinte classe aninhada à classe App :

    // Handler for device twin operation notifications from IoT Hub
    protected static class DeviceTwinStatusCallBack implements IotHubEventCallback {
      public void execute(IotHubStatusCode status, Object context) {
        System.out.println("IoT Hub responded to device twin operation with status " + status.name());
      }
    }
    
  11. Para imprimir notificações diretas do método no console, adicione a seguinte classe aninhada à classe App :

    // Handler for direct method notifications from IoT Hub
    protected static class DirectMethodStatusCallback implements IotHubEventCallback {
      public void execute(IotHubStatusCode status, Object context) {
        System.out.println("IoT Hub responded to direct method operation with status " + status.name());
      }
    }
    
  12. Para lidar com chamadas diretas de método do Hub IoT, adicione a seguinte classe aninhada à classe App :

    // Handler for direct method calls from IoT Hub
    protected static class DirectMethodCallback
        implements DeviceMethodCallback {
      @Override
      public DeviceMethodData call(String methodName, Object methodData, Object context) {
        DeviceMethodData deviceMethodData;
        switch (methodName) {
          case "lockDoor": {
            System.out.println("Executing direct method: " + methodName);
            deviceMethodData = new DeviceMethodData(METHOD_SUCCESS, "Executed direct method " + methodName);
            break;
          }
          default: {
            deviceMethodData = new DeviceMethodData(METHOD_NOT_DEFINED, "Not defined direct method " + methodName);
          }
        }
        // Notify IoT Hub of result
        return deviceMethodData;
      }
    }
    
  13. Atualize a assinatura do método principal para incluir a seguinte throws cláusula:

    public static void main( String[] args ) throws IOException, URISyntaxException
    
  14. Substitua o código no método principal com o seguinte código para:

    • Crie um cliente de dispositivo para se comunicar com o Hub IoT.
    • Crie um objeto Device para armazenar as propriedades gêmeas do dispositivo.
    // Create a device client
    DeviceClient client = new DeviceClient(connString, protocol);
    
    // An object to manage device twin desired and reported properties
    Device dataCollector = new Device() {
      @Override
      public void PropertyCall(String propertyKey, Object propertyValue, Object context)
      {
        System.out.println("Received desired property change: " + propertyKey + " " + propertyValue);
      }
    };
    
  15. Para iniciar os serviços de cliente de dispositivo, adicione o seguinte código ao método principal :

    try {
      // Open the DeviceClient
      // Start the device twin services
      // Subscribe to direct method calls
      client.open();
      client.startDeviceTwin(new DeviceTwinStatusCallBack(), null, dataCollector, null);
      client.subscribeToDeviceMethod(new DirectMethodCallback(), null, new DirectMethodStatusCallback(), null);
    } catch (Exception e) {
      System.out.println("Exception, shutting down \n" + " Cause: " + e.getCause() + " \n" + e.getMessage());
      dataCollector.clean();
      client.closeNow();
      System.out.println("Shutting down...");
    }
    
  16. Para esperar que o usuário pressione a tecla Enter antes de desligar, adicione o seguinte código ao final do método principal :

    // Close the app
    System.out.println("Press any key to exit...");
    Scanner scanner = new Scanner(System.in);
    scanner.nextLine();
    dataCollector.clean();
    client.closeNow();
    scanner.close();
    
  17. Salve e feche o arquivo simulated-device\src\main\java\com\mycompany\app\App.java .

  18. Crie o aplicativo de dispositivo simulado e corrija quaisquer erros. No prompt de comando, navegue até a pasta simulated-device e execute o seguinte comando:

    mvn clean package -DskipTests
    

Executar as aplicações

Agora você está pronto para executar os aplicativos do console.

  1. Em um prompt de comando na pasta simulated-device , execute o seguinte comando para iniciar o aplicativo de dispositivo escutando as alterações de propriedade desejadas e chamadas diretas de método:

    mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
    

    O cliente do dispositivo é iniciado

  2. Em um prompt de comando na schedule-jobs pasta, execute o seguinte comando para executar o aplicativo de serviço schedule-jobs para executar dois trabalhos. O primeiro define os valores de propriedade desejados, o segundo chama o método direto:

    mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
    

    O aplicativo de serviço Java IoT Hub cria dois trabalhos

  3. O aplicativo de dispositivo lida com a alteração de propriedade desejada e a chamada de método direta:

    O cliente do dispositivo responde às alterações

Próximos passos

Neste artigo, você agendou trabalhos para executar um método direto e atualizar as propriedades do gêmeo do dispositivo.

Para continuar explorando o Hub IoT e os padrões de gerenciamento de dispositivos, atualize uma imagem no tutorial Atualização de Dispositivo para o Hub IoT do Azure usando a Imagem de Referência do Raspberry Pi 3 B+.