使用 Java 将设备连接到 IoT 中心

简介

Azure IoT 中心是一项完全托管的 Azure 服务。 该服务可在数百万台物联网 (IoT) 设备和一个解决方案后端之间实现安全可靠的双向通信。 IoT 项目面临的最大挑战之一是如何可靠且安全地将设备连接到解决方案后端。 为了解决此难题,IoT 中心:

  • 提供可靠的设备到云和云到设备的超大规模消息传送。
  • 使用每个设备的安全凭据和访问控制来实现安全通信。
  • 包含最流行语言和平台的设备库。

本教程演示如何:

  • 使用 Azure 门户创建 IoT 中心。
  • 在 IoT 中心内创建设备标识。
  • 创建一个模拟设备,用于将遥测数据发送到解决方案后端。

在本教程结束时,将获得三个 Java 控制台应用:

  • create-device-identity,用于创建设备标识和关联的安全密钥以连接设备应用。
  • read-d2c-messages,用于显示设备应用发送的遥测数据。
  • simulated-device,使用前面创建的设备标识连接到 IoT 中心,并使用 MQTT 协议每秒发送一次遥测消息。

Note

Azure IoT SDK 文章介绍了一些 Azure IoT SDK,它们可用于构建在设备和解决方案后端运行的应用。

要完成本教程,需要以下各项:

创建 IoT 中心

创建模拟设备应用要连接到的 IoT 中心。 以下步骤说明如何使用 Azure 门户来完成此任务。

  1. 登录到 Azure 门户

  2. 选择“新建” > “物联网” > “IoT 中心”。

    Azure 门户跳转栏

  3. 在“IoT 中心”窗格中,输入 IoT 中心的以下信息:

    • 名称:创建 IoT 中心的名称。 如果输入的名称有效,则显示一个绿色复选标记。

    Important

    IoT 中心将公开为 DNS 终结点,因此,命名时请务必避免包含任何敏感信息。

    • 定价和缩放级别:对于本教程,请选择F1 - 免费级别。 有关详细信息,请参阅定价和缩放层

    • 资源组:创建用于托管 IoT 中心的资源组,或使用现有的资源组。 有关详细信息,请参阅使用资源组管理 Azure 资源

    • 位置:选择最近的位置。

    • 固定仪表板:选中此选项可以方便地从仪表板访问 IoT 中心。

      IoT 中心窗口

  4. 单击“创建”。 创建 IoT 中心可能需要数分钟的时间。 可在“通知”窗格中监视进度。

  5. 新的 IoT 中心就绪以后,请在 Azure 门户中单击其磁贴,打开其属性窗口。 创建 IoT 中心以后,即可找到将设备和应用程序连接到 IoT 中心时需要使用的重要信息。 记下“主机名”,并单击“共享访问策略”。

    新建 IoT 中心窗口

  6. 在“共享访问策略”中,单击“iothubowner”策略,然后记下“iothubowner”窗口中的 IoT 中心连接字符串。 有关详细信息,请参阅“IoT 中心开发人员指南”中的访问控制

    共享访问策略

最后,请记下“主密钥”值。 然后单击“终结点”和“事件”内置终结点。 在“属性”边栏选项卡中,记下“与事件中心兼容的名称”和“与事件中心兼容的终结点”的地址。 创建 read-d2c-messages 应用时,将要用到这三个值。

Azure 门户 IoT 中心消息传递边栏选项卡

现在已创建 IoT 中心。 已获取 IoT 中心主机名、IoT 中心连接字符串、IoT 中心主密钥、与事件中心兼容的名称以及与事件中心兼容的终结点,接下来需要完成本教程。

创建设备标识

