다음을 통해 공유


작업 예약 및 브로드캐스트(Java)

Azure IoT Hub를 사용하여 수백만 대의 디바이스를 업데이트하는 작업을 예약하고 추적합니다. 작업을 사용하여 다음을 수행합니다.

  • desired 속성 업데이트
  • tags 업데이트
  • 직접 메서드 호출

작업(job)은 이러한 작업(action) 중 하나를 래핑하고 디바이스 집합에 대한 실행을 추적합니다. 디바이스 쌍 쿼리에서는 작업 실행 대상인 디바이스 집합을 정의합니다. 예를 들어 백 엔드 앱은 작업(job)을 사용하여 10,000대 디바이스에 대해 디바이스를 재부팅하는 직접 메서드를 호출할 수 있습니다. 디바이스 쌍 쿼리로 디바이스 집합을 지정하고 향후 실행될 작업(job)을 예약합니다. 작업은 해당하는 각 디바이스에서 재부팅 직접 메서드를 수신 및 실행할 때 진행 상태를 추적합니다.

이러한 각 기능에 대한 자세한 내용은 다음을 참조하세요.

참고 항목

이 문서에서 설명하는 기능은 IoT Hub의 표준 계층에서만 사용할 수 있습니다. 기본 및 표준/무료 IoT Hub 계층에 대한 자세한 내용은 솔루션에 적합한 IoT Hub 계층 선택을 참조하세요.

이 문서에서는 2개의 Java 앱을 만드는 방법을 보여 줍니다.

  • 백 엔드 앱에 의해 호출될 수 있는 lockDoor라는 직접 메서드를 구현하는 simulated-device라는 디바이스 앱을 만듭니다.

  • 2개의 작업을 만드는 schedule-jobs라는 백 엔드 앱을 만듭니다. 한 작업은 lockDoor 직접 메서드를 호출하고 다른 작업은 원하는 속성 업데이트를 여러 디바이스로 보냅니다.

참고 항목

디바이스 및 백 엔드 앱을 빌드하는 데 사용할 수 있는 SDK 도구에 대한 자세한 내용은 Azure IoT SDK를 참조하세요.

필수 조건

  • IoT 허브. CLI 또는 Azure Portal을 사용하여 만듭니다.

  • IoT Hub에 등록된 디바이스. IoT 허브에 디바이스가 없으면 디바이스 등록의 단계를 따릅니다.

  • Java SE Development Kit 8. JDK 8용 다운로드를 가져오려면 장기 지원에서 Java 8을 선택해야 합니다.

  • Maven 3

  • 방화벽에서 포트 8883이 열려 있는지 확인합니다. 이 문서의 디바이스 샘플은 포트 8883을 통해 통신하는 MQTT 프로토콜을 사용합니다. 이 포트는 일부 회사 및 교육용 네트워크 환경에서 차단될 수 있습니다. 이 문제를 해결하는 자세한 내용과 방법은 IoT Hub에 연결(MQTT)을 참조하세요.

참고 항목

간단히 하기 위해 이 문서에서는 다시 시도 정책을 구현하지 않습니다. 프로덕션 코드에서는 문서 일시적인 오류 처리에서 제시한 대로 다시 시도 정책(예: 지수 백오프)을 구현해야 합니다.

IoT Hub 연결 문자열 가져오기

이 문서에서는 디바이스에서 직접 메서드를 호출하는 작업을 예약하고, 디바이스 쌍을 업데이트하는 작업을 예약하고, 각 작업의 진행률을 모니터링하는 백 엔드 서비스를 만듭니다. 이러한 작업을 수행하려면 서비스에 레지스트리 읽기레지스트리 쓰기 권한이 있어야 합니다. 기본적으로 모든 IoT Hub는 이 사용 권한을 부여하는 registryReadWrite라는 공유 액세스 정책을 사용하여 만듭니다.

registryReadWrite 정책에 대한 IoT Hub 연결 문자열을 가져오려면 다음 단계를 수행합니다.

  1. Azure Portal에서 리소스 그룹을 선택합니다. 허브가 있는 리소스 그룹을 선택한 다음, 리소스 목록에서 허브를 선택합니다.

  2. 허브의 왼쪽 창에서 공유 액세스 정책을 선택합니다.

  3. 정책 목록에서 registryReadWrite 정책을 선택합니다.

  4. 기본 연결 문자열을 복사하고 값을 저장합니다.

    연결 문자열을 검색하는 방법을 보여 주는 화면 캡처

