更新时间:2024-08-21 GMT+08:00

消息下发

概述

消息下发不依赖产品模型,提供给设备的单向通知,具有消息缓存功能;云端消息下发中,平台会以异步方式(消息下发后无需等待设备侧回复响应)下发消息给设备;若设备不在线,则在设备上线后发送数据(支持配置,最长缓存时间24小时)。平台对每个设备默认只保存20条消息,超过20条后,后续的消息会替换下发最早的消息。同时,消息下发支持使用自定义topic的格式进行数据下发。

表1 消息下发Topic类别

消息下发Topic类别

描述

系统Topic

平台预先定义了各种设备和平台通信的Topic,具体Topic列表和功能说明可参考Topic定义

自定义Topic

用户可以自定义Topic,设备和平台间可以基于用户自定义的Topic进行通信。

自定义topic分类:

  • 在产品中定义需要使用的Topic,这类Topic有$oc/devices/{device_id}/user/前缀,消息上报或者消息下发时平台会校验Topic是否在产品中定义,未在产品中定义的Topic会被平台拒绝。使用方式可以参考链接使用自定义Topic进行通信的最佳实践。
  • 使用非$oc开头的自定义Topic,如/aircondition/data/up进行消息通信,这类Topic平台不校验Topic权限,根据MQTT协议定义的规则进行Topic的消息上下行通信。

使用场景

  • 数据格式需要自定义,不依赖物模型。

使用限制

  • 单个消息内容不大于256KB。
  • 单个设备下发消息缓存数量为20个。
  • MQTT自定义Topic支持的最大长度为128字节。
  • 缓存时间支持配置,最长不超过24小时。

使用服务质量

  • 支持MQTT的服务质量等级为QoS 0、QoS 1,不支持QoS 2。
  • Topic的QoS为 0 时,消息下发不需要等设备回ACK,仅只下发一次; Topic的QoS为 1时,消息下发需要等设备回ACK。
  • 平台默认该设备订阅了QoS为0的系统Topic, 如果需要QoS为1的下行系统Topic,需要设备设置订阅QoS。
  • 设备订阅非$oc开头的自定义Topic, 如果需要平台支持该Topic的QoS为1, 需向平台提交工单联系申请。
  • 如果设备订阅Topic的QoS为1, 平台下发消息没有收到设备确认ACK时, 平台会重发消息, 默认每隔2s重发1次, 共重发3次;

    重发后设备依旧没有回确认响应且消息还在缓存时间内, 设备再次上线或订阅Topic时, 平台会再重发消息, 默认每隔10s重发1次, 共重发5次;

    同时每次重发也会触发每隔2s重发机制, 故订阅Topic的QoS为1时平台会重发消息。平台重发消息后,设备会收到重复消息,建议设备要有去重机制。

消息缓存下发使用说明

消息下发是平台向设备直接下发消息的一种方式。消息下发具有缓存特性,当设备不在线时,平台会对下发的消息进行缓存,直到设备上线。

以下使用系统Topic进行设备消息缓存下发说明:
图1 消息缓存下发流程
  1. 应用侧或平台用下发设备消息接口,下发请求到物联网平台,下发消息样例如下:
    POST https://{Endpoint}/v5/iot/{project_id}/devices/{device_id}/messages
    Content-Type: application/json
    X-Auth-Token: ********
    
    {
      "message_id": "99b32da9-cd17-4cdf-a286-f6e849cbc364",  
      "name": "messageName",  
      "message": "HelloWorld"
    }
  2. 物联网平台向应用返回201 Created,消息状态为PENDING。
  3. 物联网平台通过设备消息状态变更通知接口推送消息结果给应用,设备未上线时,设备消息状态为缓存(PENDING),对应的消息样例如下:
    Topic: $oc/devices/{device_id}/sys/messages/down  
    数据格式:
    {  
        "resource": "device.message.status",  
        "event": "update",  
        "notify_data": {   
          "message_id": "string",   
          "name": "string",    
          "device_id": "string",    
          "status": "PENDING",   
          "timestamp": "string"  
         }
     }
  4. 设备上线。
  5. 设备订阅消息下发的topic(平台采用了隐式订阅的功能,对于下行的系统topic,设备无需订阅,非系统topic需要设备订阅),用于接收消息。
  6. 物联网平台根据协议规范下发消息给设备。下发的消息样例如下:
    Topic: $oc/devices/{device_id}/sys/messages/down  
    数据格式:
    {
        "object_device_id": "{object_device_id}",
        "name": "name",
        "id": "id",
        "content": "hello"
    }
  7. 平台将消息的最终结果推送给应用服务器,设备消息状态为已发送(DELIVERED)。使用接口:设备消息状态变更通知接口
    Topic: $oc/devices/{device_id}/sys/messages/down  
    数据格式:
    {  
        "resource": "device.message.status",  
        "event": "update",  
        "notify_data": {   
          "message_id": "string",   
          "name": "string",    
          "device_id": "string",    
          "status": "DELIVERED",   
          "timestamp": "string"  
         }
     }