在本部分中,会创建一个 Java 控制台应用,用于在 IoT 中心的标识注册表中创建设备标识。 设备无法连接到 IoT 中心,除非它在标识注册表中具有条目。 有关详细信息,请参阅 IoT 中心开发人员指南标识注册表部分。 当你运行此控制台应用时,它生成唯一的设备 ID 和密钥,当设备向 IoT 中心发送设备到云的消息时,可以用于标识设备本身。

  1. 创建名为 iot-java-get-started 的空文件夹。 在 iot-java-get-started 文件夹的命令提示符处,使用以下命令创建名为 create-device-identity 的 Maven 项目。 请注意,这是一条很长的命令:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=create-device-identity -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  2. 在命令提示符下,浏览到 create-device-identity 文件夹。
  3. 使用文本编辑器,打开 create-device-identity 文件夹中的 pom.xml 文件,并在 dependencies 节点中添加以下依赖项。 通过此依赖项可在应用中使用 iot-service-client 包:

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

    Note

    可以使用 Maven 搜索检查是否有最新版本的 iot-service-client

  4. 保存并关闭 pom.xml 文件。

  5. 使用文本编辑器打开 create-device-identity\src\main\java\com\mycompany\app\App.java 文件。
  6. 在该文件中添加以下 import 语句:

    import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException;
    import com.microsoft.azure.sdk.iot.service.Device;
    import com.microsoft.azure.sdk.iot.service.RegistryManager;
    
    import java.io.IOException;
    import java.net.URISyntaxException;
    
  7. 将以下类级变量添加到 App 类,并将 {yourhubconnectionstring} 替换为前面记录的值:

    private static final String connectionString = "{yourhubconnectionstring}";
    private static final String deviceId = "myFirstJavaDevice";
    

    Important

    收集的日志中可能会显示设备 ID 用于客户支持和故障排除,因此,在为日志命名时,请务必避免包含任何敏感信息。

  8. 修改 main 方法的签名,包含如下所示的异常:

    public static void main( String[] args ) throws IOException, URISyntaxException, Exception
    
  9. 添加以下代码作为 main 方法的主体。 此代码在 IoT 中心标识注册表中创建名为 javadevice 的设备(如果还没有该设备)。 随即显示稍后需要用到的设备 ID 和密钥:

    RegistryManager registryManager = RegistryManager.createFromConnectionString(connectionString);
    
    // Create a device that's enabled by default, 
    // with an autogenerated key.
    Device device = Device.createFromId(deviceId, null, null);
    try {
      device = registryManager.addDevice(device);
    } catch (IotHubException iote) {
      // If the device already exists.
      try {
        device = registryManager.getDevice(deviceId);
      } catch (IotHubException iotf) {
        iotf.printStackTrace();
      }
    }
    
    // Display information about the
    // device you created.
    System.out.println("Device Id: " + device.getDeviceId());
    System.out.println("Device key: " + device.getPrimaryKey());
    
  10. 保存并关闭 App.java 文件。
  11. 若要使用 Maven 生成 create-device-identity 应用,请在命令提示符下的 create-device-identity 文件夹中执行以下命令:

    mvn clean package -DskipTests
    
  12. 若要使用 Maven 运行 create-device-identity 应用,请在 create-device-identity 文件夹中的命令提示符下执行以下命令:

    mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
    
  13. 记下设备 ID设备密钥。 稍后在创建连接到作为设备的 IoT 中心的应用时需要这些值。

Note

IoT 中心标识注册表只存储设备标识,以启用对 IoT 中心的安全访问。 它存储设备 ID 和密钥作为安全凭据,以及启用或禁用标志(可用于禁用对单个设备的访问)。 如果应用需要存储设备特定的其他元数据,需使用应用特定的存储。 有关详细信息,请参阅 IoT 中心开发人员指南

接收设备到云的消息

在本部分中,会创建一个 Java 控制台应用程序,用于读取来自 IoT 中心的设备到云消息。 IoT 中心公开与事件中心兼容的终结点,以便你可读取设备到云的消息。 为了简单起见,本教程创建的基本读取器不适用于高吞吐量部署。 Process device-to-cloud messages (处理设备到云的消息)教程介绍了如何大规模处理设备到云的消息。 事件中心入门 教程更详细地介绍了如何处理来自事件中心的消息,此教程也适用于与 IoT 中心事件中心兼容的终结点。