IoT Hub 공유 액세스 정책 및 사용 권한에 대한 자세한 내용은 액세스 제어 및 권한을 참조하세요.

서비스 응용 프로그램 만들기

이 섹션에서는 작업을 통해 다음을 수행하는 Java 콘솔 앱을 만듭니다.

  • 여러 디바이스에서 lockDoor 직접 메서드 호출

  • 여러 디바이스로 원하는 속성 전송

앱을 만들려면 다음을 수행합니다.

  1. 개발 머신에서 iot-java-schedule-jobs라는 빈 폴더를 만듭니다.

  2. iot-java-schedule-jobs 폴더에서 명령 프롬프트를 통해 다음 명령을 사용하여 schedule-jobs라는 Maven 프로젝트를 만듭니다. 긴 단일 명령입니다.

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=schedule-jobs -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  3. 명령 프롬프트에서 schedule-jobs 폴더로 이동 합니다.

  4. 텍스트 편집기를 사용하여 schedule-jobs 폴더에서 pom.xml 파일을 열고 종속성 노드에 다음 종속성을 추가합니다. 이러한 종속성을 통해 IoT Hub와 통신하도록 앱에서 iot-service-client 패키지를 사용할 수 있습니다.

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

    참고 항목

    Maven 검색을 사용하여 iot-service-client의 최신 버전을 확인할 수 있습니다.

  5. 종속성 노드 뒤에 다음 빌드 노드를 추가합니다. 이 구성에서는 Maven에 Java 1.8을 사용하여 앱을 빌드하도록 지시합니다.

    <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. pom.xml 파일을 저장하고 닫습니다.

  7. 텍스트 편집기를 사용하여 schedule-jobs\src\main\java\com\mycompany\app\App.java 파일을 엽니다.

  8. 파일에 다음 import 문을 추가합니다.

    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. 다음 클래스 수준 변수를 App 클래스에 추가합니다. {youriothubconnectionstring}을 이전에 IoT Hub 연결 문자열 가져오기에서 복사한 IoT Hub 연결 문자열로 바꿉니다.

    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. App 클래스에 다음 메서드를 추가하여 디바이스 쌍에서 원하는 속성 BuildingFloor를 업데이트하는 작업을 예약합니다.

    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. lockDoor 메서드를 호출하는 작업을 예약하려면 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. 작업을 모니터링하려면 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. 실행한 작업의 세부 정보를 쿼리하려면 다음 메서드를 추가합니다.

    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. 다음 throws 절을 포함하도록 main 메서드 서명을 업데이트합니다.

    public static void main( String[] args ) throws Exception
    
  15. 두 작업을 순차적으로 실행하고 모니터링하려면 main 메서드의 코드를 다음 코드로 바꿉니다.

    // 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. schedule-jobs\src\main\java\com\mycompany\app\App.java 파일을 저장하고 닫습니다.

  17. schedule-jobs 앱을 빌드하고 오류를 수정합니다. 명령 프롬프트에서 schedule-jobs 폴더로 이동하고 다음 명령을 실행합니다.

    mvn clean package -DskipTests
    

디바이스 앱 만들기

