Recharts和InfluxDB教程 - 使用ReactJS可视化物联网传感器数据

导航至

在本教程中,您将学习如何使用Recharts图表库在ReactJS中创建自定义数据可视化,以显示存储在InfluxDB中的时间序列数据。为此,您需要存储一些由物联网传感器记录的实时数据,这些传感器记录了房间内的温度、湿度和一氧化碳水平。

在本篇文章结束时,您将深入了解Recharts的基础知识,并初步了解如何使用InfluxDB编写和读取时间序列数据。

什么是Recharts?

Recharts 是一个流行的图表和数据可视化库,用于与ReactJS一起使用。Recharts最初于2016年发布,是第一个专门为ReactJS应用设计的图表库之一,该库本身是用React和D3js构建的。

与大多数伟大的开源项目一样,Recharts是在面对构建业务应用程序时遇到的真实问题和痛点后设计的。当时的主要问题是在ReactJS应用中尝试集成现有的JavaScript图表库,这证明是相当尴尬的。通过在React本身中创建图表库,开发人员可以利用React的虚拟DOM来使图表更快,使用React的原生组件生命周期系统,并使自定义图表更加容易。Recharts提供的最佳功能包括

  • 11种不同的图表类型
  • 易于理解的API
  • 解耦的组件架构
  • 灵活且易于定制
  • 强大的社区使其易于解决任何问题

Recharts的主要目标是使在ReactJS应用程序中使用常规React组件创建图表变得简单,提供原生的SVG支持,并为组件提供一个声明式API。可以说Recharts在实现这些目标方面取得了成功,因此现在在GitHub上有超过17,000颗星,数百位贡献者,以及许多公司在生产中使用它。所有这些都使得Recharts成为您自己应用程序中的一个安全选择。

什么是InfluxDB?

InfluxDB 是一个NoSQL 时间序列数据库,这意味着InfluxDB针对存储、查询和分析时间序列数据进行了优化。时间序列数据是一系列按时间顺序索引的数据点。时间序列数据的常见示例包括

除了能够有效地存储和查询时间序列数据外,InfluxDB还作为一个用于处理数据的平台工具。这包括基于您的数据创建无服务器任务和警报的功能,使用Flux查询语言预构建的常见时间序列数据分析函数,以及易于使用的仪表板来显示数据。

开始使用Recharts

在进入教程本身之前,我将简要介绍Recharts库,并介绍一些将在本特定教程之外使用Recharts时有用的高级概念。

图表数据格式

为了使图表正常显示,您需要将数据作为属性提供给图表。数据结构通常是一个对象数组,每个对象代表一个单独的数据点。以下是条形图的示例数据值

const data = [
  {
    name: 'Page A',
    uv: 4000,
    pv: 2400,
    amt: 2400,
  },
  {
    name: 'Page B',
    uv: 3000,
    pv: 1398,
    amt: 2210,
  },
  {
    name: 'Page C',
    uv: 2000,
    pv: 9800,
    amt: 2290,
  },
  {
    name: 'Page D',
    uv: 2780,
    pv: 3908,
    amt: 2000,
  },
  {
    name: 'Page E',
    uv: 1890,
    pv: 4800,
    amt: 2181,
  },
  {
    name: 'Page F',
    uv: 2390,
    pv: 3800,
    amt: 2500,
  },
  {
    name: 'Page G',
    uv: 3490,
    pv: 4300,
    amt: 2100,
  },
];

您将使用代码中的属性将这些对象键映射到Recharts

图表属性

每种图表类型都有一些独特的属性可以传递以自定义它,但也有一些通用的属性对所有图表都可用,值得了解

  • 数据:需要将整个`data`对象传递给父图表组件
  • 数据键:`dataKey`属性
  • 高度/宽度:高度和宽度将确定您图表的大小,以固定像素数表示。如果您想要一个响应式图表,Recharts还提供了一个响应式容器组件,该组件将包装您的图表组件,并占用可用空间的百分比
  • 描边/填充:这些属性用于定义图表的线/条的颜色。

Recharts图表组件的结构

Recharts的设计理念是一切都是组件。因此,您通常会嵌套组件以获得所需的图表。例如,提示、网格和图表轴都是通过将提供的组件嵌套到图表父元素中来添加到图表中的。

Recharts提供的高级组件是ResponsiveContainer组件,可以用来包装图表,以便它们可以具有响应式的高度和宽度。

