1、简介
TDengine 是一款开源、云原生的时序数据库,专为物联网、工业互联网、金融、IT 运维监控等场景设计并优化。它能让大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据得到高效实时的处理,对业务的运行状态进行实时的监测、预警,从大数据中挖掘出商业价值。
TDengine 社区版是一开源版本,采用的是 AGPL 许可证,它具备高效处理时序数据所需要的所有功能,包括:
- SQL 写入、无模式写入和通过第三方工具写入
- S标准 SQL 查询、时序数据特色查询以及自定义函数(UDF)
- S缓存每个时间序列的最新数据
- S连续查询以及事件驱动流计算
- S类 Kafka 数据订阅,加以过滤功能
- S与 Grafana、Google Data Studio 可视化工具的无缝集成
- S集群、高可用、高可靠
- S主流编程语言的连接器
- S命令行以及监控、数据导出、数据导入等多种工具
2、安装
2.1 安装TDengine服务
下载地址:
https://docs.taosdata.com/get-started/package/
目前 TDengine 在 Windows 平台上只支持 Windows Server 2016/2019 和 Windows 10/11。
这里我们先下载和安装TDengine-server-3.0.1.7-Windows-x64.exe
安装之后的文件夹如下:
2.2 运行TDengine服务(taosd)
安装后,可以在拥有管理员权限的 cmd 窗口执行 sc start taosd 或在 C:\TDengine 目录下,运行 taosd.exe 来启动 TDengine 服务进程。
taosd.exe
2.3 执行TDengine命令行(taos)
为便于检查 TDengine 的状态,执行数据库(Database)的各种即席(Ad Hoc)查询,TDengine 提供一命令行应用程序(以下简称为 TDengine CLI)taos。要进入 TDengine 命令行,您只要在终端执行 taos 即可。
taos
3、测试
TDengine 完整的软件包包括服务端(taosd)、用于与第三方系统对接并提供 RESTful 接口的 taosAdapter、应用驱动(taosc)、命令行程序 (CLI,taos) 和一些工具软件。TDengine 除了提供多种语言的连接器之外,还通过 taosAdapter 提供 RESTful 接口。
在 TDengine CLI 中,用户可以通过 SQL 命令来创建/删除数据库、表等,并进行数据库(Database)插入查询操作。在终端中运行的 SQL 语句需要以分号(;)结束来运行。示例:
3.1 新建数据库(命令行命令)
进入 TDengine CLI 后,你可执行各种 SQL 语句,包括插入、查询以及各种管理命令。
CREATE DATABASE demo;
USE demo;
CREATE TABLE t (ts TIMESTAMP, speed INT);
INSERT INTO t VALUES ('2019-07-15 00:00:00', 10);
INSERT INTO t VALUES ('2019-07-15 01:00:00', 20);
SELECT * FROM t;
除执行 SQL 语句外,系统管理员还可以从 TDengine CLI 进行检查系统运行状态、添加删除用户账号等操作。TDengine CLI 连同应用驱动也可以独立安装在机器上运行,更多细节请参考 TDengine 命令行。
3.2 新建数据库(脚本文件)
在 TDengine CLI 里可以通过 source 命令来运行脚本文件中的多条 SQL 命令。
# taos> source <filename>;
taos> source test.txt;
- test.txt内容如下:
CREATE DATABASE demo2;
USE demo2;
CREATE TABLE t (ts TIMESTAMP, speed INT);
INSERT INTO t VALUES ('2019-07-15 00:00:00', 10);
INSERT INTO t VALUES ('2019-07-15 01:00:00', 20);
SELECT * FROM t;
3.3 命令行参数
# taos -h ip/hostname
# taos -h h1.taos.com -s "use db; show tables;"
taos -h 127.0.0.1 -s "use demo; show tables;"
常用的几个命令行参数:
-c, --config-dir: 指定配置文件目录,默认为/etc/taos
-h, --host: 指定服务的FQDN,默认为本地服务
-s, --commands: 在不进入终端的情况下运行TDengine命令
-u, --user: 连接TDengine服务器的用户名,缺省为root
-p, --password: 连接TDengine服务器的密码,缺省为taosdata
-?, --help: 打印出所有命令行参数
3.4 taosBenchmark命令(体验写入速度)
可以使用 TDengine 的自带工具 taosBenchmark 快速体验 TDengine 的写入速度。
启动 TDengine 服务,然后在终端执行 taosBenchmark(曾命名为 taosdemo):
# taosBenchmark --help
taosBenchmark
该命令将在数据库 test 下面自动创建一张超级表 meters,该超级表下有 1 万张表,表名为 d0 到 d9999,每张表有 1 万条记录,每条记录有 ts、current、voltage、phase 四个字段,时间戳从 2017-07-14 10:40:00 000 到 2017-07-14 10:40:09 999,每张表带有标签 location 和 groupId,groupId 被设置为 1 到 10,location 被设置为 California.Campbell、California.Cupertino、California.LosAngeles、California.MountainView、California.PaloAlto、California.SanDiego、California.SanFrancisco、California.SanJose、California.SantaClara 或者 California.Sunnyvale。
3.5 TDengine CLI命令(体验查询速度)
使用上述 taosBenchmark 插入数据后,可以在 TDengine CLI(taos)输入查询命令,体验查询速度。
use test;
describe meters;
show tables;
查询超级表 meters 下的记录总条数:
SELECT COUNT(*) FROM test.meters;
查询 1 亿条记录的平均值、最大值、最小值等:
SELECT AVG(current), MAX(voltage), MIN(phase) FROM test.meters;
查询 location = “California.SanFrancisco” 的记录总条数:
SELECT COUNT(*) FROM test.meters WHERE location = "California.SanFrancisco";
查询 groupId = 10 的所有记录的平均值、最大值、最小值等:
SELECT AVG(current), MAX(voltage), MIN(phase) FROM test.meters WHERE groupId = 10;
对表 d10 按 10 每秒进行平均值、最大值和最小值聚合统计:
SELECT FIRST(ts), AVG(current), MAX(voltage), MIN(phase) FROM test.d10 INTERVAL(10s);
select count(tbname) from test.meters;
select count(*) from test.meters;
select * from test.meters limit 1;
3.6 基础命令
- 查看服务端参数
taosd -C
- 查看客户端参数
taos -C
- 客户端登录
taos
- 查看数据库参数
show variables;
3.7 节点管理
- 查看数据节点
show dnodes;
- 查看管理节点
show mnodes;
- 添加数据节点
create dnode "ms001.xiaomu.com:6030" ;
- 删除数据节点
drop dnode "ms001.xiaomu.com:6030" ;
3.8 用户管理
- 添加用户
create user xiaomu pass '123456';
- 删除用户
drop user xiaomu ;
- 查看用户
show users;
- 修改用户密码
alter user xiaomu pass '666666';
- 修改用户权限
alter user xiaomu privilege read;
系统内共有 super/write/read 三种权限级别,但不允许把 super 权限赋予用户。
3.9 会话管理
- 查看会话
show connections;
- 清理会话
kill connection 1219806014 ;
3.10 数据库管理
# 创建数据库
create database testdb replica 3 keep 30 days 7 blocks 4 update 1;
# 删除数据库
drop database testdb ;
# 修改数据库参数
alter database testdb comp 1;
alter database testdb replica 2;
alter database testdb keep 365;
alter database testdb quorum 2;
alter database testdb blocks 100;
alter database testdb cachelast 1;
# 查看vgroups
show vgroups;
3.11 表管理
# 创建表
use testdb;
create table t1 (ts TIMESTAMP,name1 BINARY(100)) ;
# 查看表结构
describe t1;
# 插入数据
insert into t1 values(‘2021-03-10 22:37:36.100’,‘test’);
# 查询表
select * from t1;
# 查看当前所有表
show tables;
# 创建超级表
use testdb;
create table st1 (ts TIMESTAMP,name1 BINARY(100)) tags (tgs binary(20));
# 查看超级表结构
describe st1;
# 插入数据
insert into t2 using st1 tags(‘tt’) values(‘2021-03-10 22:37:36.100’,‘test’);
# 查询超级表
select * from t1;
# 查看当前所有超级表
show stables;
4、开发指南
开发一个应用,如果你准备采用 TDengine 作为时序数据处理的工具,那么有如下几个事情要做:
- 确定应用到 TDengine 的连接方式。无论你使用何种编程语言,你总是可以使用 REST 接口, 但也可以使用每种编程语言独有的连接器进行方便的连接。
- 根据自己的应用场景,确定数据模型。根据数据特征,决定建立一个还是多个库;分清静态标签、采集量,建立正确的超级表,建立子表。
- 决定插入数据的方式。TDengine 支持使用标准的 SQL 写入,但同时也支持 Schemaless 模式写入,这样不用手工建表,可以将数据直接写入。
4.1 连接器建立连接的方式
TDengine 提供了丰富的应用程序开发接口,为了便于用户快速开发自己的应用,TDengine 支持了多种编程语言的连接器,其中官方连接器包括支持 C/C++、Java、Python、Go、Node.js、C#、Rust、Lua(社区贡献)和 PHP (社区贡献)的连接器。这些连接器支持使用原生接口(taosc)和 REST 接口(部分语言暂不支持)连接 TDengine 集群。社区开发者也贡献了多个非官方连接器,例如 ADO.NET 连接器、Lua 连接器和 PHP 连接器。
连接器建立连接的方式,TDengine 提供两种:
- 通过 taosAdapter 组件提供的 REST API 建立与 taosd 的连接,这种连接方式下文中简称“REST 连接”
- 通过客户端驱动程序 taosc 直接与服务端程序 taosd 建立连接,这种连接方式下文中简称“原生连接”。
关键不同点在于:
- 使用 REST 连接,用户无需安装客户端驱动程序 taosc,具有跨平台易用的优势,但性能要下降 30% 左右。
- 使用原生连接可以体验 TDengine 的全部功能,如参数绑定接口、订阅等等。
4.2 安装客户端驱动 taosc
如果选择原生连接,而且应用程序不在 TDengine 同一台服务器上运行,你需要先安装客户端驱动,否则可以跳过此一步。为避免客户端驱动和服务端不兼容,请使用一致的版本。
-
安装路径
默认安装路径为:C:\TDengine,其中包括以下文件(目录):
taos.exe:TDengine CLI 命令行程序
taosadapter.exe:提供 RESTful 服务和接受其他多种软件写入请求的服务端可执行文件
taosBenchmark.exe:TDengine 测试程序
cfg : 配置文件目录
driver: 应用驱动动态链接库
examples: 示例程序 bash/C/C#/go/JDBC/Python/Node.js
include: 头文件
log : 日志文件
unins000.exe: 卸载程序
- 配置 taos.cfg
编辑 taos.cfg 文件(默认路径 C:\TDengine\cfg\taos.cfg),将 firstEP 修改为 TDengine 服务器的 End Point,例如:h1.tdengine.com:6030。
如利用 FQDN 连接服务器,必须确认本机网络环境 DNS 已配置好,或在 hosts 文件中添加 FQDN 寻址记录, 如编辑 C:\Windows\system32\drivers\etc\hosts,添加类似如下的记录:192.168.1.99 h1.taos.com
- 安装验证
在 cmd 下进入到 C:\TDengine 目录下直接执行 taos.exe,连接到 TDengine 服务,进入到 TDengine CLI 界面,示例如下:
taos> show databases;
taos> use test;
taos> show tables;
4.3 安装连接器
使用 pip 从 PyPI 安装:
pip install taospy
从 Git URL 安装:
pip install git+https://github.com/taosdata/taos-connector-python.git
4.4 建立连接(原生连接)
在执行这一步之前,请确保有一个正在运行的,且可以访问到的 TDengine,而且服务端的 FQDN 配置正确。以下示例代码,都假设 TDengine 安装在本机,且 FQDN(默认 localhost) 和 serverPort(默认 6030) 都使用默认配置。
import taos
def test_connection():
# all parameters are optional.
# if database is specified,
# then it must exist.
conn = taos.connect(host="localhost",
port=6030,
user="root",
p1assword="taosdata",
database="log")
print('client info:', conn.client_info)
print('server info:', conn.server_info)
conn.close()
if __name__ == "__main__":
test_connection()
4.5 创建库
TDengine 采用类关系型数据模型,需要建库、建表。因此对于一个具体的应用场景,需要考虑库、超级表和普通表的设计。
为了在各种场景下 TDengine 都能以最大效率工作,TDengine 建议将不同数据特征的表创建在不同的库里。
CREATE DATABASE power KEEP 365 DURATION 10 BUFFER 16 WAL_LEVEL 1;
USE power;
上述语句将创建一个名为 power 的库,这个库的数据将保留 365 天(超过 365 天将被自动删除),每 10 天一个数据文件,每个 VNode 的写入内存池的大小为 16 MB,对该数据库入会写 WAL 但不执行 FSYNC。
4.6 创建超级表
一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用 TDengine, 需要对每个类型的数据采集点创建一个超级表。
CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);
与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 Schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。
4.7 创建表
TDengine 对每个数据采集点需要独立建表。与标准的关系型数据库一样,一张表有表名,Schema,但除此之外,还可以带有一到多个标签。创建时,需要使用超级表做模板,同时指定标签的具体值。
CREATE TABLE d1001 USING meters TAGS ("California.SanFrancisco", 2);
其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值为 “California.SanFrancisco”,标签 groupId 的具体标签值为 2。
4.8 自动建表
在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表且后面的 USING 语句被忽略。
INSERT INTO d1002 USING meters TAGS ("California.SanFrancisco", 2) VALUES (NOW, 10.2, 219, 0.32);
4.9 查询数据
在命令行工具 taos 中,从表 d1001 中查询出 voltage > 215 的记录,按时间降序排列,仅仅输出 2 条。
taos> select * from d1001 where voltage > 215 order by ts desc limit 2;
在 TDengine CLI,查找加利福尼亚州所有智能电表采集的电压平均值,并按照 location 分组。
taos> SELECT AVG(voltage), location FROM meters GROUP BY location;
在 TDengine CLI, 查找 groupId 为 2 的所有智能电表的记录条数,电流的最大值。
taos> SELECT count(*), max(current) FROM meters where groupId = 2;
通过迭代逐行获取查询结果。我们创建了 power 数据库,并向 meters 表写入了一些数据,以下示例代码展示如何查询这个表的数据。
import taos
def test_connection():
# all parameters are optional.
# if database is specified,
# then it must exist.
conn = taos.connect(host="localhost",
port=6030,
user="root",
p1assword="taosdata",
database="test")
print('client info:', conn.client_info)
print('server info:', conn.server_info)
query_api_demo(conn)
conn.close()
def query_api_demo(conn: taos.TaosConnection):
result: taos.TaosResult = conn.query("SELECT tbname, * FROM meters LIMIT 2")
print("field count:", result.field_count)
print("meta of fields[1]:", result.fields[1])
print("======================Iterate on result=========================")
for row in result:
print(row)
if __name__ == "__main__":
test_connection()
一次获取所有查询结果,并把每一行转化为一个字典返回。
import taos
def test_connection():
# all parameters are optional.
# if database is specified,
# then it must exist.
conn = taos.connect(host="localhost",
port=6030,
user="root",
p1assword="taosdata",
database="test")
print('client info:', conn.client_info)
print('server info:', conn.server_info)
fetch_all_demo(conn)
conn.close()
def fetch_all_demo(conn: taos.TaosConnection):
result: taos.TaosResult = conn.query("SELECT ts, current FROM meters LIMIT 10")
rows = result.fetch_all_into_dict()
print("row count:", result.row_count)
print("===============all data===================")
print(rows)
if __name__ == "__main__":
test_connection()
4.10 连接器简介
TDengine 提供了丰富的应用程序开发接口,为了便于用户快速开发自己的应用,TDengine 支持了多种编程语言的连接器,其中官方连接器包括支持 C/C++、Java、Python、Go、Node.js、C# 和 Rust 的连接器。这些连接器支持使用原生接口(taosc)和 REST 接口(部分语言暂不支持)连接 TDengine 集群。社区开发者也贡献了多个非官方连接器,例如 ADO.NET 连接器、Lua 连接器和 PHP 连接器。
taospy 是 TDengine 的官方 Python 连接器。taospy 提供了丰富的 API, 使得 Python 应用可以很方便地使用 TDengine。taospy 对 TDengine 的原生接口和 REST 接口都进行了封装, 分别对应 taospy 包的 taos 模块 和 taosrest 模块。 除了对原生接口和 REST 接口的封装,taospy 还提供了符合 Python 数据访问规范(PEP 249) 的编程接口。这使得 taospy 和很多第三方工具集成变得简单,比如 SQLAlchemy 和 pandas。
taos-ws-py 是使用 WebSocket 方式连接 TDengine 的 Python 连接器包。可以选装。
使用客户端驱动提供的原生接口直接与服务端建立的连接的方式下文中称为“原生连接”;使用 taosAdapter 提供的 REST 接口或 WebSocket 接口与服务端建立的连接的方式下文中称为“REST 连接”或“WebSocket 连接”。
-
支持的平台
原生连接支持的平台和 TDengine 客户端支持的平台一致。
REST 连接支持所有能运行 Python 的平台。 -
支持的功能
原生连接支持 TDengine 的所有核心功能, 包括: 连接管理、执行 SQL、参数绑定、订阅、无模式写入(schemaless)。
REST 连接支持的功能包括:连接管理、执行 SQL。 (通过执行 SQL 可以: 管理数据库、管理表和超级表、写入数据、查询数据、创建连续查询等)。
pip3 uninstall taos taospy
pip3 install taospy
pip3 install taospy==2.3.0
pip3 install taospy[ws]
pip3 install taos-ws-py
对于原生连接,需要验证客户端驱动和 Python 连接器本身是否都正确安装。如果能成功导入 taos 模块,则说明已经正确安装了客户端驱动和 Python 连接器。可在 Python 交互式 Shell 中输入:
import taos
对于 REST 连接,只需验证是否能成功导入 taosrest 模块。可在 Python 交互式 Shell 中输入:
import taosrest
对于 WebSocket 连接,只需验证是否能成功导入 taosws 模块。可在 Python 交互式 Shell 中输入:
import taosws
4.11 连接器(原生连接:taos)
(1)原生连接
请确保 TDengine 集群已经启动, 且集群中机器的 FQDN (如果启动的是单机版,FQDN 默认为 hostname)在本机能够解析, 可用 ping 命令进行测试:
ping <FQDN>
然后测试用 TDengine CLI 能否正常连接集群:
taos -h <FQDN> -p <PORT>
taos -h localhost -P 6030
上面的 FQDN 可以为集群中任意一个 dnode 的 FQDN, PORT 为这个 dnode 对应的 serverPort。
import taos
conn: taos.TaosConnection = taos.connect(host="localhost",
user="root",
p1assword="taosdata",
database="test",
port=6030,
config="/etc/taos", # for windows the default value is C:\TDengine\cfg
timezone="Asia/Shanghai") # default your host's timezone
server_version = conn.server_info
print("server_version", server_version)
client_version = conn.client_info
print("client_version", client_version) # 3.0.0.0
conn.close()
conn = taos.connect()
# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement.
conn.execute("DROP DATABASE IF EXISTS test")
conn.execute("CREATE DATABASE test")
# change database. same as execute "USE db"
conn.select_db("test")
conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)")
affected_row = conn.execute("INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m, 24.4)")
print("affected_row", affected_row)
# output:
# affected_row 3
4.12 连接器(REST连接:taosrest )
(2)REST 连接
对于 REST 连接, 除了确保集群已经启动,还要确保 taosAdapter 组件已经启动。可以使用如下 curl 命令测试:
在安装文件内找到taosAdapter .exe。
运行taosAdapter.exe之后的界面如下:
输入如下的命令:
# curl -u root:taosdata http://<FQDN>:<PORT>/rest/sql -d "select server_version()"
curl -u root:taosdata http://localhost:6030/rest/sql -d "select server_version()"
上面的 FQDN 为运行 taosAdapter 的机器的 FQDN, PORT 为 taosAdapter 配置的监听端口, 默认为 6041。 如果测试成功,会输出服务器版本信息,比如:
from taosrest import connect, TaosRestConnection, TaosRestCursor
conn = connect(url="http://localhost:6041",
user="root",
p1assword="taosdata",
timeout=30)
# create STable
cursor = conn.cursor()
cursor.execute("DROP DATABASE IF EXISTS power")
cursor.execute("CREATE DATABASE power")
cursor.execute(
"CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)")
# insert data
cursor.execute("""INSERT INTO power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000)
power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000)
power.d1003 USING power.meters TAGS('California.LosAngeles', 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000)
power.d1004 USING power.meters TAGS('California.LosAngeles', 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)""")
print("inserted row count:", cursor.rowcount)
# query data
cursor.execute("SELECT * FROM power.meters LIMIT 3")
# get total rows
print("queried row count:", cursor.rowcount)
# get column names from cursor
column_names = [meta[0] for meta in cursor.description]
# get rows
data = cursor.fetchall()
print(column_names)
for row in data:
print(row)
conn.close()
以上示例代码假设 TDengine 安装在本机, 且 FQDN 和 serverPort 都使用了默认配置。
4.12 连接器(REST连接:RestClient )
RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方法用于执行任意 SQL 语句, 并返回执行结果。
from taosrest import RestClient
client = RestClient("http://localhost:6041", user="root", p1assword="taosdata")
res: dict = client.sql("SELECT ts, current FROM power.meters LIMIT 1")
print(res)
4.13 连接器(pandas )
- REST连接
import pandas
from sqlalchemy import create_engine, text
engine = create_engine("taosrest://root:taosdata@localhost:6041")
conn = engine.connect()
df: pandas.DataFrame = pandas.read_sql(text("SELECT * FROM power.meters"), conn)
conn.close()
# print index
print(df.index)
# print data type of element in ts column
print(type(df.ts[0]))
print(df.head(3))
- 原生连接
import pandas
from sqlalchemy import create_engine, text
engine = create_engine("taos://root:taosdata@localhost:6030/power")
conn = engine.connect()
df = pandas.read_sql(text("SELECT * FROM power.meters"), conn)
conn.close()
# print index
print(df.index)
# print data type of element in ts column
print(type(df.ts[0]))
print(df.head(3))
4.14 异常处理
所有数据库操作如果出现异常,都会直接抛出来。由应用程序负责异常处理。比如:
import taos
try:
conn = taos.connect()
conn.execute("CREATE TABLE 123") # wrong sql
except taos.Error as e:
print(e)
print("exception class: ", e.__class__.__name__)
print("error number:", e.errno)
print("error message:", e.msg)
except BaseException as other:
print("exception occur")
print(other)
结语
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位大佬童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!