消息下发使用QoS 1说明

MQTT设备接入,使用QoS 1的系统Topic进行设备消息下发说明:

图2 消息下发使用Qos 1流程图
  1. 设备上线。
  2. 订阅Topic,设置订阅Topic的QoS为1。
    图3 订阅Topic的QoS为1
  3. 应用侧或平台用下发设备消息接口,下发请求到物联网平台,下发消息样例如下:
    POST https://{Endpoint}/v5/iot/{project_id}/devices/{device_id}/messages
    Content-Type: application/json
    X-Auth-Token: ********
    
    {
      "message_id": "99b32da9-cd17-4cdf-a286-f6e849cbc364",  
      "name": "messageName",  
      "message": "HelloWorld"
    }
  4. 物联网平台根据协议规范下发消息给设备。MQTT设备必须先订阅对应的Topic才能收到平台下发的消息(平台采用了隐式订阅的功能,对于下行的系统topic,设备无需订阅,非系统topic需要设备订阅),消息样例如下:
    Topic: $oc/devices/{device_id}/sys/messages/down  
    数据格式:
    {
        "object_device_id": "{object_device_id}",
        "name": "name",
        "id": "id",
        "content": "hello"
    }
  5. 物联网平台向设备下发消息后,向应用服务器返回201 Created,消息状态为DELIVERED。消息下发是异步操作,不需要等设备ACK就可以回响应。
  6. 物联网平台没有收到设备接收消息的ACK响应,重发消息;默认每隔2s重发一次,总下发3次。
  7. 设备再次上线或订阅Topic。
  8. 物联网平台会重发之前设备未回ACK且未超时的消息,默认每隔10s重发一次,总下发5次;每次重发也会触发每隔2s重发机制。
  9. 平台将消息的最终结果推送给应用服务器,设备消息状态为已发送(DELIVERED)或超时(TIMEOUT)。使用接口:设备消息状态变更通知接口
    Topic: $oc/devices/{device_id}/sys/messages/down  
    数据格式:
    {  
        "resource": "device.message.status",  
        "event": "update",  
        "notify_data": {   
          "message_id": "string",   
          "name": "string",    
          "device_id": "string",    
          "status": "DELIVERED",   
          "timestamp": "string"  
         }
     }

下发消息状态

MQTT设备消息执行状态以及状态变化机制如下所示。

图4 设备消息状态
表2 消息执行状态

消息执行状态

说明

缓存(PENDING)

MQTT协议设备不在线,物联网平台会将消息进行缓存,此时任务状态为“缓存”状态。

超时(TIMEOUT)

物联网平台缓存的PENDING状态的消息,如果1天之内(支持配置,最长缓存时间24小时)还没有下发下去,物联网平台会将消息状态设置为“超时”。

已发送(DELIVERED)

物联网平台将消息发送给设备后,状态变为“已发送”。

失败(FAILED)

物联网平台发送消息给设备不成功,消息状态变为“失败”。

平台消息下发使用示例

云端消息下发,控制台上创建下发任务,MQTT协议设备接入为例,在设备接入控制台上进行消息缓存下发。

  1. 访问设备接入服务,单击“管理控制台”进入设备接入控制台。选择您的实例,单击实例卡片进入。
  2. 选择左侧导航栏的设备 > 所有设备,在设备列表中,单击具体的设备进入到设备的详情页面。
  3. 云端下发>消息下发标签页,单击“下发消息”,在弹出的窗口中选择需要下发的命令并设置命令参数。

    图5 下发消息-MQTT

  4. 可以在平台看到下发状态为已发送。

    图6 下发消息-查询结果

