华为云用户手册

  • 字符集排序规则 表2 排序规则说明 排序规则选项 说明 区分大小写(_ CS ) 区分大写字母和小写字母。 如果指定此项,排序时小写字母将在其对应的大写字母之前。 如果未指定此选项,排序规则将不区分大小写。 即RDS for SQL Server 在排序时将大写字母和小写字母视为相同。 通过指定 _CI,可以显式选择不区分大小写。 区分重音(_AS) 区分重音字符和非重音字符。 例如,“a”和“ấ”为不同字符。 如果未指定此选项,则排序规则将不区分重音。 即RDS for SQL Server 在排序时将字母的重音形式和非重音形式视为相同。 通过指定 _AI,可以显式选择不区分重音。
  • 支持的实例字符集 RDS for SQL Server当前支持的实例字符集如表1所示: 表1 支持的实例字符集 实例字符集 说明 Chinese_PRC_90_CI_AI Chinese-PRC-90,不区分大小写、不区分重音(兼容SQL Server 2005)。 Chinese_PRC_CI_AI Chinese-PRC,不区分大小写、不区分重音。 Chinese_PRC_CI_AS Chinese-PRC,不区分大小写、区分重音。 Chinese_PRC_CS_AS Chinese-PRC,区分大小写、区分重音。 Cyrillic_General_CI_AS Cyrllic_General,不区分大小写,区分重音。 SQL_Latin1_General_CP1_CI_AS 对于Unicode 数据为 Latin1_General,不区分大小写,区分重音。 THAI_CI_AS THAI,不区分大小写、区分重音。
  • 通过DRS升级大版本 RDS for MySQL支持通过DRS将RDS for MySQL 5.6版本数据迁移到5.7版本。使用该方式进行大版本升级,需要提前准备好待迁移到的高版本数据库实例。 您可以在“实例管理”页面,单击目标实例名称,在页面右上角,单击“迁移数据库”,进入 数据复制服务 信息页面。 具体迁移操作,请参见《数据复制服务用户指南》的“入云迁移”内容。 表1 MySQL数据库版本信息 源数据库版本 目标数据库版本 迁移类型 RDS for MySQL/自建MySQL/其他云MySQL 5.5.x 5.6.x 5.7.x 8.0.x RDS for MySQL 5.6.x 5.7.x 8.0.x MySQL数据库版本升级 DRS仅支持从低版本迁移到高版本。
  • 注意事项 升级数据库大版本过程将造成不超过五分钟的业务中断,请您尽量在业务低峰期执行该操作,并且确保您的应用有自动重连机制。 升级主实例大版本时,如有只读实例,也会同步升级只读实例的大版本,升级大版本过程将造成不超过五分钟的业务中断,请您选择合适的时间升级(不支持单独升级只读实例的大版本)。 升级大版本后,实例会升级到新的内核大版本,不支持降级。 请务必仔细对比升级前后版本差异,如有必要建议新建一个RDS for MySQL 5.7版本实例进行语法测试,避免应用使用的低版本语法或特性在升级高版本后不支持。 建议您克隆原实例,先使用克隆实例进行升级测试,确认各项功能正常后再升级原实例。 内核大版本升级期间需要确保实例预留了足够的空间支撑升级过程中的业务写入。 大版本升级定时任务需预准备,设置后不可取消。 升级大版本后,升级前的备份将不可用于新版本实例,时间点恢复功能将不能选择升级前的时间点。 大版本升级过程中禁止event的DDL操作,如create event、drop event和alter event。 大版本升级后,规格参数会恢复到升级版本的默认值,包括以下参数:threadpool_size、innodb_buffer_pool_size、innodb_io_capacity、innodb_io_capacity_max、innodb_buffer_pool_instances、back_log、max_connections。
  • 约束限制 仅支持RDS for MySQL 5.6版本升级到5.7最新版本。有关内核版本详情,请参见内核版本说明。 对于主备实例,复制延迟大于300秒无法升级大版本。 实例中存在异常节点,无法升级大版本。 MySQL 5.7及之后版本不再支持Sequence Engine,升级前请确认。 目前RDS for MySQL实例最大可支持50万张表,大于50万张表时,可能会导致大版本升级失败。 云数据库RDS for MySQL暂不支持已开启事件定时器功能的实例升级大版本,若您想使用该功能,请先关闭事件定时器。具体操作请参考开启或关闭事件定时器。
  • 操作场景 该任务指导用户首次使用漏洞管理服务时,如何购买漏洞管理服务的专业版、高级版和企业版扫描功能。 仅支持从专业版升级至高级版,当您是专业版用户时,如果需要将专业版扫描配额包中的二级域名配额升级为一级域名配额,可以直接将专业版升级到高级版。 不支持多个版本同时存在。如果是老客户,已购买的版本下存在基础版和专业版,基础版全部免费升级为专业版,版本到期时间以订单到期时间最长的为准。 不支持从专业版或高级版直接升级至企业版,当您是专业版或高级版用户时,如果需要使用企业版,请直接购买企业版。为保证您的权益,请您购买企业版后,提工单退订专业版或高级版。 购买漏洞管理服务或配额后,不支持直接修改配额,仅支持升级规格,请谨慎操作。如需减少配额请参考如何减少漏洞管理服务配额?。
  • 云审计 服务支持的漏洞管理服务操作列表 通过云审计服务,用户可以记录与漏洞管理服务相关的操作事件,便于日后的查询、审计和回溯。 开启了云审计服务后,系统开始记录漏洞管理服务资源的操作。 云审计服务管理控制台保存最近7天的操作记录,查看云审计日志操作请参考查看审计事件。 云审计服务支持的漏洞管理服务操作列表如表1所示。 表1 云审计服务支持的漏洞管理服务操作列表 操作名称 资源类型 事件名称 网站 创建域名 domain createDomain 删除域名 domain deleteDomain 编辑域名 domain editDomain 免认证/一键认证 domain authenticateDomain 快捷认证 domain authorizeDomain 创建 漏洞扫描 任务 scan createScanTask 创建内部漏洞扫描任务 scan createInnerScanTask 重启漏洞扫描任务 scan restartScanTask 取消漏洞扫描任务 scan cancelScanTask 编辑漏洞扫描任务 scan editScanTask 创建订阅套餐 resource createPurchaseOrder 更新订阅套餐 resource createAlterOrder 批量更新订阅套餐 resource createBatchAlterOrder 新用户注册 resource createVSSResource 删除监测任务 monitor deleteMonitorJob 暂停监测任务 monitor pauseMonitorJob 恢复监测任务 monitor resumeMonitorJob 忽略漏洞 vuln addVulnFalsePositive 取消忽略漏洞 vuln deleteVulnFalsePositive 生成网站扫描报告 report generateWebScanReport 下载网站扫描报告 report downloadWebScanReport 主机 添加主机 host addHost 删除主机 host deleteHost 编辑主机 host editHost 更换分组 host changeHostGroup 新增主机组 host addHostGroup 编辑主机组 host editHostGroup 删除主机组 host deleteHostGroup 创建主机扫描任务 scan createHostScanTask 取消主机扫描任务 scan cancelHostScanTask 添加跳板机 jumper saveJumperServer 编辑跳板机 jumper editJumperServer 删除跳板机 jumper deleteJumperServer 添加smb授权 credential saveSmbCredential 编辑smb授权 credential editSmbCredential 删除smb授权 credential deleteSmbCredential 添加ssh授权 credential saveSshCredential 编辑ssh授权 credential editSshCredential 删除ssh授权 credential deleteSshCredential 添加租户委托 tenant addTenantAgency 删除租户委托 tenant deleteTenantAgency 清空资源 cleanup resourcesCleanUp 忽略漏洞 vuln addVulnFalsePositive 取消忽略漏洞 vuln deleteVulnFalsePositive 生成主机扫描报告 report generateHostScanReport 下载主机扫描报告 report downloadHostScanReport 父主题: 云审计服务支持的关键操作
  • 修改内网域名 在使用内网域名的过程中,如果发现内网域名的配置信息不符合您的业务需求,可以重新设置域名的管理员邮箱地址、域名的描述信息。 更多关于域名管理员邮箱的信息,请参见SOA记录中的Email格式为什么变化了?。 进入内网域名列表页面。 选择待修改的内网域名,单击“操作”列下的“修改”。 进入“修改内网域名”页面。 图1 修改内网域名 根据实际需要,修改“邮箱”或“描述”信息。 单击“确定”,保存修改后的内网域名。
  • 常见域名检测问题及解决措施 表1 常见域名检测问题及解决措施 类型 检测项 错误信息提示 解决措施 域名信息 域名服务商 - - 域名到期时间 域名到期 请联系您的域名服务商进行续费。 在华为云域名服务商注册的域名续费详细请参考域名续费。 域名状态 clientHold或serverHold 解析不生效,请联系您的域名服务商处理。 DNS服务商 是否是华为云DNS服务器地址。 非华为云DNS服务器地址,请联系当前使用DNS服务商处理。 如果需要使用华为云DNS解析,请参考怎样查看并修改域名的DNS服务器地址?。 DNS解析 公共DNS解析结果 未检测到域名的DNS记录。 华为云DNS服务商,请登录DNS控制台创建域名并添加解析记录,详细请参见网站解析。 非华为云DNS服务商,请联系当前使用DNS服务商处理。 域名TTL生效时间 - - 网站信息 网站备案 网站未备案 网站未备案,访问将会被阻断,详细请参考网站备案。 80端口检查 端口不通 请联系您公司的运维人员检查网站配置。 443端口检查 端口不通 HTTP状态码 端口不通,请检查服务器设置 Ping检查 失败 华为云DNS服务商,请登录DNS控制台创建域名并添加解析记录。详细请参见网站解析。 非华为云DNS服务商,请联系当前使用的DNS服务商处理。 工信部黑名单 在黑名单 请联系您的域名服务商解决。
  • 操作步骤 进入公网域名列表页面。 在域名列表中,勾选待批量删除记录集的域名,并在域名列表上方的“批量操作”下拉框中,选择“批量删除记录集”。 图1 批量删除记录集 在“批量操作”页面的“批量删除记录集”页签,如表1所示设置参数。 域名:待批量删除记录集的主域名。 步骤4中已经勾选了待批量删除记录集的域名,此处无需设置。 如果您直接单击“批量删除记录集”,未勾选待批量删除记录集的域名,则需要填写域名的主域名, 多个域名 以换行符分隔。 删除类型:默认勾选“删除满足下方任意一个条件的记录”。 当前仅支持“主机记录等于”,即批量删除指定“主机记录”的记录集。 图2 设置批量删除参数 单击“提交”,完成批量删除记录集操作。 您可以在每个域名对应的记录集列表中查看对应的记录集是否删除成功。 图3 为exampletest1.com添加的记录集 图4 为exampletest2.com添加的记录集 图5 为exampletest3.com添加的记录集 操作完成后,您可以在“批量操作记录”页面查看操作名称、操作结果、操作时间和状态信息,还可以下载失败操作记录。
  • 操作场景 云解析服务支持对记录集的批量操作,可以为多个域名批量删除指定“主机记录”的所有记录集。 如果批量删除单个域名的记录集,还可以参考批量删除记录集(单个域名)操作。 删除记录集后,不可恢复,请谨慎操作。 本节以表1所示内容为例,介绍批量删除记录集的操作。 表1 批量删除记录集示例 域名 主机记录 说明 exampletest1.com - 批量删除域名exampletest1.com的所有记录集。 123 批量删除域名123.exampletest1.com的所有记录集。 exampletest2.com - 批量删除域名exampletest2.com的所有记录集。 123 批量删除域名123.exampletest2.com的所有记录集。 exampletest3.com - 批量删除域名exampletest3.com的所有记录集。 123 批量删除域名123.exampletest3.com的所有记录集。
  • 接入流程 流程说明如下: 申请入驻云商店,成为商家。 云商店运营人员审核公司的资质信息。 准备生产接口服务器,根据本接入指南开发生产接口。 在卖家中心调试生产接口。 加入联营计划,成为联营商家。 在卖家中心申请发布商品。 云商店运营人员审批通过后,产品发布为联营商品。 在卖家中心自助管理生产接口通知消息。 联营License类商品接入可参考《License类商品接入视频指导(旧版)》。 父主题: 联营License类商品接入指南(旧版)
  • 接入流程 License类商品接入云商店的流程如下图所示: 流程说明如下: 申请入驻云商店,成为服务商(即云商店商家)。 云商店运营人员审核公司的资质信息。 准备生产接口服务器,根据本接入指南开发生产接口。 在卖家中心申请测试授权码。 通过测试授权码,调测云商店授权码开放接口。 在卖家中心调试商家接入生产接口。 在卖家中心申请发布License商品,模版选择License授权码。 云商店运营人员审批通过后商品发布成功。 在卖家中心自助管理生产接口通知消息。 联营License类商品接入可参考《License类商品接入视频指导(2.0)》。 父主题: 联营License类商品接入指南(2.0版本)
  • 编解码插件的输入/输出格式样例 假定某款水表支持的服务定义如下: 服务类型 属性名称 属性说明 属性类型(数据类型) Battery - - - - batteryLevel 电量(0--100)% int Meter - - - - signalStrength 信号强度 int - currentReading 当前读数 int - dailyActivityTime 日激活通讯时长 string 那么数据上报时decode接口的输出: { "identifier": "12345678", "msgType": "deviceReq", "data": [ { "serviceId": "Meter", "serviceData": { "currentReading": "46.3", "signalStrength": 16, "dailyActivityTime": 5706 }, "eventTime": "20160503T121540Z" }, { "serviceId": "Battery", "serviceData": { "batteryLevel": 10 }, "eventTime": "20160503T121540Z" } ] } 收到数据上报后,平台对设备的应答响应,调用encode接口编码,输入为 { "identifier": "123", "msgType": "cloudRsp", "request":[ 1, 2 ], "errcode": 0, "hasMore": 0 } 假定某款水表支持的命令定义如下: 基本功能名称 分类 名称 命令参数 数据类型 枚举值 WaterMeter 水表 - - - - - CMD SET_TEMPERATURE_READ_PERIOD - - - - - - value int - - RSP SET_TEMPERATURE_READ_PERIOD_RSP - - - - - - result int 0表示成功,1表示输入非法,2表示执行失败 那么命令下发调用encode接口时,输入为 { "identifier": "12345678", "msgType": "cloudReq", "serviceId": "WaterMeter", "cmd": "SET_TEMPERATURE_READ_PERIOD", "paras": { "value": 4 }, "hasMore": 0 } 收到设备的命令应答后,调用decode接口解码,解码的输出 { "identifier": "123", "msgType": "deviceRsp", "errcode": 0, "body": { "result": 0 } }
  • 编解码插件打包 插件编程完成后,需要使用Maven打包成jar包,并制作成插件包。 Maven打包 打开DOS窗口,进入“pom.xml”所在的目录。 输入maven打包命令:mvn package。 DOS窗口中显示“BUILD SUC CES S”后,打开与“pom.xml”目录同级的target文件夹,获取打包好的jar包。 jar包命名规范为:设备类型-厂商ID-版本.jar,例如:WaterMeter-Huawei-version.jar。 com目录存放的是class文件。 META-INF下存放的是OSGI框架下的jar的描述文件(根据pom.xml配置生成的)。 OSGI-INF下存放的是服务配置文件,把编解码注册为服务,供平台调用(只能有一个xml文件)。 其他jar是编解码引用到的jar包。 制作插件包 新建文件夹命名为“package”,包含一个“preload/”子文件夹。 将打包好的jar包放到“preload/”文件夹。 在“package”文件夹中,新建“package-info.json”文件。该文件的字段说明和模板如下: 注:“package-info.json”需要以UTF-8无BOM格式编码。仅支持英文字符。 表4 “package-info.json”字段说明 字段名 字段描述 是否必填 specVersion 描述文件版本号,填写固定值:"1.0"。 是 fileName 软件包文件名,填写固定值:"codec-demo" 是 version 软件包版本号。描述package.zip的版本,请与下面的bundleVersion取值保持一致。 是 deviceType 设备类型,与产品模型文件中的定义保持一致。 是 manufacturerName 制造商名称,与产品模型文件中的定义保持一致,否则无法上传到平台。 是 platform 平台类型,本插件包运行的 物联网平台 的操作系统,填写固定值:"linux"。 是 packageType 软件包类型,该字段用来描述本插件最终部署的平台模块,填写固定值:"CIGPlugin"。 是 date 出包时间,格式为:"yyyy-MM-dd HH-mm-ss",如"2017-05-06 20:48:59"。 否 description 对软件包的自定义描述。 否 ignoreList 忽略列表,默认为空值。 是 bundles 一组bundle的描述信息。 注:bundle就是压缩包中的jar包,只需要写一个bundle。 是 表5 bundles的字段说明 字段名 字段描述 是否必填 bundleName 插件名称,和上文中pom.xml的Bundle-SymbolicName保持一致。 是 bundleVersion 插件版本,与上面的version取值保持一致。 是 priority 插件优先级,可赋值默认值:5。 是 fileName 插件jar的文件名称。 是 bundleDesc 插件描述,用来介绍bundle功能。 是 versionDesc 插件版本描述,用来介绍版本更迭时的功能特性。 是 package-info.json文件模板: { "specVersion":"1.0", "fileName":"codec-demo", "version":"1.0.0", "deviceType":"WaterMeter", "manufacturerName":"Huawei", "description":"codec", "platform":"linux", "packageType":"CIGPlugin", "date":"2017-02-06 12:16:59", "ignoreList":[], "bundles":[ { "bundleName": "WaterMeter-Huawei", "bundleVersion": "1.0.0", "priority":5, "fileName": "WaterMeter-Huawei-1.0.0.jar", "bundleDesc":"", "versionDesc":"" }] } 选中“package”文件夹中的全部文件,打包成zip格式(“package.zip”)。 注:“package.zip”中不能包含“package”这层目录。 离线插件包最大限制为4M,如果超出4M,请自行分析并将包大小裁剪到4M以内。
  • encode接口说明 encode接口的入参是JSON格式的数据,是平台下发的命令或应答。 平台的下行报文可以分为两种情况: 平台对设备上报数据的应答(对应图中的消息②) 表2 平台收到设备的上报数据后对设备的应答encode接口的入参结构定义 字段名 类型 参数描述 是否必填 identifier String 设备在应用协议里的标识,物联网平台通过decode接口解析码流时获取该参数,通过encode接口编码时将该参数放入码流。 否 msgType String 固定值"cloudRsp",表示平台收到设备的数据后对设备的应答。 是 request byte[] 设备上报的数据。 是 errcode int 请求处理的结果码,物联网平台根据该参数判断命令下发的状态。 0表示成功,1表示失败。 是 hasMore int 表示平台是否还有后续消息下发,0表示没有,1表示有。 后续消息是指,平台还有待下发的消息,以hasMore字段告知设备不要休眠。hasMore字段仅在PSM模式下生效,且需要“下行消息指示”开启。 是 LwM2M协议的命令下发格式和MQTT协议的命令下发格式不一样 注:在cloudRsp场景下编解码插件检测工具显示返回null时,表示插件未定义上报数据的应答,设备侧不需要物联网平台给予响应。 示例: { "identifier": "123", "msgType": "cloudRsp", "request": [ 1, 2 ], "errcode": 0, "hasMore": 0 } 平台命令下发(对应图中的消息③) 表3 平台下发命令encode接口的入参结构定义 字段名 类型 参数描述 是否必填 identifier String 设备在应用协议里的标识,物联网平台通过decode接口解析码流时获取该参数,通过encode接口编码时将该参数放入码流。 否 msgType String 固定值"cloudReq",表示平台下发的请求。 是 serviceId String 服务的id。 是 cmd String 服务的命令名,参见产品模型的服务命令定义。 是 paras ObjectNode 命令的参数,具体字段由产品模型定义。 是 hasMore Int 表示平台是否还有后续命令下发,0表示没有,1表示有。 后续命令是指,平台还有待下发的消息,以hasMore字段告知设备不要休眠。hasMore字段仅在PSM模式下生效,且需要“下行消息指示”开启。 是 mid Int 2字节无符号的命令id,由物联网平台内部分配(范围1-65535)。 物联网平台在通过encode接口下发命令时,把物联网平台分配的mid放入码流,和命令一起下发给设备;设备在上报命令执行结果(deviceRsp)时,再将此mid返回物联网平台。否则物联网平台无法将下发命令和命令执行结果(deviceRsp)进行关联,也就无法根据命令执行结果(deviceRsp)更新命令下发的状态(成功或失败)。 是 示例: { "identifier": "123", "msgType": "cloudReq", "serviceId": "NBWaterMeterCommon", "mid": 2016, "cmd": "SET_TEMPERATURE_READ_PERIOD", "paras": { "value": 4 }, "hasMore": 0} }
  • decode接口说明 decode接口的入参binaryData为设备发过来的CoAP报文的payload部分。 设备的上行报文有两种情况需要插件处理(消息④是模组回复的协议ACK,无需插件处理): 设备上报数据(对应图中的消息①) 字段名 类型 是否必填 参数描述 identifier String 否 设备在应用协议里的标识,物联网平台通过decode接口解析码流时获取该参数,通过encode接口编码时将该参数放入码流。 msgType String 是 固定值"deviceReq",表示设备上报数据。 hasMore Int 否 表示设备是否还有后续数据上报,0表示没有,1表示有。 后续数据是指,设备上报的某条数据可能分成多次上报,在本次上报数据后,物联网平台以hasMore字段判定后续是否还有消息。hasMore字段仅在PSM模式下生效,当上报数据的hasMore字段为1时,物联网平台暂时不下发缓存命令,直到收到hasMore字段为0的上报数据,才下发缓存命令。如上报数据不携带hasMore字段,则物联网平台按照hasMore字段为0处理。 data ArrayNode 是 设备上报数据的内容。 表1 ArrayNode定义 字段名 类型 是否必填 参数描述 serviceId String 是 服务的id。 serviceData ObjectNode 是 一个服务的数据,具体字段在产品模型里定义。 eventTime String 否 设备采集数据时间(格式:yyyyMMddTHHmmssZ)。 如:20161219T114920Z。 示例: { "identifier": "123", "msgType": "deviceReq", "hasMore": 0, "data": [{ "serviceId": "NBWaterMeterCommon", "serviceData": { "meterId": "xxxx", "dailyActivityTime": 120, "flow": "565656", "cellId": "5656", "signalStrength": "99", "batteryVoltage": "3.5" }, "eventTime": "20160503T121540Z" }, { "serviceId": "waterMeter", "serviceData": { "internalTemperature": 256 }, "eventTime": "20160503T121540Z" }] } LwM2M协议的数据格式跟MQTT的数据格式不同。 设备对平台命令的应答(对应图中的消息⑤) 字段名 类型 参数描述 是否必填 identifier String 设备在应用协议里的标识,物联网平台通过decode接口解析码流时获取该参数,通过encode接口编码时将该参数放入码流。 否 msgType String 固定值"deviceRsp",表示设备的应答消息。 是 mid Int 2字节无符号的命令id。在设备需要返回命令执行结果(deviceRsp)时,用于将命令执行结果(deviceRsp)与对应的命令进行关联。 物联网平台在通过encode接口下发命令时,把物联网平台分配的mid放入码流,和命令一起下发给设备;设备在上报命令执行结果(deviceRsp)时,再将此mid返回给物联网平台。否则物联网平台无法将下发命令和命令执行结果(deviceRsp)进行关联,也就无法根据命令执行结果(deviceRsp)更新命令下发的状态(成功或失败)。 是 errcode Int 请求处理的结果码,物联网平台根据该参数判断命令下发的状态。 0表示成功,1表示失败。 是 body ObjectNode 命令的应答,具体字段由产品模型定义。 注:body体不是数组。 否 示例: { "identifier": "123", "msgType": "deviceRsp", "mid": 2016, "errcode": 0, "body": { "result": 0 } }
  • 如何开发网关 网关是一个特殊的设备,除具备一般设备功能之外,还具有子设备管理、子设备消息转发的功能。SDK提供了AbstractGateway抽象类来简化网关的实现。该类提供了子设备管理功能,需要从平台获取子设备信息并保存(需要子类提供子设备持久化接口)、子设备下行消息转发功能(需要子类实现转发处理接口)、以及上报子设备列表、上报子设备属性、上报子设备状态、上报子设备消息等接口。 使用AbstractGateway类 继承该类,在构造函数里提供子设备信息持久化接口,实现其下行消息转发的抽象接口: 1 2 3 4 5 6 7 public abstract void onSubdevCommand(String requestId, Command command); public abstract void onSubdevPropertiesSet(String requestId, PropsSet propsSet); public abstract void onSubdevPropertiesGet(String requestId, PropsGet propsGet); public abstract void onSubdevMessage(DeviceMessage message); iot-gateway-demo代码介绍 工程iot-gateway-demo基于AbstractGateway实现了一个简单的网关,它提供tcp设备接入能力。关键类: SimpleGateway:继承自AbstractGateway,实现子设备管理和下行消息转发 StringTcpServer:基于netty实现一个TCP server,本例中子设备采用TCP协议,并且首条消息为鉴权消息 SubDevicesFilePersistence:子设备信息持久化,采用json文件来保存子设备信息,并在内存中做了缓存 Session:设备会话类,保存了设备id和TCP的channel的对应关系 SimpleGateway类 添加或删除子设备处理 添加子设备:AbstractGateway的onAddSubDevices接口已经完成了子设备信息的保存。我们不需要再增加额外处理,因此SimpleGateway不需要重写onAddSubDevices接口 删除子设备:我们不仅需要修改持久化信息,还需要断开当前子设备的连接。所以我们重写了onDeleteSubDevices接口,增加了拆链处理,然后调用父类的onDeleteSubDevices。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public int onDeleteSubDevices(SubDevicesInfo subDevicesInfo) { for (DeviceInfo subdevice : subDevicesInfo.getDevices()) { Session session = nodeIdToSesseionMap.get(subdevice.getNodeId()); if (session != null) { if (session.getChannel() != null) { session.getChannel().close(); channelIdToSessionMap.remove(session.getChannel().id().asLongText()); nodeIdToSesseionMap.remove(session.getNodeId()); } } } return super.onDeleteSubDevices(subDevicesInfo); } 下行消息处理 网关收到平台下行消息时,需要转发给子设备。平台下行消息分为三种:设备消息、属性读写、命令。 设备消息:这里我们需要根据deviceId获取nodeId,从而获取session,从session里获取channel,就可以往channel发送消息。在转发消息时,可以根据需要进行一定的转换处理。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Override public void onSubdevMessage(DeviceMessage message) { //平台接口带的都是deviceId,deviceId是由nodeId和productId拼装生成的,即 //deviceId = productId_nodeId String nodeId = IotUtil.getNodeIdFromDeviceId(message.getDeviceId()); if (nodeId == null) { return; } //通过nodeId获取session,进一步获取channel Session session = nodeIdToSesseionMap.get(nodeId); if (session == null) { log.error("subdev is not connected " + nodeId); return; } if (session.getChannel() == null){ log.error("channel is null " + nodeId); return; } //直接把消息转发给子设备 session.getChannel().writeAndFlush(message.getContent()); log.info("writeAndFlush " + message); } 属性读写: 属性读写包括属性设置和属性查询。 属性设置: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public void onSubdevPropertiesSet(String requestId, PropsSet propsSet) { if (propsSet.getDeviceId() == null) { return; } String nodeId = IotUtil.getNodeIdFromDeviceId(propsSet.getDeviceId()); if (nodeId == null) { return; } Session session = nodeIdToSesseionMap.get(nodeId); if (session == null) { return; } //这里我们直接把对象转成string发给子设备,实际场景中可能需要进行一定的编解码转换 session.getChannel().writeAndFlush(JsonUtil.convertObject2String(propsSet)); //为了简化处理,我们在这里直接回响应。更合理做法是在子设备处理完后再回响应 getClient().respondPropsSet(requestId, IotResult.SUCCESS); log.info("writeAndFlush " + propsSet); } 属性查询: 1 2 3 4 5 6 7 @Override public void onSubdevPropertiesGet(String requestId, PropsGet propsGet) { //不建议平台直接读子设备的属性,这里直接返回失败 log.error("not supporte onSubdevPropertiesGet"); deviceClient.respondPropsSet(requestId, IotResult.FAIL); } 命令:处理流程和消息类似,实际场景中可能需要不同的编解码转换。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public void onSubdevCommand(String requestId, Command command) { if (command.getDeviceId() == null) { return; } String nodeId = IotUtil.getNodeIdFromDeviceId(command.getDeviceId()); if (nodeId == null) { return; } Session session = nodeIdToSesseionMap.get(nodeId); if (session == null) { return; } //这里我们直接把command对象转成string发给子设备,实际场景中可能需要进行一定的编解码转换 session.getChannel().writeAndFlush(JsonUtil.convertObject2String(command)); //为了简化处理,我们在这里直接回命令响应。更合理做法是在子设备处理完后再回响应 getClient().respondCommand(requestId, new CommandRsp(0)); log.info("writeAndFlush " + command); } 上行消息处理 上行处理在StringTcpServer的channelRead0接口里。如果会话不存在,需要先创建会话: 如果子设备信息不存在,这里会创建会话失败,直接拒绝连接 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { Channel incoming = ctx.channel(); log.info("channelRead0" + incoming.remoteAddress() + " msg :" + s); //如果是首条消息,创建session //如果是首条消息,创建session Session session = simpleGateway.getSessionByChannel(incoming.id().asLongText()); if (session == null) { String nodeId = s; session = simpleGateway.createSession(nodeId, incoming); //创建会话失败,拒绝连接 if (session == null) { log.info("close channel"); ctx.close(); } } 如果会话存在,则进行消息转发: 1 2 3 4 5 6 7 else { //如果需要上报属性则调用reportSubDeviceProperties DeviceMessage deviceMessage = new DeviceMessage(s); deviceMessage.setDeviceId(session.getDeviceId()); simpleGateway.reportSubDeviceMessage(deviceMessage, null); } 到这里,网关的关键代码介绍完了,其他的部分看源代码。整个demo是开源的,用户可以根据需要进行扩展。比如修改持久化方式、转发中增加消息格式的转换、实现其他子设备接入协议。 iot-gateway-demo的使用 在平台上为网关注册开户。 修改StringTcpServer的main函数,替换构造参数,然后运行该类。 1 2 3 simpleGateway = new SimpleGateway(new SubDevicesFilePersistence(), "ssl://iot-acc.cn-north-4.myhuaweicloud.com:8883", "5e06bfee334dd4f33759f5b3_demo", "mysecret"); 在平台上看到该网关在线后,添加子设备。 此时网关上日志打印: 2023-01-05 19:14:32 INFO SubDevicesFilePersistence:83 - add subdev: 456gefw3fh 运行TcpDevice类,建立连接后,输入子设备的nodeId。 此时网关设备日志打印: 2023-01-05 19:15:13 INFO StringTcpServer:118 - channelRead0/127.0.0.1:60535 msg :subdev2 2023-01-05 19:15:13 INFO SimpleGateway:68 - create new session okSession{nodeId='456gefw3fh', channel=[id: 0x42c9dc24, L:/127.0.0.1:8080 - R:/127.0.0.1:60535], deviceId='5e06bfee334dd4f337589c1de_subdev2'} 在平台上看到子设备上线。 子设备上报消息 查看日志看到上报成功 查看消息跟踪 在平台上找到网关,选择 设备详情-消息跟踪,打开消息跟踪。继续让子设备发送数据,等待片刻后看到消息跟踪:
  • 导入代码样例 下载quickStart(websocket).zip样例,并解压。 修改Demo里的关键工程配置参数。其中MqttOverWebsocketDemo.html需要配置host地址、设备ID和设备密钥,用于启动Demo时连接平台。 host地址:即域名,请参考平台对接信息获取; 设备ID和设备密钥:在物联网平台注册设备或调用创建设备接口后获取。 var host = '****'; //IoT平台mqtt对接地址 var deviceId = '****'; //请填写在平台注册的设备ID var secret = '****'; //请填写在平台注册的设备密钥
  • 接收下发命令 在Demo中提供了接收平台下发命令的功能,在MQTT建链完成并成功订阅Topic后,可以在管理控制台设备详情中命令下发或使用应用侧Demo对该设备ID进行命令下发。下发成功后,在Demo中接收到平台下发给设备的命令。 例如下发参数名为smokeDetector: SILENCE,参数值为1的命令。 图6 命令下发 命令下发成功后,Demo收到平台下发的命令,浏览器调试界面的console栏显示如下: 图7 显示 由于是同步命令需要端侧回复响应可参考接口。
  • 接收下发命令 在Demo中提供了接收平台下发命令的功能,在MQTT建链完成并成功订阅Topic后,可以在管理控制台设备详情中命令下发或使用应用侧Demo对该设备ID进行命令下发。下发成功后,在Demo中接收到平台下发给设备的命令。 例如下发参数名为smokeDetector: SILENCE,参数值为50的命令。 命令下发成功后,Demo收到的消息是50,命令运行主界面显示如下: 由于是同步命令需要端侧回复响应可参考接口。
  • JavaScript编解码插件模板 以下为JavaScript编解码插件的模板,开发者需要按照平台提供的模板,实现对应的接口。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /** * 设备上报数据到物联网平台时调用此接口进行解码, 将设备的原始数据解码为符合产品模型定义的JSON格式数据。 * 该接口名称和入参已经定义好,开发者只需要实现具体接口即可。 * @param byte[] payload 设备上报的原始码流 * @param string topic MQTT设备上报数据时的topic,非MQTT设备上报数据时不携带该参数 * @return string json 符合产品模型定义的JSON格式字符串 */ function decode(payload, topic) { var jsonObj = {}; return JSON.stringify(jsonObj); } /** * 物联网平台下发指令时,调用此接口进行编码, 将产品模型定义的JSON格式数据编码为设备的原始码流。 * 该接口名称和入参格式已经定义好,开发者只需要实现具体接口即可。 * @param string json 符合产品模型定义的JSON格式字符串 * @return byte[] payload 编码后的原始码流 */ function encode(json) { var payload = []; return payload; }
  • 设备发放流程中使用到的证书都有哪些,它们有何区别? 设备发放提供证书认证方式,证书认证为双向认证过程,涉及到设备发放(平台)和设备两端,过程如下图所示。 图1 设备发放流程 双向证书认证过程使用到了如下几类证书: 表1 证书类别 证书 说明 证书及其私钥持有者 签发者 服务端证书 步骤2中,设备发放设备侧将该证书返回设备。 设备发放设备侧持有 权威CA(服务端证书的CA证书)签发 服务端CA证书 步骤3中,客户端使用该服务端CA证书验证服务端证书,通常为权威CA证书,获取方式见MQTT CONNECT连接鉴权。 权威CA机构持有 权威CA机构签发 设备证书(客户端证书) 步骤4中,设备将该证书发送给设备发放设备侧。 设备 CA证书 CA证书(设备CA证书/客户端CA证书) 步骤5中,设备发放设备侧使用该CA证书验证来自设备的客户端证书。用户通过应用侧上传该证书到设备发放平台。 用户 通常为自签发 样例中各类证书常用文件名: 表2 证书常用文件名 证书 文件名 MQTT.fx中的字段名 服务端证书 - - 服务端CA证书 如下其中之一: GlobalSignRSAOVSSLCA2018.bks(android)GlobalSignRSAOVSSLCA2018.crt.pem(c或java)GlobalSignRSAOVSSLCA2018.jks(java) bsca.jks(java) bsrootcert.pem(c) CA File 设备证书(客户端证书) client.crt Client Certificate File 设备证书(客户端证书)私钥 client.key Client Key File CA证书(设备CA证书/客户端CA证书) server.crt - 双向认证,即双向证书认证,与单向认证中不同的是,不仅包含单向认证中的设备对平台的证书验证步骤,还包含了平台对设备的证书验证步骤。
  • 热门问题 设备管理服务和 设备接入服务 合一后的差异点是什么? 如何获取对接物联网平台的地址? 如何获取新域名和老域名接入的地址/证书? 新旧域名接入的鉴权方式有什么区别? 命令/属性下发总是超时? 应用侧如何获取appid和secret? Java SDK中有多个demo,该参考哪一个demo? C版 SDK中有多个demo,该参考哪一个demo? 如果是TCP协议,或者自定义协议如何接入平台? 设备显示上报数据成功,在控制台未看到数据? 应用服务器调用接口失败怎么处理? 应用服务器如何获取设备上报到物联网平台的数据? 如何制作订阅推送调测证书? 调用订阅接口时,回调地址如何获取? 订阅后消息推送失败如何处理? 物联网卡无法正常接入设备接入平台? 为何上报中文数据,平台会乱码呈现?
  • 数据上报失败如何处理? 若设备是使用接口注册的,请确认设备是否因为没在指定的timeout时间内上线而被物联网平台自动删除了。如果设备已被删除,请重新注册设备再尝试上报数据。 请检查使用接口注册设备时,填写的产品信息是否和产品模型一致。 请检查上报的数据名称是否和产品模型定义的服务属性一致。 确定以上都不存在问题时,请检查设备和物联网平台之间的网络链路是否畅通,设备是否正常运行。 若设备为NB-IoT设备,请再参考NB模组无法正常上报数据怎么办?进行排查。
  • 为何上报中文数据,平台会乱码呈现? 问题描述 使用MQTT.fx设备模拟器进行数据上报时,在json字符串中携带中文字符,如下图: 图4 MQTT.fx数据上报截图 上报至IoTDA平台后,会出现乱码情况,如下图: 图5 设备详情样例 可能原因 MQTT.fx设备模拟器不支持中文字符。 解决办法 与平台交互时,不使用中文字符。 请将上报数据中的中文字符进行Unicode编码处理。 替换第三方设备模拟器,推荐使用IoTDA自研的设备模拟器。
  • 命令下发失败如何处理? 问题描述 调用命令下发接口报错或调用接口成功但设备未收到命令。 可能原因 设备协议不支持。 设备订阅的下行topic不正确或者设备上行topic及消息体不正确。 解决方法 确认使用的接口是否支持设备协议(同步命令下发当前仅支持MQTT协议设备)。 同步命令下发操作步骤: 确认设备订阅下行topic是否正确(需订阅成功后才可调用接口,不订阅,设备收不到平台消息),详细请参考平台命令下发接口文档。 若设备有收到平台下发的消息,确认设备上行topic及消息体是否正确,且必须在收到平台消息20s内(消息接口不限定20s),通过上行topic向平台发送,否则接口报错(request_id为下行消息中携带)。 异步命令下发操作步骤: 确认是立即下发还是缓存下发(根据入参的send_strategy判断)。 立即下发:设备在线,调用接口后设备立即收到。 缓存下发:调用接口后,设备上报数据后收到。
  • 物联网平台的命令状态总共有几种? LWM2M/CoAP设备命令下发状态 物联网平台命令下发包含如下状态: 超期:表示命令在物联网平台缓存时间超期,未向设备下发。 成功:表示物联网平台已经将命令下发给设备,且收到设备上报的命令执行结果。 失败:表示编解码插件解析为空,或执行结果响应里面有“ERROR CODE”等。 超时:表示物联网平台等待ACK响应超时。 取消:表示应用侧已经取消命令下发。 等待:表示命令在物联网平台缓存,还未下发给设备。 已发送:表示物联网平台已经将命令下发给设备。 已送达:表示物联网平台已经将命令下发给设备,且收到设备返回的ACK消息。 各命令状态之间的转换如下图所示: 图6 命令状态转换图 MQTT设备下发消息状态 等待:MQTT设备不在线,物联网平台会将消息进行缓存,此时任务状态为“等待”状态。 超时:物联网平台缓存的PENDING状态的消息,如果1天之内还没有下发下去,物联网平台会将消息状态设置为"超时”。 已送达:物联网平台将消息发送给设备后,状态变为“已送达”。 失败:物联网平台发送消息给设备不成功,消息状态变为“失败”。 各消息状态之间的转换如下图所示: 图7 消息状态转换图
  • 物联网平台下发异步命令有重发机制吗? 物联网平台具备异步命令重发机制。异步命令发送后(可以在设备接入的命令详情中查看命令发送时间):如果物联网平台未收到设备返回的ACK,则在10s~15s后会进行第一次重传。如果物联网平台仍未收到设备返回的ACK,在20s~30s后进行第二次重传。如果物联网平台依旧未收到设备返回的ACK,在40s~60s进行第三次重传。如果物联网平台在80s~180s后还没收到设备返回的ACK,则命令状态变为超时。
  • 离线开发的插件通过了工具检测,上传到平台后,设备日志提示异常? 问题描述 离线开发的插件包已经通过了编解码插件检测工具检测后,上传平台后,设备日志提示异常。 图5 设备日志错误提示 可能原因 该异常是运行插件代码抛出的异常,一般是相关依赖没有引入或者代码逻辑有误,可根据日志中的Java异常提示进行处理。 解决方法 在离线插件关键代码处(例如decode函数入口处,出口处等)打印日志,并联系物联网平台支撑人员在后台获取日志,辅助定位问题。 图6 打印日志样例
共100000条