帮你快速理解、总结文档立即下载

转发消息至用户业务系统

最近更新时间:2026-10-27 17:33:32
本文档已由 AI 辅助审校
我的收藏

功能介绍

在物联网业务中,设备通常需要将运行状态、传感器数据及设备事件等信息上报至物联网平台。企业除了通过平台管理设备外,还需要将这些数据同步至自身业务系统,用于设备监控、数据分析、异常告警及业务联动等场景。
腾讯云物联网开发平台提供规则引擎数据转发能力,支持根据设备消息的 Topic 和内容筛选数据,并通过 HTTP/HTTPS 协议,将符合条件的消息转发至用户指定的业务服务。
通过该能力,用户无需主动轮询平台获取设备数据,即可在业务服务中接收设备消息,并根据业务需求完成数据存储、分析和处理。

数据转发机制

设备通过 MQTT 协议向物联网开发平台上报消息,规则引擎根据配置的数据来源和筛选条件识别目标数据,将符合条件的消息通过 HTTP/HTTPS 请求转发至用户业务服务。
数据转发流程如下:


HTTP 转发与鉴权

规则引擎通过 HTTP/HTTPS 与用户业务系统通信。为提高请求安全性,建议开启鉴权 Token,由接收服务验证请求签名。
HTTP 转发包含以下两个主要阶段:
阶段
请求方式
说明
服务地址校验
GET
校验接收服务的可访问性及鉴权签名。
设备消息转发
POST
将符合规则筛选条件的设备消息发送至业务服务。

1. 服务地址校验

保存 HTTP 转发行为时,平台会检查目标 URL 的可连接性。启用规则时,平台会向配置的服务地址发送 GET 请求,验证服务是否能够正常处理回调。
开启 Token 鉴权后,GET 请求包含以下 Header:
参数
说明
Signature
平台生成的 SHA1 签名。
Timestamp
请求时间戳。
Nonce
随机字符串。
Echostr
用于服务地址校验的随机字符串。
业务服务需要根据配置的 Token 完成签名校验:
1. 将 Token、Timestamp、Nonce 作为字符串按字典序排序。
2. 将排序后的三个字符串直接拼接,不添加分隔符。
3. 对拼接结果计算 SHA1,得到签名字符串。
4. 将计算结果与请求中的 Signature 比较。
签名验证成功后,服务需要返回 HTTP 200,并在响应正文中原样返回 Echostr。
例如:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

UPWIAFASvDUFcTEE
平台通过返回的 Echostr 验证服务地址是否有效。
说明:
此处使用的是规则引擎 Forward 的 Signature、Timestamp、Nonce 请求头,与“数据开发 > 自定义推送”使用的 x-tc-signature、x-tc-timestamp、x-tc-nonce 不同,请勿混用。

2. 设备消息转发

当规则已启用,且设备消息满足筛选条件时,平台向目标 HTTP 服务发送 POST 请求。
开启 Token 鉴权时,POST 请求同样携带签名相关 Header,接收服务应在处理数据前完成签名验证。
业务服务成功接收请求后,应返回 HTTP 200,表示本次 HTTP 转发已被服务端确认。
建议接收服务在完成必要的签名校验与接收处理后尽快返回成功响应。对于数据库写入、复杂计算、外部服务调用等耗时操作,可通过内部任务队列异步处理,避免由于响应时间过长导致转发失败。

转发数据格式

规则引擎转发的消息内容与数据来源、消息类型及规则 SQL 有关。
设备通过 Topic 上报 JSON 数据时,第三方服务收到的 HTTP POST Body 格式示例如下:
{
"payload": {
"brightness": 80
},
"timemills": 1764038772492,
"seq": 212934692,
"timestamp": 1764038772,
"topic": "$thing/up/property/PRODUCT_ID/DEVICE_NAME",
"devicename": "DEVICE_NAME",
"productid": "PRODUCT_ID"
}
主要字段说明:
字段
说明
payload
根据规则 SQL 提取的设备消息数据。
timemills
转发行为触发时间,毫秒级时间戳。
seq
消息序列号。
timestamp
消息时间戳。
topic
消息来源 Topic。
devicename
设备名称。
productid
产品 ID。
注意:
实际 payload 内容以设备上报数据和规则 SQL 提取结果为准,不应将其内部结构固定为 params。
对于二进制设备消息,平台会将原始 Payload 进行 Base64 编码后转发。
设备状态变化通知使用不同的数据格式,其中的 Payload 字段同样可能需要进行 Base64 解码。
完整的数据结构可参见 数据转发至第三方服务。
详细的规则引擎限制可参见 规则引擎。