Recharts库中最常用的实用组件包括

  • 提示 - 可以用于在用户悬停在数据点上时显示数据点的确切值。格式化是完全可定制的
  • 图例 - 用于显示图表的单元名称/值的组件
  • Y轴/X轴 - 用于向图表添加轴的组件

使用Recharts的基本折线图

关于理论的内容已经足够了:现在是时候看看使用Recharts实现简单折线图的一些实际代码了

import {
  LineChart,
  Line,
  Tooltip,
  CartesianGrid,
  YAxis,
  Legend
} from "recharts";

const data = [
  {
    name: "requests",
    count: 1500
  },
  {
    name: "requests",
    count: 1720
  },
  {
    name: "requests",
    count: 1650
  },
  {
    name: "requests",
    count: 1510
  },
  {
    name: "requests",
    count: 1400
  }
];
export const BasicLine = () => {
  return (
    <LineChart width={400} height={400} data={data}>
      <Line type="monotone" dataKey="count" />
      <CartesianGrid />
      <YAxis />
      <Legend verticalAlign="top" height={30} />
      <Tooltip />
    </LineChart>
  );
};

Basic Line Chart with Recharts

我将此图表放置在新建的components文件夹中,然后将其导入到主App.js文件中,以便由React渲染。这很简单,但为您提供了如何使用Recharts的基本示例。要注意的关键点是dataKey属性,它用于定义折线图的价值。如果一个对象有多个键,您仍然可以控制用于图表值的键。务必查看文档,并尝试调整折线图属性,以便在进入主项目之前更好地了解Recharts。

设置InfluxDB

本教程将涉及在InfluxDB中存储时间序列数据,然后查询这些数据,使用Recharts进行可视化。您可以在几分钟内创建一个免费的InfluxDB云账户,或者下载并安装开源版本

一旦您有了正在运行的InfluxDB实例,您需要遵循以下步骤来设置使用JavaScript客户端库的InfluxDB API

  1. 创建组织 - 这将在创建账户时完成,您的组织名称可以在用户设置中找到,如果您想更改它或忘记它。
  2. 创建存储桶来存储数据 - 存储桶名称将用于我们的代码中以查询数据。
  3. 为您的存储桶创建API令牌 - 创建存储桶后,您需要创建一个API令牌,该令牌将用于从客户端库发出的API请求进行认证。

安装项目需求

要遵循本教程,您需要一个NodeJS开发环境。Create React App将被用作项目的起始模板,但您可以使用您感到最舒适的环境设置。如果您不想在本地计算机上设置任何东西,可以使用Codesandbox或Replit之类的工具在浏览器中创建开发环境。这两个服务都是免费的,并提供React模板,因此您可以在几秒钟内开始。

如果您在本地开发并使用VS Code作为您的编辑器,您可以尝试使用Flux VS Code扩展,这将为您提供语法高亮、自动完成和在VS Code中直接集成InfluxDB。这将使测试和运行您的InfluxDB查询变得更容易,所以请试试看。

本教程需要InfluxDB JavaScript客户端库和Recharts,可以使用以下命令安装:

npm install @influxdata/influxdb-client recharts

您还需要获取您之前创建的API令牌,并将其存储为环境变量或代码中的变量。请注意,这是一个安全问题,并且不应在生产环境中使用,因为您的API令牌将嵌入到公共前端源代码中。

在生产环境中,您会希望在这些API调用服务器端处理以保护令牌的安全。您可以在我们的Highcharts教程中看到这个例子。另一个选择是给予您的API令牌客户端只读访问权限,并设置短暂的寿命,以便它快速过期。

向InfluxDB写入数据

接下来,您需要向InfluxDB写入一些数据,以便您在教程的后面部分有可以查询和可视化的东西。InfluxDB提供了多种写入数据的方式:

  • Telegraf - Telegraf是由InfluxData创建的开源服务器代理,用于收集、处理数据,然后输出到存储而不需要任何代码。Telegraf拥有250多个输入插件,可以输出到40个不同的数据存储。如果您有兴趣学习如何使用Telegraf,可以参考这个入门教程
  • 文件上传 - InfluxDB UI提供了上传CSV或文本文件的功能,这些文件格式为行协议。您也可以直接在UI中输入数据并提交几个数据点,如果您想快速测试一些数据。
  • 客户端库和REST API - InfluxDB通过其REST API直接提供对所有功能的访问,为开发者在如何存储和查询数据方面提供了最大灵活性。还提供了13种不同编程语言的客户端库。这些库是REST API的包装器,为开发者提供了一系列功能,使使用InfluxDB开发应用程序变得更容易。
  • 示例数据 - InfluxDB包含一些示例数据集,可以使用Flux将其写入bucket。