Note

与事件中心兼容的终结点始终使用 AMQP 协议读取设备到云的消息。

  1. 创建设备标识 部分中创建的 iot-java-get-started 文件夹中,在命令提示符处使用以下命令创建名为 read-d2c-messages 的 Maven 项目。 请注意,这是一条很长的命令:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=read-d2c-messages -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  2. 在命令提示符下,浏览到 read-d2c-messages 文件夹。
  3. 使用文本编辑器,打开 read-d2c-messages 文件夹中的 pom.xml 文件,并在 dependencies 节点中添加以下依赖项。 借助此依赖项,可使用应用中的 eventhubs-client 包,从与事件中心兼容的终结点进行读取:

    <dependency> 
        <groupId>com.microsoft.azure</groupId> 
        <artifactId>azure-eventhubs</artifactId> 
        <version>0.15.0</version> 
    </dependency>
    
  4. 保存并关闭 pom.xml 文件。

  5. 使用文本编辑器打开 read-d2c-messages\src\main\java\com\mycompany\app\App.java 文件。
  6. 在该文件中添加以下 import 语句:

    import java.io.IOException;
    import com.microsoft.azure.eventhubs.*;
    
    import java.nio.charset.Charset;
    import java.time.*;
    import java.util.function.*;
    
  7. 将下列类级变量添加到 App 类。 将 {youriothubkey}{youreventhubcompatibleendpoint}{youreventhubcompatiblename} 替换为前面记下的值:

    private static String connStr = "Endpoint={youreventhubcompatibleendpoint};EntityPath={youreventhubcompatiblename};SharedAccessKeyName=iothubowner;SharedAccessKey={youriothubkey}";
    
  8. 将以下 receiveMessages 方法添加到 App 类。 此方法创建 EventHubClient 实例以连接到与事件中心兼容的终结点,然后以异步方式创建 PartitionReceiver 实例,以便从事件中心分区读取。 它持续循环并输出消息详细信息,直到应用终止。

    // Create a receiver on a partition.
    private static EventHubClient receiveMessages(final String partitionId) {
      EventHubClient client = null;
      try {
        client = EventHubClient.createFromConnectionStringSync(connStr);
      } catch (Exception e) {
        System.out.println("Failed to create client: " + e.getMessage());
        System.exit(1);
      }
      try {
        // Create a receiver using the
        // default Event Hubs consumer group
        // that listens for messages from now on.
        client.createReceiver(EventHubClient.DEFAULT_CONSUMER_GROUP_NAME, partitionId, Instant.now())
          .thenAccept(new Consumer<PartitionReceiver>() {
            public void accept(PartitionReceiver receiver) {
              System.out.println("** Created receiver on partition " + partitionId);
              try {
                while (true) {
                  Iterable<EventData> receivedEvents = receiver.receive(100).get();
                  int batchSize = 0;
                  if (receivedEvents != null) {
                    System.out.println("Got some events");
                    for (EventData receivedEvent : receivedEvents) {
                      System.out.println(String.format("Offset: %s, SeqNo: %s, EnqueueTime: %s",
                        receivedEvent.getSystemProperties().getOffset(),
                        receivedEvent.getSystemProperties().getSequenceNumber(),
                        receivedEvent.getSystemProperties().getEnqueuedTime()));
                      System.out.println(String.format("| Device ID: %s",
                        receivedEvent.getSystemProperties().get("iothub-connection-device-id")));
                      System.out.println(String.format("| Message Payload: %s",
                        new String(receivedEvent.getBytes(), Charset.defaultCharset())));
                      batchSize++;
                    }
                  }
                  System.out.println(String.format("Partition: %s, ReceivedBatch Size: %s", partitionId, batchSize));
                }
              } catch (Exception e) {
                System.out.println("Failed to receive messages: " + e.getMessage());
              }
            }
          });
        } catch (Exception e) {
          System.out.println("Failed to create receiver: " + e.getMessage());
      }
      return client;
    }
    

    Note

    在创建开始运行后只读取发送到 IoT 中心的消息的接收方时,此方法使用筛选器。 此方法很适合测试环境,因为这样可以看到当前的消息集。 在生产环境中,代码应确保它能处理所有消息。有关详细信息,请参阅如何处理 IoT 中心设备到云的消息教程。

  9. 修改 main 方法的签名,包含如下所示的异常:

    public static void main( String[] args ) throws IOException
    
  10. App 类的 main 方法中添加以下代码。 此代码将创建两个(EventHubClientPartitionReceiver)实例并使用户可在处理完消息后关闭应用:

    // Create receivers for partitions 0 and 1.
    EventHubClient client0 = receiveMessages("0");
    EventHubClient client1 = receiveMessages("1");
    System.out.println("Press ENTER to exit.");
    System.in.read();
    try {
      client0.closeSync();
      client1.closeSync();
      System.exit(0);
    } catch (Exception e) {
      System.exit(1);
    }
    

    Note

    此代码假设已在 F1(免费)层创建 IoT 中心。 免费 IoT 中心有“0”和“1”这两个分区。

  11. 保存并关闭 App.java 文件。

  12. 若要使用 Maven 生成 read-d2c-messages 应用,请在 read-d2c-messages 文件夹中的命令提示符下执行以下命令:

    mvn clean package -DskipTests
    