转发失败与重试机制

当 HTTP 转发失败时,规则引擎会执行重试,以提高消息送达的成功率。
处理阶段
处理机制
首次转发
将数据发送至目标 HTTP 服务。
第一次重试
失败后间隔1秒。
第二次重试
再次失败后间隔3秒。
第三次重试
再次失败后间隔10秒。
重试仍失败
未配置错误行为时,丢弃该消息。
已配置错误行为
三次重试失败后,执行一次错误行为转发;仍失败则丢弃。
对于需要提高异常消息处理能力的场景,可以在规则引擎中配置转发错误行为操作。
错误行为需要与主要转发行为使用不同的行为类型。例如,主要行为为第三方 HTTP 服务,错误行为可以选择 CKafka,将异常消息写入消息队列供业务系统后续处理。

快速实践

下面通过一个简单示例,演示如何将设备上报的物模型属性,经过规则引擎转发至 HTTP 业务服务。
本文使用腾讯云云函数 SCF 搭建示例 HTTP 接收服务,并使用 Python 设备模拟器发送测试数据。
实际业务中,也可以使用自建 HTTP 服务或其他符合要求的业务系统。

前提条件

已开通物联网开发平台实例。
已具备产品、设备和规则引擎管理权限。
已开通腾讯云云函数 SCF 服务。
本地已安装 Python 3.9 或更高版本。
已获取本文配套的 Python 设备模拟器示例文件。

步骤一:创建产品与设备

1. 登录 物联网开发平台控制台,选择目标实例。
2. 进入设备管理 > 产品,创建一个基础产品,数据协议选择物模型,认证方式选择密钥认证,具体操作步骤详见 创建产品。
3. 进入物模型配置页面,添加以下属性:
属性名称
标识符
数据类型
范围
设备开关
power_switch
bool
0、1
灯光亮度
brightness
int
0–100
设备温度
temperature
float
-20–100
可直接复制以下内容导入物模型:
{
"version": "1.0",
"profile": {
"ProductId": "YOUR_PRODUCT_ID",
"CategoryId": "YOUR_CATEGORY_ID"
},
"properties": [
{
"id": "power_switch", "name": "设备开关", "mode": "rw", "required": false,
"define": {"type": "bool", "mapping": {"0": "关闭", "1": "开启"}}
},
{
"id": "brightness", "name": "灯光亮度", "mode": "rw", "required": false,
"define": {"type": "int", "unit": "%", "min": "0", "max": "100", "step": "1"}
},
{
"id": "temperature", "name": "设备温度", "mode": "r", "required": false,
"define": {"type": "float", "unit": "℃", "min": "-20", "max": "100", "step": "0.1"}
}
],
"events": [],
"actions": []
}

4. 进入设备管理 > 设备,创建一台设备属于刚创建的产品,具体操作步骤详见 创建设备。
5. 在设备详情中获取 ProductID、DeviceName 和 DeviceSecret,用于后续模拟设备接入,具体操作步骤详见 获取设备三元组。

步骤二:部署 HTTP 接收服务

本文通过 SCF Express Web 函数部署测试服务,用于响应规则引擎地址校验、接收 HTTP POST 消息及输出接收日志。
1. 创建函数
进入 SCF 控制台,创建 Web 函数,选择支持的 Node.js + Express 框架模板。

输入函数名称,确认相关信息后单击完成创建函数。

2. 配置 Token
单击函数名进入函数配置,单击编辑新增环境变量:
Key
Value
IOT_FORWARD_TOKEN
iotDemo2026Secret
保存环境变量并部署最新函数代码。

3. 配置接收代码
3.1 单击函数名进入函数代码页面,找到 src/app.js:


3.2 将 src/app.js 代码替换为以下内容:
'use strict';

const express = require('express');
const crypto = require('crypto');

const app = express();
const TOKEN = process.env.IOT_FORWARD_TOKEN;

app.use(express.json({ limit: '1mb' }));

function verifySignature(req, res, next) {
if (!TOKEN) {
return res.status(500).send('Token not configured');
}

const signature = String(
req.headers.signature || ''
).trim().toLowerCase();

const timestamp = String(req.headers.timestamp || '');
const nonce = String(req.headers.nonce || '');

if (
!timestamp ||
!nonce ||
!/^[0-9a-f]{40}$/.test(signature)
) {
return res.status(403).send('Forbidden');
}

const parts = [TOKEN, timestamp, nonce].sort();
const source = parts.join('');

const expected = crypto
.createHash('sha1')
.update(source, 'utf8')
.digest('hex');

const valid = crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);

