更新时间:2022-02-21 GMT+08:00

接口实现注意事项

接口需要支持线程安全

decode和encode函数需要支持线程安全,不得添加成员变量或静态变量来缓存过程数据。

错误示例:多线程并发时A线程将status设置为Failed,B线程可能会同时设置为Success,从而导致status不正确,引起程序运行异常。

public class ProtocolAdapter {
private String status;

@Override
public ObjectNode decode(finalbyte[] binaryData) throws Exception {
if (binaryData == null) {
status = "Failed";
return null;
}
ObjectNode node;
           ...;
status = "Success";
return node;
}

@Override
public byte[] encode(finalObjectNode input) throws Exception {
if ("Failed".equals(status)) {
status = null;
return null;
}
byte[] output;
           ...;
status = null;
return output;
}
}

正确示例:直接使用入参编解码,编解码库不做业务处理。

public class ProtocolAdapter {
@Override
public ObjectNode decode(finalbyte[] binaryData) throws Exception {
ObjectNode node;
  ...;
return node;
}

@Override
public byte[] encode(finalObjectNode input) throws Exception {
byte[] output;
  ...;
return output;
}
} 

mid字段的解释

IoT平台是依次进行命令下发的,但IoT平台收到命令执行结果响应的次数未必和命令下发的次序相同,mid就是用来将命令执行结果响应和下发的命令进行关联的。在IoT平台,是否实现mid,消息流程也有所不同:

实现mid

图1 实现mid的消息流程

若实现了mid,并且命令执行结果已上报成功,则:

  1. 命令执行结果响应中的状态(SUCCESSFUL/FAILED)会刷新到平台数据库中该命令的记录;
  2. 平台推送给应用服务器的命令执行结果通知中携带commandId;
  3. 应用服务器查询会得到该命令的状态为SUCCESSFUL/FAILED。

不实现mid

图2 不实现mid的消息流程

若不实现mid,并且命令执行结果已上报成功,则:

  1. 命令执行结果响应中的状态(SUCCESSFUL/FAILED)不会刷新到平台数据库中该命令的记录;
  2. 平台推送给应用服务器的命令执行结果通知中不携带commandId;
  3. 应用服务器查询会得到该命令的最终状态为DELIVERED。
说明:
  • 上述两个消息流程旨在解释mid字段的作用,部分消息流程在图中简化处理。
  • 针对只关注命令是否送达设备,不关注设备对命令执行情况的场景,设备和编解码插件不需要实现对mid的处理。
  • 如果厂商评估后不实现mid,则应用服务器不能在IoT平台获取命令的执行结果,需要应用服务器自行实现解决方案。比如应用服务器在收到命令执行结果响应(不带commandId)后,可以根据如下方法来进行响应匹配:
    • 根据命令下发的顺序。使用此方法,平台在对同一设备同时下发多条命令时,一旦发生丢包,将会导致命令执行结果和已下发的命令匹配错误。因此,建议应用服务器每次对同一设备仅下发一条命令,在收到命令执行结果响应后,再下发下一条命令。
    • 编解码插件可以在命令响应消息的resultDetail里加上命令的相关信息来帮助识别命令,比如命令码。应用服务器根据resultDetail里的信息来识别命令执行结果响应和已下发命令的对应关系。

禁止使用DirectMemory

DirectMemory是直接调用操作系统接口申请内存,不受JVM的控制,使用不当很容易造成操作系统内存不足,因此编解码插件代码中禁止使用DirectMemory。

错误示例:使用UNSAFE.allocateMemory申请直接内存

if ((maybeDirectBufferConstructor instanceof Constructor))
{
      address = UNSAFE.allocateMemory(1L);
      Constructor<?> directBufferConstructor;
      ...
}
else
{
      ...
}