创建设备应用

在本部分中,会创建一个 Java 控制台应用程序,用于模拟向 IoT 中心发送设备到云消息的设备。

  1. 创建设备标识 部分创建的 iot-java-get-started 文件夹中,在命令提示符处创建名为 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 文件,并在 dependencies 节点中添加以下依赖项。 通过此依赖项可以使用应用中的 iothub-java-client 包与 IoT 中心通信,并将 Java 对象序列化为 JSON:

    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-device-client</artifactId>
      <version>1.3.32</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.3.1</version>
    </dependency>
    

    Note

    可以使用 Maven 搜索检查是否有最新版本的 iot-device-client

  4. 保存并关闭 pom.xml 文件。

  5. 使用文本编辑器打开 simulated-device\src\main\java\com\mycompany\app\App.java 文件。
  6. 在该文件中添加以下 import 语句:

    import com.microsoft.azure.sdk.iot.device.*;
    import com.google.gson.Gson;
    
    import java.io.*;
    import java.net.URISyntaxException;
    import java.util.Random;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ExecutorService;
    
  7. 将以下类级变量添加到 App 类。 将 {youriothubname} 替换为 IoT 中心名称,将 {yourdevicekey} 替换为在创建设备标识部分中生成的设备密钥值:

    private static String connString = "HostName={youriothubname}.azure-devices.cn;DeviceId=myFirstJavaDevice;SharedAccessKey={yourdevicekey}";
    private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
    private static String deviceId = "myFirstJavaDevice";
    private static DeviceClient client;
    

    本示例应用在实例化 DeviceClient 对象时使用 protocol 变量。 可以使用 MQTT、AMQP 或 HTTPS 协议与 IoT 中心通信。

  8. App 类中添加以下嵌套的 TelemetryDataPoint 类,以指定设备要发送到 IoT 中心的遥测数据:

    private static class TelemetryDataPoint {
      public String deviceId;
      public double temperature;
      public double humidity;
    
      public String serialize() {
        Gson gson = new Gson();
        return gson.toJson(this);
      }
    }
    
  9. App 类中添加以下嵌套的 EventCallback 类,以显示 IoT 中心在处理来自设备应用的消息时返回的确认状态。 处理消息时,此方法还会通知应用中的主线程:

    private static class EventCallback implements IotHubEventCallback {
      public void execute(IotHubStatusCode status, Object context) {
        System.out.println("IoT Hub responded to message with status: " + status.name());
    
        if (context != null) {
          synchronized (context) {
            context.notify();
          }
        }
      }
    }
    
  10. App 类中添加以下嵌套的 MessageSender 类。 此类中的 run 方法会生成要发送到 IoT 中心的示例遥测数据,并在发送下一条消息之前等待确认:

    private static class MessageSender implements Runnable {
    
      public void run()  {
        try {
          double minTemperature = 20;
          double minHumidity = 60;
          Random rand = new Random();
    
          while (true) {
            double currentTemperature = minTemperature + rand.nextDouble() * 15;
            double currentHumidity = minHumidity + rand.nextDouble() * 20;
            TelemetryDataPoint telemetryDataPoint = new TelemetryDataPoint();
            telemetryDataPoint.deviceId = deviceId;
            telemetryDataPoint.temperature = currentTemperature;
            telemetryDataPoint.humidity = currentHumidity;
    
            String msgStr = telemetryDataPoint.serialize();
            Message msg = new Message(msgStr);
            msg.setProperty("temperatureAlert", (currentTemperature > 30) ? "true" : "false");
            msg.setMessageId(java.util.UUID.randomUUID().toString()); 
            System.out.println("Sending: " + msgStr);
    
            Object lockobj = new Object();
            EventCallback callback = new EventCallback();
            client.sendEventAsync(msg, callback, lockobj);
    
            synchronized (lockobj) {
              lockobj.wait();
            }
            Thread.sleep(1000);
          }
        } catch (InterruptedException e) {
          System.out.println("Finished.");
        }
      }
    }
    

    IoT 中心确认前面的消息一秒后,此方法将发送新的设备到云消息。 该消息包含一个具有设备 ID 的 JSON 序列化对象和一个随机生成的编号,用于模拟温度传感器和湿度传感器。

  11. main 方法替换为以下代码,该代码创建用于向 IoT 中心发送设备到云消息的线程:

    public static void main( String[] args ) throws IOException, URISyntaxException {
      client = new DeviceClient(connString, protocol);
      client.open();
    
      MessageSender sender = new MessageSender();
    
      ExecutorService executor = Executors.newFixedThreadPool(1);
      executor.execute(sender);
    
      System.out.println("Press ENTER to exit.");
      System.in.read();
      executor.shutdownNow();
      client.closeNow();
    }
    
  12. 保存并关闭 App.java 文件。
  13. 若要使用 Maven 构建 simulated-device 应用,请在 simulated-device 文件夹的命令提示符处执行以下命令:

    mvn clean package -DskipTests
    