对于本教程,我们将使用示例数据集以减少复杂性。有几个不同的时间序列数据集可用,但本教程我们将使用空气传感器数据,它提供了建筑物多个房间的温度、湿度和一氧化碳传感器数据。

这些数据每15分钟更新一次,因此为了确保您的数据保持最新,您需要创建一个InfluxDB任务,每隔15分钟更新bucket中的数据。点击侧边栏上的“任务”图标,然后点击“创建任务”。

给任务起个名字,然后将“每隔”列的值设置为‘15m’,以便任务每15分钟运行一次。将以下Flux代码粘贴到编辑器中

import "influxdata/influxdb/sample"

sample.data(set: "airSensor")
  |> to(bucket: "your-bucket-name"  )

请确保将桶名称更改为您之前创建的桶。

Change the bucket name

点击 保存 并返回任务仪表板页面。点击您创建的任务的轮形图标,然后点击 运行,使其立即运行,这样您就不必等待可能长达15分钟。

Create-Task

现在返回数据探索器并检查您的桶。如果您遵循了所有步骤,提交查询时应能看到您的数据。如果您看不到任何数据,请尝试刷新浏览器并再次确认您查看的是正确的桶。如果您仍然看不到任何数据,请尝试查看文档,并确保您遵循了所有之前的步骤,没有遗漏任何内容。

从 InfluxDB 查询数据

现在我们已经存储了一些数据,我们可以开始进行查询。在本教程中,您将使用 Flux,这是一种专为从头开始处理时间序列数据而设计的查询语言。Flux 提供了许多内置函数来处理时间序列数据,使您作为开发者的生活更加轻松。它在语法方面与 JavaScript 有点相似,因此学习基础知识不会花费太多时间。

要从 InfluxDB 中查询存储的数据开始,最简单的方法是使用用户界面中的 数据探索器。您可以直观地修改查询并实时查看数据的变化。然后,您可以切换到可视化查询构建器,并查看实际的底层 Flux 查询。在数据上玩弄并查看查询可能是学习 Flux 的最快方式。要全面了解 Flux 可以做什么,请务必阅读 文档

从高层次来看,Flux 查询有 4 个主要部分

  1. 来源 - 正在被查询的特定桶
  2. 过滤器 - 在大多数情况下,您可能只需要您桶中的一部分数据,因此您将根据时间范围进行筛选,并可能从每个数据点中选择一些需要的字段。
  3. 形状 - 在此阶段,您可能想修改数据格式以进行进一步处理。这可能包括将列旋转到行、删除列或按键分组数据。
  4. 处理 - 最终阶段是处理数据。一些示例包括聚合剩余值、选择特定最小或最大值、重写或修改每行值,或分析数据并根据结果发送警报。

以下是一个遵循上述结构的简单 Flux 查询

from(bucket: "example-bucket")            // ?? Source
  |> range(start: -1d)                    // ?? Filter on time
  |> filter(fn: (r) => r._field == "foo") // ?? Filter on column values
  |> group(columns: ["sensorID"])         // ?? Shape
  |> mean()                               // ?? Process

使用 Recharts 可视化数据

所有设置完成之后,我们终于可以进入好东西了。对于本部分,我们将利用 Recharts 的 ComposedChart 组件,该组件允许我们将多种不同类型的图表组合在一起。结果将是一个折线图和面积图,显示我们正在跟踪的特定房间的湿度和温度,该房间由物联网传感器跟踪。

您首先想做的事情是在您之前创建的 components 文件夹中创建一个新的组件。您可以为它命名任何您想要的名称,但我会将其命名为 InfluxChart,这样它就会在 App.js 文件中被导入。

以下是图表组件的代码。以下我将逐行解释

import React, { useState, useEffect } from "react";
import { InfluxDB } from "@influxdata/influxdb-client";
import {
  ComposedChart,
  Bar,
  Line,
  XAxis,
  YAxis,
  Tooltip,
  Legend,
  CartesianGrid,
  Area
} from "recharts";

const token = "your-API-token";
const org = "org-name";
const url = "INFLUX_DB_URL";