이 섹션에서는 IoT Hub에서 전송한 원하는 속성을 처리하고 직접 메서드 호출을 구현하는 Java 콘솔 앱을 만듭니다.

  1. 명령 프롬프트에서 다음 명령을 사용하여 iot-java-schedule-jobs 폴더에 simulated-device라는 새 Maven 프로젝트를 만듭니다. 긴 단일 명령입니다.

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=simulated-device -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  2. 명령 프롬프트에서 simulated-device 폴더로 이동합니다.

  3. 텍스트 편집기를 사용하여 simulated-device 폴더에서 pom.xml 파일을 열고 종속성 노드에 다음 종속성을 추가합니다. 이러한 종속성을 통해 IoT 허브와 통신하도록 앱에서 iot-device-client 패키지를 사용할 수 있습니다.

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

    참고 항목

    Maven 검색을 사용하여 iot-device-client의 최신 버전을 확인할 수 있습니다.

  4. 종속성 노드에 다음 종속성을 추가합니다. 이 종속성은 디바이스 클라이언트 SDK에서 로깅을 구현하는 데 사용하는 Apache SLF4J 로깅 외관에 맞게 NOP를 구성합니다. 이 구성은 선택 사항이지만, 건너뛰면 앱을 실행할 때 콘솔에 경고가 표시될 수 있습니다. 디바이스 클라이언트 SDK에 로그인하는 방법에 대한 자세한 내용은 Java용 Azure IoT 디바이스 SDK 샘플 추가 정보 파일에서 로깅을 참조하세요.

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.28</version>
    </dependency>
    
  5. 종속성 노드 뒤에 다음 빌드 노드를 추가합니다. 이 구성에서는 Maven에 Java 1.8을 사용하여 앱을 빌드하도록 지시합니다.

    <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. pom.xml 파일을 저장하고 닫습니다.

  7. 텍스트 편집기를 사용하여 simulated-device\src\main\java\com\mycompany\app\App.java 파일을 엽니다.

  8. 파일에 다음 import 문을 추가합니다.

    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. 다음 클래스 수준 변수를 App 클래스에 추가합니다. {yourdeviceconnectionstring}을 IoT Hub에 디바이스를 등록할 때 보았던 디바이스 연결 문자열로 바꿉니다.

    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;
    

    이 샘플 앱은 DeviceClient 개체를 인스턴스화할 때 프로토콜 변수를 사용합니다.

  10. 콘솔에 디바이스 쌍 알림을 인쇄하려면 다음 중첩 클래스를 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. 콘솔에 직접 메서드 알림을 인쇄하려면 다음 중첩 클래스를 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. IoT Hub로부터의 직접 메서드 호출을 처리하려면 다음 중첩 클래스를 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. 다음 throws 절을 포함하도록 main 메서드 서명을 업데이트합니다.

    public static void main( String[] args ) throws IOException, URISyntaxException
    
  14. main 메서드의 코드를 다음 코드로 바꿉니다.

    • IoT Hub와 통신하는 디바이스 클라이언트를 만듭니다.
    • Device 개체를 만들어 디바이스 쌍 속성을 저장합니다.
    // 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. 디바이스 클라이언트 서비스를 시작하려면 다음 코드를 main 메서드에 추가합니다.

    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. 종료하기 전에 사용자가 Enter 키를 누를 때까지 대기하려면 다음 코드를 main 메서드 끝에 추가합니다.

    // 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. simulated-device\src\main\java\com\mycompany\app\App.java 파일을 저장한 후 닫습니다.

  18. simulated-device 앱을 빌드하고 오류를 수정합니다. 명령 프롬프트에서 simulated-device 폴더로 이동한 후 다음 명령을 실행합니다.

    mvn clean package -DskipTests
    

앱 실행

이제 콘솔 앱을 실행할 준비가 되었습니다.

  1. 명령 프롬프트의 simulated-device 폴더에서 다음 명령을 실행하여 원하는 속성 변경 내용과 직접 메서드 호출을 수신 대기하도록 디바이스 앱을 시작합니다.

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

    디바이스 클라이언트 시작

  2. 명령 프롬프트의 schedule-jobs 폴더에서 다음 명령을 실행하여 schedule-jobs 서비스 앱을 실행해 두 작업을 실행합니다. 첫 번째 작업에서는 원하는 속성 값을 설정하고 두 번째 작업에서는 직접 메서드를 호출합니다.

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

    Java IoT Hub 서비스 앱에서 두 개의 작업을 작성함

  3. 디바이스 앱이 원하는 속성 변경 및 직접 메서드 호출을 처리합니다.

    디바이스 클라이언트에 변경 내용에 응답함

다음 단계

이 문서에서는 직접 메서드를 실행하고 디바이스 쌍의 속성을 업데이트하는 작업을 예약했습니다.

IoT Hub 및 디바이스 관리 패턴을 계속 살펴보려면 Raspberry Pi 3 B+ 참조 이미지를 사용하는 Azure IoT Hub용 디바이스 업데이트 자습서에서 이미지를 업데이트합니다.