Note

为简单起见,本教程不实现任何重试策略。 在生产代码中,应该按 MSDN 文章 Transient Fault Handling(暂时性故障处理)中所述实施重试策略(例如指数性的回退)。

运行应用

现在,已准备就绪,可以运行应用。

  1. 在 read-d2c 文件夹的命令提示符处,运行以下命令监视 IoT 中心的第一个分区:

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

    用于监视设备到云的消息的 Java IoT 中心服务应用

  2. 在 simulated-device 文件夹的命令提示符处,运行以下命令将遥测数据发送到 IoT 中心:

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

    用于发送设备到云消息的 Java IoT 中心设备应用

  3. Azure 门户中的“使用情况”磁贴显示发送到 IoT 中心的消息数:

    显示发送到 IoT 中心的消息数的 Azure 门户“使用情况”磁贴

后续步骤

在本教程中,已在 Azure 门户中配置了新的 IoT 中心,然后在 IoT 中心的标识注册表中创建了设备标识。 已使用此设备标识来让设备应用向 IoT 中心发送设备到云的消息。 还创建了一个应用,用于显示 IoT 中心接收的消息。

若要继续了解 IoT 中心入门知识并浏览其他 IoT 方案,请参阅:

若要了解如何扩展 IoT 解决方案和如何大规模处理设备到云的消息,请参阅 Process device-to-cloud messages (处理设备到云的消息)教程。

若要继续了解 Azure IoT 中心入门知识并浏览其他 IoT 方案,请参阅以下文章: