# 消息下发
#### 概述
消息下发不依赖产品模型，提供给设备的单向通知，具有消息缓存功能；云端消息下发中，平台会以异步方式（消息下发后无需等待设备侧回复响应）下发消息给设备；若设备不在线，则在设备上线后发送数据（支持配置，最长缓存时间24小时）。平台对每个设备默认只保存20条消息，超过20条后，后续的消息会替换下发最早的消息。同时，消息下发支持使用[自定义Topic通信](https://support.huaweicloud.com/usermanual-iothub/iot_02_9992.html)的格式进行数据下发。
表1消息下发Topic类别 
| 消息下发Topic类别 | 描述                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
|:---|:---|
| 系统Topic     | 平台预先定义了各种设备和平台通信的Topic，具体Topic列表和功能说明可参考[Topic定义](https://support.huaweicloud.com/api-iothub/iot_06_v5_3004.html)。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| 自定义Topic   | 用户可以自定义Topic，设备和平台间可以基于用户自定义的Topic进行通信。 **自定义topic分类：** - 在产品中定义需要使用的Topic，这类Topic有$oc/devices/{device_id}/user/前缀，消息上报或者消息下发时平台会校验Topic是否在产品中定义，未在产品中定义的Topic会被平台拒绝。使用方式可以参考链接[使用自定义Topic进行通信](https://support.huaweicloud.com/bestpractice-iothub/iot_bp_0019.html)的最佳实践。    - 使用非$oc开头的自定义Topic，如/aircondition/data/up进行消息通信，这类Topic平台不校验Topic权限，根据MQTT协议定义的规则进行Topic的消息上下行通信。使用方式可以参考链接[第三方云设备迁移至华为云最佳实践](https://support.huaweicloud.com/bestpractice-iothub/iot_bp_0092.html)。   |
   
#### 使用场景
数据格式需要自定义，不依赖物模型。
#### 使用限制
- 单个消息内容不大于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, 需向平台[提交工单](https://console.huaweicloud.com/ticket/?locale=zh-cn#/ticketindex/createIndex)联系申请。
- 如果设备订阅Topic的QoS为1, 平台下发消息没有收到设备确认ACK时, 平台会重发消息, 默认每隔2s重发1次, 共重发3次; 重发后设备依旧没有回确认响应且消息还在缓存时间内, 设备再次上线或订阅Topic时, 平台会再重发消息, 默认每隔10s重发1次, 共重发5次;
  同时每次重发也会触发每隔2s重发机制, 故订阅Topic的QoS为1时平台会重发消息。平台重发消息后，设备会收到重复消息，建议设备要有去重机制。
  
 
#### 相关API接口
- [下发设备消息](https://support.huaweicloud.com/api-iothub/iot_06_v5_0059.html)
- [平台消息下发](https://support.huaweicloud.com/api-iothub/iot_06_v5_3017.html)
 
#### 消息缓存下发使用说明
消息下发是平台向设备直接下发消息的一种方式。消息下发具有缓存特性，当设备不在线时，平台会对下发的消息进行缓存，直到设备上线。
以下使用系统Topic进行设备消息缓存下发说明：
图1消息缓存下发流程   
![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001890919818.png "点击放大")
1. 应用侧或平台用[下发设备消息](https://support.huaweicloud.com/api-iothub/iot_06_v5_0059.html)接口，下发请求到物联网平台，下发消息样例如下：
   ```
   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. 物联网平台通过[设备消息状态变更通知](https://support.huaweicloud.com/api-iothub/iot_06_v5_01203.html)推送消息结果给应用，设备未上线时，设备消息状态为缓存（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）。使用接口：[设备消息状态变更通知](https://support.huaweicloud.com/api-iothub/iot_06_v5_01203.html)。
   ```
   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流程图   
![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001891079754.png "点击放大")
1. 设备上线。
2. 订阅Topic，设置订阅Topic的QoS为1。
   图3订阅Topic的QoS为1   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001934479497.png "点击放大") 
3. 应用侧或平台用[下发设备消息](https://support.huaweicloud.com/api-iothub/iot_06_v5_0059.html)接口，下发请求到物联网平台，下发消息样例如下：
   ```
   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）。使用接口：[设备消息状态变更通知](https://support.huaweicloud.com/api-iothub/iot_06_v5_01203.html)。
   ```
   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设备消息状态   
![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001890919822.png "点击放大")
表2消息执行状态 
| 消息执行状态          | 说明                                                                     |
|:---|:---|
| 缓存（PENDING）      | MQTT协议设备不在线，物联网平台会将消息进行缓存，此时任务状态为"缓存"状态。                                                      |
| 超时（TIMEOUT）    | 物联网平台缓存的PENDING状态的消息，如果1天之内（支持配置，最长缓存时间24小时）还没有下发下去，物联网平台会将消息状态设置为"超时"。 |
| 已发送（DELIVERED） | 物联网平台将消息发送给设备后，状态变为"已发送"。                                              |
| 失败（FAILED）       | 物联网平台发送消息给设备不成功，消息状态变为"失败"。                                                                   |
   
#### 平台消息下发使用示例
云端消息下发，控制台上创建下发任务，MQTT协议设备接入为例，在设备接入控制台上进行消息缓存下发。
1. 访问[设备接入服务](https://www.huaweicloud.com/product/iothub.html)，单击"管理控制台"进入设备接入控制台。选择您的实例，单击实例卡片进入。
2. 选择左侧导航栏的"设备 \> 所有设备"，在设备列表中，单击具体的设备进入到设备的详情页面。
3. 在"云端下发\>消息下发"标签页，单击"下发消息"，在弹出的窗口中填写对应消息内容。 
   图5下发消息-MQTT   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001981577881.png "点击放大")
   
   
4. 可以在平台看到下发状态为已发送。 
   图6下发消息-查询结果   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000002298867873.png "点击放大")
   
   
 
#### 平台批量消息下发使用示例
云端消息批量下发，控制台上创建批量下发消息任务，MQTT协议设备接入为例，在设备接入控制台上进行批量消息缓存下发。
1. 访问[设备接入服务](https://www.huaweicloud.com/product/iothub.html)，单击"管理控制台"进入设备接入控制台。选择您的实例，单击实例卡片进入。
2. 选择左侧导航栏的"设备 \> 所有设备"，单击"批量下发消息"页签，进入"批量下发消息"标签页。 
   - 单击"批量下发消息"按钮，在弹出的窗口中填写任务参数和下发消息参数。
     图7批量下发消息   
     ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000002499953932.png "点击放大") 
   
   
   
   
#### 配置应用侧Java SDK
1. 配置Maven依赖，本示例使用的开发环境为JDK 1.8及以上版本。SDK代码获取：[SDK下载](https://support.huaweicloud.com/sdkreference-iothub/iot_10_1002.html)。
   
   ```
   <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。更多信息请查看[访问密钥](https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html)。                                                                                                                                                                                                                                                                                                |
   | sk                     | 您的华为云账号秘密访问密钥（Secret Access Key）。                                                                                                                                                                                                                                                                                                                                                                                                                |
   | projectId               | 项目ID。获取方法请参见[获取项目ID](https://support.huaweicloud.com/api-iothub/iot_06_v5_1001.html)。                                                                                                                                                                                                                                                                                                                                                             |
   | IoTDARegion.CN_NORTH_4 | 请替换为您要访问的物联网平台的区域，当前物联网平台可以访问的区域，在SDK代码[IoTDARegion.java](https://github.com/huaweicloud/huaweicloud-sdk-java-v3/tree/master/services/iotda/src/main/java/com/huaweicloud/sdk/iotda/v5/region/IoTDARegion.java)中已经定义。 您可以在控制台上查看当前服务所在区域名称，区域名称、区域和终端节点的对应关系，具体步骤请参考[平台对接信息](https://support.huaweicloud.com/usermanual-iothub/iot_01_01271.html#ZH-CN_TOPIC_0000001514571325__section436212919379)。 |
   | REGION_ID              | 请替换为您要访问的物联网平台的区域。如果是上海一，请填写"cn-east-3"；如果是北京四，请填写"cn-north-4";如果是华南广州，请填写"cn-south-4"。                                                                                                                                                                                                                                                                                                                                                            |
   | ENDPOINT                 | 请在控制台的"总览"界面的"接入信息"中查看"应用接入"的https接入地址。                                                                                                                                                                                                                                                                                                                                                                                                          |
   | DEVICE_ID              | 下发消息的设备ID，用于唯一标识一个设备，在注册设备时由物联网平台分配获得。 取值范围：长度不超过128，只允许字母、数字、下划线（_）、连接符（-）的组合。                                                                                                                                                                                                                                                                                                                                                                   |
      
   
   
 
#### 配置设备侧Java SDK
示例中使用的开发环境为JDK 1.8及以上版本。SDK代码获取：[SDK下载](https://support.huaweicloud.com/sdkreference-iothub/iot_10_1002.html)。
1. 配置设备侧SDK的Maven依赖。 
   ```
   <dependency>
   <groupId>com.huaweicloud</groupId>
   <artifactId>iot-device-sdk-java</artifactId>
   <version>1.1.4</version>
   </dependency>
   ```
   
   
2. 配置设备侧SDK，设备连接参数。 
   ```
   //加载iot平台的ca证书。获取链接参考：证书资源
   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. 在设备接入控制台，选择您的实例，单击实例卡片进入。选择左侧导航栏的"设备 \> 所有设备"，单击具体设备"详情"进入设备详情，在"消息跟踪"页签内，启动"消息跟踪"。 
   图8消息跟踪-启动消息跟踪   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001950378340.png "点击放大")
   
   
2. 运行应用侧SDK代码，下发消息，应用侧可以看到平台响应样例如下: 
   图9应用侧消息下发成功响应   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001890919826.png "点击放大")
   
   
3. 在平台的消息跟踪中可以看到下发结果。 
   图10消息跟踪-缓存下发消息   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001982034653.png "点击放大")
   
   
4. 设备端运行设备侧 SDK代码，设备侧收到消息时日志格式样例如下： 
   图11设备消息下发成功   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001934479505.png "点击放大")
   
   