if (!valid) {
return res.status(403).send('Forbidden');
}

next();
}

// HTTP 服务地址校验
app.get('/test', verifySignature, (req, res) => {
const echo = req.headers.echostr;

if (typeof echo !== 'string') {
return res.status(400).send('Missing Echostr');
}

console.log('[VERIFY] 地址校验成功');

return res.status(200)
.type('text/plain')
.send(echo);
});

// 设备消息接收
app.post('/test', verifySignature, (req, res) => {
console.log(JSON.stringify({
event: 'IOT_MESSAGE_RECEIVED',
time: new Date().toISOString(),
body: req.body
}));

return res.status(200)
.type('text/plain')
.send('success');
});

// SCF Web 函数监听 9000 端口
app.listen(9000, '0.0.0.0', () => {
console.log('[SCF] HTTP Server started');
});
3.3 复制代码保存后单击部署,等待部署成功。

4. 创建公网函数 URL
4.1 进入函数 URL 页面,配置公网 HTTPS 访问,授权类型选择开放。

4.2 单击新建函数 URL 后,启用公网访问和内网访问。然后单击确定完成创建。

4.3 复制生成的公网 HTTPS 地址,并在末尾添加 /test 路径:
https://<实际 SCF 公网函数 URL>/test
注意区分公网与内网函数 URL。SCF 默认公网函数 URL 仅供测试使用,生产环境请遵循 SCF 的域名使用要求。

5. 验证 HTTP 服务
使用 PowerShell 执行:
curl.exe -i "https://<实际 SCF 公网函数 URL>/test"
在已配置 Token 且服务正常的情况下,由于请求没有携带签名,预期返回:
HTTP/1.1 403 Forbidden
说明请求已到达 HTTP 接收服务,并被鉴权逻辑拦截。

步骤三:配置规则引擎

1. 创建转发规则
进入 物联网开发平台控制台,选择目标实例后找到数据流转 > 规则引擎页面,创建转发规则。

2. 设置数据来源与筛选条件
进入规则详情,在筛选数据区域单击编辑配置信息:
配置项
设置
字段
*
产品
步骤一创建的产品。
设备
测试设备。
消息类型
物模型属性上报。
筛选条件
不设置。
* 表示提取全部数据字段,适合首次验证完整消息转发链路。

3. 添加 HTTP 转发行为
在行为操作区域添加转发行为。
参数
配置
行为类型
数据转发到第三方服务(Forward)。
API 地址
SCF 公网 HTTPS 地址,包含 /test。
增加鉴权 Token
勾选
Token
iotDemo2026Secret
保存行为操作,等待平台完成服务地址检查。

4. 启用规则
完成配置后,启用规则。
平台会向 HTTP 服务发送 GET 请求。接收服务验证签名并返回 Echostr 后,即可处理后续设备消息转发。

步骤四:模拟设备上报数据

