写在前面:文章内容为本人的学习过程,路过大神不喜勿喷!
一、准备条件
- KEPServerEX 6 : 连接设备侧软件
- IoTDB:数据存储侧软件
- EMQX 或 Mosquitto:中间转发MQTT消息的服务端
- Java8 x64 和 x86:64位为运行环境,x86为KEP6的MQTT客户端运行环境
- MQTTX:客户端连接工具
- WorkBench:数据库操作工具
二、安装KEPServerEX6
- 大家自行网络遨游寻找KEPServerEX 6 下载,下载安装以后还需要进行破解(破解版仅学习使用吧,如果大家商用请支持正版),由于需要使用KEPServerEX的Iot GetWay组件,如果不破解有可能会提示到期时间的警告。
- 当安装好KEPServerEX 6后需要配置下32位的jre环境
- 在开始菜单中启动
- 在桌面右下角,右键KEPServerEX选择设置,配置32位jre的根目录位置。
至此,KEPServerEX6基础已经安装好。
三、安装IoTDB
- IoTDB为Apache的开源项目,所以直接去官网或者Git上下载即可
选择Http下载压缩包,解压缩到本地即可使用。
四、整体数据流转方式
- 个人认为,根据实际数据量、设备数量、点位数量选择不同的数据流转方式吧。
IoTDB的官方示例:1000设备,每个设备100 测点,共 100000 序列,INT32类型。采样频率1Hz(每秒一次),存储1年,3副本。
完整计算公式:1000设备 * 100测点 * 12字节每数据点 * 86400秒每天 * 365天每年 * 3副本/10压缩比=11T
简版计算公式:1000 * 100 * 12 * 86400 * 365 * 3 / 10 = 11T
- 方式二对比方式一来看,多了一个独立的MQTT服务端,以至于更好的可以对外提供服务,但是需要多布置一台MQTT服务端,增加维护和部署的难度。
- 此处,我使用第一种方式进行学习和试验。
五、配置KEPServer点位
此处,遵循一下IoTDB的存储形式,来增加KEPServer的设备点位。以下是官网的一段描述:
- 我们以风电场物联网场景为例,说明如何在 IoTDB 中创建一个正确的数据模型。
- 根据企业组织结构和设备实体层次结构,我们将其物联网数据模型表示为如下图所示的属性层级组织结构,即电力集团层-风电场层-实体层-物理量层。其中 ROOT 为根节点,物理量层的每一个节点为叶子节点。IoTDB 采用树形结构定义数据模式,以从 ROOT 节点到叶子节点的路径来命名一个时间序列,层次间以“.”连接。例如,下图最左侧路径对应的时间序列名称为ROOT.ln.wf01.wt01.status。
- 在KEPServer中添加测试点位
- 右键连接性新建通道 —> 右键通道新建设备 —> 右键设备新建分组 —> 右键分组新建标记
- 使用KEPServer的IoT Getway组件,右键IoT Getway选择New Agent。
- 配置MQTT的URL和Topic,其他选项根据需要修改吧。
- 根据MQTT服务端的需要,配置ClientID、用户名和密码。注意要确保ClientID不在整个流程中重复。用户名和密码如果MQTT服务端需要身份验证,则需要填写,如何没有配置身份验证空着即可。
- 选择完成后,双击打开编辑器,选择Message,选择Message Fomat类型为Advanced Template,修改Template格式。此处要考虑到后面IoTDb的一些格式化问题。MQTT的消息有效载荷可以由 Java SPI 加载的PayloadFormatter格式化为事件,默认实现为JSONPayloadFormatter,此处格式与后面自定义格式化的Java类所处理的格式对应,根据实际情况修改。为方便处理,我将默认格式修改成了一下格式:
[
|#each VALUES|
{"device": "|TAGNAME|", "values": |VALUE|, "timestamp": |TIMESTAMP| } |#unless @last|,|/unless|
|/each|
]
六、配置IoTDB
- 修改${IOTDB_HOME}/conf/iotdb-datanode.properties , 根据需要修改IP和Port即可,其他就默认。
####################
### Data Node RPC Configuration
####################
# Used for connection of IoTDB native clients(Session)
# Could set 127.0.0.1(for local test) or ipv4 address
# Datatype: String
dn_rpc_address=192.168.20.131
# Used for connection of IoTDB native clients(Session)
# Bind with dn_rpc_address
# Datatype: int
dn_rpc_port=8007
# Used for communication inside cluster.
# could set 127.0.0.1(for local test) or ipv4 address.
# Datatype: String
dn_internal_address=192.168.20.131
# Used for communication inside cluster.
# Bind with dn_internal_address
# Datatype: int
dn_internal_port=10730
# Port for data exchange among DataNodes inside cluster
# Bind with dn_internal_address
# Datatype: int
dn_mpp_data_exchange_port=10740
# port for consensus's communication for schema region inside cluster.
# Bind with dn_internal_address
# Datatype: int
dn_schema_region_consensus_port=10750
# port for consensus's communication for data region inside cluster.
# Bind with dn_internal_address
# Datatype: int
dn_data_region_consensus_port=10760
# Datatype: long
# The time of data node waiting for the next retry to join into the cluster.
# dn_join_cluster_retry_interval_ms=5000
####################
### Seed ConfigNode
####################
# dn_seed_config_node points to any running ConfigNode's cn_internal_address:cn_internal_port.
# Note: After this DataNode successfully joins the cluster for the first time, this parameter is no longer used.
# Each node automatically maintains the list of ConfigNodes and traverses connections when restarting.
# Format: address:port e.g. 127.0.0.1:10710
# Datatype: String
dn_seed_config_node=192.168.20.131:10710
- 修改 ${IOTDB_HOME}/conf/iotdb-common.properties , 此处需要打开IoTDB自带的MQTT服务器端,配置基础的IP和端口,注意1883是MQTT服务器的默认端口,如果开启2个或者多个MQTT服务,需要修改成不同端口监听,否则启动报错。
####################
### Config Node RPC Configuration
####################
# Used for RPC communication inside cluster.
# Could set 127.0.0.1(for local test) or ipv4 address.
# Datatype: String
cn_internal_address=192.168.20.131
# Used for RPC communication inside cluster.
# Datatype: int
cn_internal_port=10710
# Used for consensus communication among ConfigNodes inside cluster.
# Datatype: int
cn_consensus_port=10720
####################
### Seed ConfigNode
####################
# For the first ConfigNode to start, cn_seed_config_node points to its own cn_internal_address:cn_internal_port.
# For other ConfigNodes that to join the cluster, cn_seed_config_node points to any running ConfigNode's cn_internal_address:cn_internal_port.
# Note: After this ConfigNode successfully joins the cluster for the first time, this parameter is no longer used.
# Each node automatically maintains the list of ConfigNodes and traverses connections when restarting.
# Format: address:port e.g. 127.0.0.1:10710
# Datatype: String
cn_seed_config_node=192.168.20.131:10710
- mqtt_payload_formatter为自定义Json格式化类,默认是json,但是有可能数据并不满足IoTDB的存储要求。以下是
IoTDB官方提供的 MQTT 消息有效负载:
{
"device":"root.sg.d1",
"timestamp":1586076045524,
"measurements":["s1","s2"],
"values":[0.530635,0.530635]
}
或者
{
"device":"root.sg.d1",
"timestamps":[1586076045524,1586076065526],
"measurements":["s1","s2"],
"values":[[0.530635,0.530635], [0.530655,0.530695]]
}
七、自定义 MQTT 消息格式
事实上可以通过简单编程来实现 MQTT 消息的格式自定义。
可以在源码的 example/mqtt-customize 项目中找到一个简单示例。
- 创建一个 Java 项目,增加如下依赖。
- 打开官网实例直接修改的话,在mvn package的时候有可能会报错(我再次坑了很久),新建项目就没问题。
- 在下载相关依赖的时候会很慢,反正我等待了很长时间,有些依赖阿里源的镜像中没有,至于其他镜像源有没有此处我也不得而知了,所以我使用的是官方源下载的相关依赖,等…。
<dependency>
<groupId>org.apache.iotdb</groupId>
<artifactId>iotdb-server</artifactId>
<version>1.3.0</version>
</dependency>
- 创建一个实现类,实现接口 org.apache.iotdb.db.mqtt.protocol.PayloadFormatter , 此处对应传输过来的格式,是上面KEPServer 中Message配置的Template格式。
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.iotdb.mqtt.server;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.netty.buffer.ByteBuf;
import org.apache.iotdb.db.protocol.mqtt.Message;
import org.apache.iotdb.db.protocol.mqtt.PayloadFormatter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class CustomizedJsonPayloadFormatter implements PayloadFormatter {
@Override
public List<Message> format(ByteBuf payload) {
if (payload == null) {
return Collections.emptyList();
}
String json = payload.toString(StandardCharsets.UTF_8);
JsonElement element = JsonParser.parseString(json);
JsonArray array = element.getAsJsonArray();
Map<String, Message> map = new ConcurrentHashMap<>();
for (int i = 0; i < array.size(); i++) {
JsonObject object = array.get(i).getAsJsonObject();
String device = object.get("device").getAsString();
Long timestamp = object.get("timestamp").getAsLong();
String value = object.get("values").getAsString();
String[] deviceArr = device.split("\\.");
String[] deviceHead = Arrays.copyOf(deviceArr, deviceArr.length - 1);
String deviceName = String.join(".", deviceHead);
String deviceSub = deviceArr[deviceArr.length - 1];
Message message = map.getOrDefault(deviceName, new Message());
if (message.getDevice() != null) {
List<String> measurements = message.getMeasurements();
measurements.add(deviceSub);
message.setMeasurements(measurements);
List<String> values = message.getValues();
values.add(value);
message.setValues(values);
} else {
message.setDevice(deviceName);
message.setTimestamp(timestamp);
message.setMeasurements(new ArrayList<>(Collections.singletonList(deviceSub)));
message.setValues(new ArrayList<>(Collections.singletonList(value)));
map.put(deviceName, message);
}
}
return new ArrayList<>(map.values());
}
@Override
public String getName() {
// set the value of mqtt_payload_formatter in iotdb-common.properties as the following string:
return "CustomizedJson";
}
}
- 修改项目中的 src/main/resources/META-INF/services/org.apache.iotdb.db.protocol.mqtt.PayloadFormatter文件: 将示例中的文件内容清除,并将刚才的实现类的全名(包名.类名)写入文件中。注意,这个文件中只有一行。在本例中,文件内容为 : org.apache.iotdb.mqtt.server.CustomizedJsonPayloadFormatter
- 编译项目生成一个 jar 包: mvn package -DskipTests
在 IoTDB 服务端:
- 找到 ${IOTDB_HOME}/ext/mqtt/ 文件夹, 将刚才的 jar 包放入此文件夹。
- 打开 MQTT 服务参数. (enable_mqtt_service=true in conf/iotdb-common.properties)
- 用刚才的实现类中的 getName() 方法的返回值 设置为 conf/iotdb-common.properties 中
mqtt_payload_formatter 的值,在本例中,为 CustomizedJson - 启动 IoTDB
至此,整个需要配置的部分基本完成,接下来就可以启动 KEPServer 中 IoT Getway服务,在控制台出现以下内容,说明连接上了IoTDB的MQTT服务端,并且开始持续发送点位数据。
八、其他组件
- 可以安装一个MQTTX客户端订阅和发布消息,用来调试很方便。这里的配置连接很简单,网上也有很多教程,在此就不再过多赘述。
- 可以在安装一个WorkBench(开源版和企业版功能上会有差距,大家按需要下载),用来操作IoTDB,类似与Navicat等操作数据库的工具。启动后台,使用浏览器访问,配置上IoTDB即可看到所有点位信息。
最后的最后:希望文章可以帮到大家,我们共同学习,加油吧!!!!