let query = `from(bucket: "air-sensor")
  |> range(start: -30m)
  |> filter(fn: (r) => r["_measurement"] == "airSensors")
  |> filter(fn: (r) => r["_field"] == "humidity" or r["_field"] == "temperature" )
  |> filter(fn: (r) => r["sensor_id"] == "TLM0102")
  |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
  |> yield(name: "results")
`;

export const InfuxChart = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    let res = [];
    const influxQuery = async () => {
      //create InfluxDB client
      const queryApi = await new InfluxDB({ url, token }).getQueryApi(org);
      //make query
      await queryApi.queryRows(query, {
        next(row, tableMeta) {
          const o = tableMeta.toObject(row);
          //push rows from query into an array object
          res.push(o);
        },
        complete() {
          let finalData = []

          for (let i = 0; i < res.length; i++) {
            let point = {};
            point["humidity"] = res[i]["humidity"];
            point["temperature"] = res[i]["temperature"];

            point["name"] = res[i]["_time"];
            finalData.push(point);
          }
          setData(finalData);
        },
        error(error) {
          console.log("query failed- ", error);
        }
      });
    };

    influxQuery();
  }, []);
  return (
    <div>
      <h1>Influx Chart</h1>
      <ComposedChart width={900} height={400} data={data}>
        <CartesianGrid />
        <Tooltip />
        <Line
          stroke="#0ff770"
          strokeWidth={1}
          dataKey="temperature"
          dot={false}
        />
        <Area stroke="#bf04b3" fill="#f235e6" dataKey="humidity" />
        <XAxis hide dataKey="name" />
        <YAxis />
      </ComposedChart>
    </div>
  );
};

以下是生成的图表:使用 Recharts 可视化数据 - 生成的图表

以下是上述代码中的内容

  • Flux查询 - Flux查询首先使用range函数从我们的桶中抓取过去30分钟内存储的所有传感器数据。然后,这些数据被过滤,只抓取airSensors测量值,再次过滤以抓取仅包含humiditytemperature 字段的值,最后再次过滤,只抓取ID为TLM0102 的传感器数据。最后,使用pivot 函数根据时间戳将湿度和温度字段值合并为单个行值。
  • useEffect - 在图表组件内部,最复杂的代码是useEffect钩子,它使用客户端库调用InfluxDB。查询的结果被追加到res结果数组中。一旦调用返回,complete 中的代码就会运行。在这里,我们遍历结果数组,在将结果添加到finalData 数组之前修改结果。然后,我们使用由React useState钩子提供的setData 将数据设置到组件的状态中,以便图表可以显示数据。
  • 图表组件 - 图表的JSX相当简单,数据通过data属性传入,而LineBar 组件分别显示一个值。

App.js文件很简单,我们只是导入了一个放在components文件夹中的图表组件。

import { InfluxChart } from "./components/InfluxChart";
export default function App() {
  return (
    <div className="App">
      <InfluxChart />
    </div>
  );
}

因此,你已经设置了一些基本的图表,但有点无聊,下一步是什么?好消息是,这个项目可以从多个不同方向扩展。你可以更深入地了解你在使用Recharts或InfluxDB时正在做的事情。这里有几个想法

  • 自定义图表事件处理器 - Recharts允许你将自定义函数传递给图表中的每个组件,以处理各种事件,如鼠标悬停、点击以及鼠标进入或退出组件区域。这为你提供了大量自定义图表行为的选择。
  • 连接不同的数据源 - 样本数据并不是最有吸引力的数据,但好消息是,有大量不同的选项可供选择。你可以查看Telegraf的250多个集成中的任何一个,并找到一个看起来很有趣可以玩耍的数据源。
  • 动态图表 - 因为Recharts就像任何其他React组件一样,你可以在主图表组件内部组合元素,如线条或条形。以本教程中使用的传感器数据为例,你可以设置图表,使其在添加更多传感器房间时仍然有效,而不是仅限于静态值。
  • 设置警报或自动化任务 - 你已经创建了一个任务来确保空气传感器数据每15分钟更新一次,但你可以使用InfluxDB任务做更多的事情。你可以设置阈值来监控你的数据,并在这些阈值被违反时创建自动化操作。

附加资源

现在你已经在使用InfluxDB和Recharts方面打下了良好的基础,如果你想将你的知识提升到下一个层次,我将为你提供一些资源进行检查。