完成服务和规则配置后,可通过真实设备或设备模拟程序上报数据。
本示例使用 Python 命令行程序模拟设备,无需准备实际硬件。
1. 运行 Python 程序
安装依赖:
python -m pip install "paho-mqtt>=2.1,<3.0"
保存程序为 device_simulator.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Tencent Cloud IoT Explorer device simulator, MQTT HMAC/PSK auth, port 1883.
WARNING: Port 1883 is unencrypted. Use test-only device credentials.
"""

import base64
import binascii
import getpass
import hashlib
import hmac
import json
import secrets
import string
import threading
import time
from datetime import datetime

try:
import paho.mqtt.client as mqtt
except ImportError as exc:
raise SystemExit('请先运行: python -m pip install "paho-mqtt>=2.1,<3"') from exc

SDK_APP_ID = "12010126"


def log(tag, message):
print(f"\\n[{datetime.now():%H:%M:%S}] {tag:<10} {message}", flush=True)


def required(prompt, secret=False):
while True:
value = (getpass.getpass(prompt) if secret else input(prompt)).strip()
if value:
return value
print("此项不能为空。")


def mqtt_credentials(product_id, device_name, device_secret):
client_id = product_id + device_name
connid = ''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(5))
expiry = int(time.time()) + 24 * 3600
username = f"{client_id};{SDK_APP_ID};{connid};{expiry}"
try:
key = base64.b64decode(device_secret, validate=True)
except (ValueError, binascii.Error) as exc:
raise ValueError("DeviceSecret 不是有效的 Base64 字符串") from exc
if not key:
raise ValueError("DeviceSecret 解码为空")
signed = hmac.new(key, username.encode("utf-8"), hashlib.sha256).hexdigest()
return client_id, username, f"{signed};hmacsha256"


class Simulator:
def __init__(self, product_id, device_name, device_secret, broker):
self.product_id = product_id
self.device_name = device_name
self.device_secret = device_secret
self.broker = broker
self.topic_up = f"$thing/up/property/{product_id}/{device_name}"
self.topic_down = f"$thing/down/property/{product_id}/{device_name}"
self.values = {"power_switch": 0, "brightness": 50, "temperature": 25.0}
self.connected = threading.Event()
self.client = None
self.intentional_disconnect = False

def on_connect(self, client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
self.connected.clear()
log("ERROR", f"MQTT 连接被拒绝: {reason_code}")
return
self.connected.set()
log("CONNECTED", f"已连接到 {self.broker}:1883")
result, mid = client.subscribe(self.topic_down, qos=1)
if result == mqtt.MQTT_ERR_SUCCESS:
log("SUBSCRIBE", self.topic_down)
else:
log("ERROR", f"订阅失败: {result}")

def on_connect_fail(self, client, userdata):
log("ERROR", "MQTT 网络连接失败,请检查地址、端口及网络")

def on_disconnect(self, client, userdata, disconnect_flags, reason_code, properties):
self.connected.clear()
if not self.intentional_disconnect:
log("DISCONNECT", f"异常断开: {reason_code},客户端可能尝试重连")

def on_message(self, client, userdata, message):
text = message.payload.decode("utf-8", errors="replace")
try:
data = json.loads(text)
except json.JSONDecodeError:
log("RECEIVE", f"{message.topic}: {text}")
return

method = data.get("method", "unknown")
if method == "report_reply":
code = data.get("code")
tag = "REPORT_OK" if code == 0 else "REPORT_ERR"
log(tag, f"clientToken={data.get('clientToken')} code={code} status={data.get('status', '')}")
elif method == "control":
# V1仅做上报验证;云端控制不在本例处理范围内。
log("CONTROL", "收到控制请求(V1未实现执行/回复): " + json.dumps(data, ensure_ascii=False))
else:
log("RECEIVE", json.dumps(data, ensure_ascii=False))

def connect(self):
if self.client is not None:
self.disconnect()
client_id, username, password = mqtt_credentials(
self.product_id, self.device_name, self.device_secret
)
client = mqtt.Client(
callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
client_id=client_id,
clean_session=True,
protocol=mqtt.MQTTv311,
reconnect_on_failure=True,
)
client.username_pw_set(username, password)
client.on_connect = self.on_connect
client.on_connect_fail = self.on_connect_fail
client.on_disconnect = self.on_disconnect
client.on_message = self.on_message
self.intentional_disconnect = False
self.client = client
log("CONNECT", f"正在连接 {self.broker}:1883 / {self.device_name} ...")
try:
# MQTT 1883: 仅供临时验收,不使用 TLS;不要输入正式生产设备的密钥。
client.connect(self.broker, 1883, keepalive=60)
client.loop_start()
except Exception as exc:
self.client = None
self.connected.clear()
log("ERROR", f"连接启动失败: {exc}")

def disconnect(self):
client = self.client
if client is None:
log("INFO", "当前未连接")
return
self.intentional_disconnect = True
self.connected.clear()
self.client = None
try:
client.disconnect()
client.loop_stop()
finally:
log("DISCONNECT", "已主动断开连接")

def publish(self, fields):
if not self.connected.is_set() or self.client is None:
log("WARN", "设备尚未连接,按 C 连接后再发送")
return
token = f"demo-{int(time.time() * 1000)}-{secrets.token_hex(2)}"
message = {
"method": "report",
"clientToken": token,
"timestamp": int(time.time() * 1000),
"params": fields,
}
raw = json.dumps(message, ensure_ascii=False, separators=(",", ":"))
info = self.client.publish(self.topic_up, raw, qos=1)
if info.rc != mqtt.MQTT_ERR_SUCCESS:
log("PUBLISH_ERR", f"MQTT 客户端未能排队发送: {info.rc}")
return
log("PUBLISH", f"Topic: {self.topic_up}\\n{json.dumps(message, ensure_ascii=False, indent=2)}")
log("INFO", "消息已交给 MQTT 客户端;请以 report_reply code=0 判断平台处理成功。")

def show_state(self):
log("STATE", "连接状态: " + ("在线" if self.connected.is_set() else "离线"))
log("STATE", json.dumps(self.values, ensure_ascii=False, indent=2))


def print_menu():
print("\\n" + "=" * 50)
print(" Tencent Cloud IoT | 设备模拟器 V1.0")
print(" 1 开启 2 关闭 3 设置亮度 4 设置温度")
print(" 5 上报全部属性 6 查看状态")
print(" C 连接/重连 D 断开连接")
print(" H 帮助 Q 退出")
print("=" * 50)


def ranged_number(label, lower, upper, integer=False):
while True:
try:
raw = input(f"{label} ({lower}~{upper}): ").strip()
value = int(raw) if integer else float(raw)
if not (lower <= value <= upper):
raise ValueError("超出范围")
return value
except ValueError:
print("输入无效,请重新输入有效数字。")


def main():
print_menu()
print("请输入测试设备三元组(本程序不会保存密钥或打印密钥)。")
product_id = required("ProductID: ")
device_name = required("DeviceName: ")
device_secret = required("DeviceSecret (输入隐藏): ", secret=True)
default_broker = f"{product_id}.iotcloud.tencentdevices.com"
broker = input(f"MQTT Broker [{default_broker}],按 Enter 默认: ").strip() or default_broker
if "://" in broker or "/" in broker or ":" in broker:
raise ValueError("Broker 只能填主机名,不应包含协议、路径或端口")
sim = Simulator(product_id, device_name, device_secret, broker)
try:
# 先在本地检查设备密钥是否合法,再开始连接。
mqtt_credentials(product_id, device_name, device_secret)
sim.connect()
while True:
cmd = input("\\nIoT ❯ ").strip().lower()
if cmd == "1":
sim.values["power_switch"] = 1
sim.publish({"power_switch": 1})
elif cmd == "2":
sim.values["power_switch"] = 0
sim.publish({"power_switch": 0})
elif cmd == "3":
value = ranged_number("亮度", 0, 100, integer=True)
sim.values["brightness"] = value
sim.publish({"brightness": value})
elif cmd == "4":
value = round(ranged_number("温度", -20, 100), 1)
sim.values["temperature"] = value
sim.publish({"temperature": value})
elif cmd == "5":
sim.publish(dict(sim.values))
elif cmd == "6":
sim.show_state()
elif cmd == "c":
sim.connect()
elif cmd == "d":
sim.disconnect()
elif cmd in ("h", "help", "?"):
print_menu()
elif cmd in ("q", "quit", "exit"):
break
elif not cmd:
continue
else:
log("WARN", "未知命令,输入 H 查看帮助")
except (KeyboardInterrupt, EOFError):
print("\\n用户中止运行")
finally:
if sim.client is not None:
sim.disconnect()
return 0


if __name__ == "__main__":
try:
raise SystemExit(main())
except ValueError as exc:
raise SystemExit(f"参数错误: {exc}")

运行:
python device_simulator.py
程序将要求输入设备三元组:
ProductID:
DeviceName:
DeviceSecret:
输入后程序将生成 MQTT 连接参数,并连接物联网开发平台。
2. 手动上报数据
连接成功后,使用以下快捷指令发送数据:
指令
功能
1
开启设备。
2
关闭设备。
3
设置亮度并上报。
4
设置温度并上报。
5
上报全部属性。
C
连接或重新连接。
D
断开连接。
Q
退出程序。

步骤五:验证转发结果

查看设备端日志确认转发结果
在 物联网开发平台控制台 进入设备详情,找到云日志 > 行为日志。当日志中出现 RuleEngine 结果为 SUCC 即为转发成功:


常见问题

问题
可能原因
排查建议
规则引擎提示 URL 校验超时或失败。
HTTP 服务未部署、接口路径错误、网络不通或鉴权异常。
检查服务地址、端口、Token 和 GET 响应。
HTTP 服务返回 403。
签名缺失或验证失败。
检查 Token、Timestamp、Nonce 和 Signature。
SCF 返回 Token not configured。
环境变量未配置或未生效。
检查 IOT_FORWARD_TOKEN 并重新部署。
设备上报成功,但未触发规则。
规则未启用或 Topic 不匹配。
检查规则数据来源和设备消息。
转发日志出现 Forward_Third_Not_Responding。
第三方服务器未及时响应。
检查服务网络、响应时长及运行状态。
业务服务收到重复消息。
平台转发重试或异常重复投递。
在业务侧增加幂等处理。
二进制数据无法直接解析。
Payload 采用 Base64 编码。
先进行 Base64 解码,再解析内容。