配置应用侧Java SDK步骤如下:

  1. 配置Maven依赖,本示例使用的开发环境为JDK 1.8及以上版本。SDK代码获取:SDK下载

    <dependency>
        <groupId>com.huaweicloud.sdk</groupId>
        <artifactId>huaweicloud-sdk-core</artifactId>
        <version>[3.0.40-rc, 3.2.0)</version>
    </dependency>
    <dependency>
        <groupId>com.huaweicloud.sdk</groupId>
        <artifactId>huaweicloud-sdk-iotda</artifactId>
        <version>[3.0.40-rc, 3.2.0)</version>
    </dependency>

  2. 应用侧向单个设备下发消息样例如下:

    public class MessageDistributionSolution {
        // REGION_ID:如果是上海一,请填写"cn-east-3";如果是北京四,请填写"cn-north-4";如果是华南广州,请填写"cn-south-4"
        private static final String REGION_ID = "<YOUR REGION ID>";
        // ENDPOINT:请在控制台的"总览"界面的"平台接入地址"中查看“应用侧”的https接入地址。
        private static final String ENDPOINT = "<YOUR ENDPOINT>";
        // 标准版/企业版:需自行创建Region对象
        public static final Region REGION_CN_NORTH_4 = new Region(REGION_ID, ENDPOINT);
        public static void main(String[] args) {
            String ak = "<YOUR AK>";
            String sk = "<YOUR SK>";
            String projectId = "<YOUR PROJECTID>";
            // 创建认证
            ICredential auth = new BasicCredentials().withDerivedPredicate(AbstractCredentials.DEFAULT_DERIVED_PREDICATE)
                .withAk(ak)
                .withSk(sk)
                .withProjectId(projectId);
            // 创建IoTDAClient实例并初始化
            IoTDAClient client = IoTDAClient.newBuilder().withCredential(auth)
                // 基础版:请选择IoTDARegion中的Region对象
                //.withRegion(IoTDARegion.CN_NORTH_4)
                // 标准版/企业版:需自行创建Region对象
                .withRegion(REGION_CN_NORTH_4).build();
            // 实例化请求对象
            CreateMessageRequest request = new CreateMessageRequest();
            request.withDeviceId("<YOUR DEVICE_ID>");
            DeviceMessageRequest body = new DeviceMessageRequest();
            body.withMessage("<YOUR DEVICE MESSAGE>");
            request.withBody(body);
            try {
                CreateMessageResponse response = client.createMessage(request);
                System.out.println(response.toString());
            } catch (ConnectionException e) {
                e.printStackTrace();
            } catch (RequestTimeoutException e) {
                e.printStackTrace();
            } catch (ServiceResponseException e) {
                e.printStackTrace();
                System.out.println(e.getHttpStatusCode());
                System.out.println(e.getRequestId());
                System.out.println(e.getErrorCode());
                System.out.println(e.getErrorMsg());
            }
        }
    }
    表3 参数说明

    参数

    说明

    ak

    您的华为云账号访问密钥ID(Access Key ID)。请在华为云控制台我的凭证 > 访问密钥页面上创建和查看您的 AK/SK。更多信息请查看访问密钥

    sk

    您的华为云账号秘密访问密钥(Secret Access Key)。

    projectId

    项目ID。获取方法请参见 获取项目ID

    IoTDARegion.CN_NORTH_4

    请替换为您要访问的物联网平台的区域,当前物联网平台可以访问的区域,在SDK代码IoTDARegion.java中已经定义。

    您可以在控制台上查看当前服务所在区域名称,区域名称、区域和终端节点的对应关系,具体步骤请参考平台对接信息

    REGION_ID

    如果是上海一,请填写“cn-east-3”;如果是北京四,请填写“cn-north-4”;如果是华南广州,请填写“cn-south-4”。

    ENDPOINT

    请在控制台的总览界面的“接入信息”中查看“应用接入”的https接入地址。

    DEVICE_ID

    下发消息的设备ID,用于唯一标识一个设备,在注册设备时由物联网平台分配获得。 取值范围:长度不超过128,只允许字母、数字、下划线(_)、连接符(-)的组合。

配置设备侧Java SDK步骤如下,示例中使用的开发环境为JDK 1.8及以上版本。SDK代码获取:SDK下载

  1. 配置设备侧SDK的Maven依赖。

    <dependency>
    	<groupId>com.huaweicloud</groupId>
    	<artifactId>iot-device-sdk-java</artifactId>
    	<version>1.1.4</version>
    </dependency>

  2. 配置设备侧SDK,设备连接参数。

    //加载iot平台的ca证书,获取连接参考:https://support.huaweicloud.com/intl/zh-cn/devg-iothub/iot_02_1004.html
    URL resource = BroadcastMessageSample.class.getClassLoader().getResource("ca.jks");
    File file = new File(resource.getPath());
    
    //注意格式为:ssl://域名信息:端口号。
    //域名获取方式:登录华为云IoTDA控制台左侧导航栏“总览”页签,在选择的实例基本信息中,单击“接入信息”。选择8883端口对应的接入域名。
    String serverUrl = "ssl://localhost:8883";
    //在IoT平台创建的设备ID。
    String deviceId = "deviceId";
    //设备ID对应的密钥。
    String deviceSecret = "secret";
    //创建设备
    IoTDevice device = new IoTDevice(serverUrl, deviceId, deviceSecret, file);
    if (device.init() != 0) {
        return;
    }

  3. 消息下发回调函数定义。

    client.setDeviceMessageListener(deviceMessage -> {
        log.info("the onDeviceMessage is {}", deviceMessage.toString());
    });

测试验证步骤如下:

  1. 在设备接入控制台,选择您的实例,单击实例卡片进入。选择左侧导航栏的设备 > 所有设备,单击具体设备详情进入设备详情,在消息跟踪页签内,启动消息跟踪

    图7 消息跟踪-启动消息跟踪

  2. 运行应用侧SDK代码,下发消息,应用侧可以看到平台响应样例如下:

    图8 应用侧消息下发成功响应

  3. 在平台的消息跟踪中可以看到:

    图9 消息跟踪-缓存下发消息

  4. 设备端运行设备侧 SDK代码,设备侧收到消息时日志格式样例如下:

    图10 设备消息下发成功