使用 Quix Streams 来下采样数据
使用 Quix Streams 查询存储在 InfluxDB 中的时间序列数据,并定期写入 Kafka,持续对其进行降采样,然后将降采样的数据写回 InfluxDB。Quix Streams 是一个开源的 Python 库,用于构建容器化的流处理应用程序,使用 Apache Kafka。它被设计为作为一个服务运行,持续处理数据流,同时将结果流式传输到 Kafka 主题。您可以在本地尝试,使用本地 Kafka 安装,或在 Quix Cloud 上运行试用。
本指南使用 Python 和 InfluxDB 3 Python client library,但您可以使用您选择的运行时和任何可用的 InfluxDB 3 client libraries。本指南还假设您已经 setup your Python project and virtual environment。
管道架构
下图展示了数据在被降采样时如何在进程之间传递:
InfluxDB v3 源生产者
下采样过程
InfluxDB v3 吸 Sink 消费者
通常直接将原始数据写入Kafka比先将原始数据写入InfluxDB更加高效(本质上是用“raw-data”主题开始Quix Streams管道)。然而,本指南假设您已经在InfluxDB中有原始数据需要进行降采样。
设置前提条件
本指南中描述的过程需要以下内容:
- 一个准备好进行降采样的 InfluxDB Cloud Serverless 账号。
- 一个 Quix Cloud 账户或本地的 Apache Kafka 或 Red Panda 安装。
- 熟悉基本的Python和Docker概念。
安装依赖
使用 pip 安装以下依赖项:
influxdb_client_3quixstreams<2.5pandas
pip install influxdb3-python pandas quixstreams<2.5
准备InfluxDB桶
降采样过程涉及两个 InfluxDB 存储桶。 每个存储桶都有一个 保留期,指定数据在过期并被删除之前持续多久。 通过使用两个存储桶,您可以在保留期较短的存储桶中存储未修改的高分辨率数据,然后在保留期较长的存储桶中存储降采样的低分辨率数据。
确保您为以下每一项准备一个桶:
- 从中查询未修改的数据
- 另一个用于写入下采样数据的
有关创建存储桶的信息,请参见 创建存储桶。
创建下采样逻辑
此过程从输入的Kafka主题读取原始数据,该主题存储从InfluxDB流式传输的数据,进行下采样,然后将其发送到一个输出主题,该主题用于写回InfluxDB。
使用Quix Streams库的
Application类来初始化与Apache Kafka的连接。from quixstreams import Application app = Application(consumer_group='downsampling-process', auto_offset_reset='earliest') input_topic = app.topic('raw-data') output_topic = app.topic('downsampled-data') # ...配置Quix Streams内置窗口函数,以创建一个持续将数据下采样到1分钟桶的翻滚窗口。
# ... target_field = 'temperature' # The field that you want to downsample. def custom_ts_extractor(value): # ... # truncated for brevity - custom code that defines the 'time_recorded' # field as the timestamp to use for windowing... topic = app.topic(input_topic, timestamp_extractor=custom_ts_extractor) sdf = ( sdf.apply(lambda value: value[target_field]) # Extract temperature values .tumbling_window(timedelta(minutes=1)) # 1-minute tumbling windows .mean() # Calculate average temperature .final() # Emit results at window completion ) sdf = sdf.apply( lambda value: { 'time': value['end'], # End of the window 'temperature_avg': value['value'], # Average temperature } ) sdf.to_topic(output_topic) # Output results to the 'downsampled-data' topic # ...
结果被流式传输到 Kafka 主题,downsampled-data。
注意:“sdf”代表“流式数据框”。
您可以在Quix GitHub repository中找到此过程的完整代码。
创建生产者和消费者客户端
使用 influxdb_client_3 和 quixstreams 模块实例化两个与 InfluxDB 和 Apache Kafka 交互的客户端:
- A producer client configured to read from your InfluxDB bucket with 未修改的 data and 生成 that data to Kafka.
- 一个消费者客户端,被配置为消费来自Kafka的数据并将降采样的数据写入相应的InfluxDB桶。
创建生产者客户端
提供以下制片人的凭证:
- host: InfluxDB Cloud Serverless 区域 URL (不带协议)
- org: InfluxDB 组织名称
- token: InfluxDB API 令牌,具有对您想要查询和写入的存储桶的读写权限。
- database: InfluxDB 桶名称
生产者在特定的时间间隔内从InfluxDB查询新数据。它被配置为查找在变量中定义的特定测量。它将原始数据写入名为‘raw-data’的Kafka主题
from influxdb_client_3 import InfluxDBClient3
from quixstreams import Application
import pandas
# Instantiate an InfluxDBClient3 client configured for your unmodified bucket
influxdb_raw = InfluxDBClient3(
host='cloud2.influxdata.com',
token='API_TOKEN',
database='RAW_BUCKET_NAME'
)
# os.environ['localdev'] = 'true' # Uncomment if you're using local Kafka rather than Quix Cloud
# Create a Quix Streams producer application that connects to a local Kafka installation
app = Application(
broker_address=os.environ.get('BROKER_ADDRESS','localhost:9092'),
consumer_group=consumer_group_name,
auto_create_topics=True
)
# Override the app variable if the local development env var is set to false or is not present.
# This causes Quix Streams to use an application configured for Quix Cloud
localdev = os.environ.get('localdev', 'false')
if localdev == 'false':
# Create a Quix platform-specific application instead (broker address is in-built)
app = Application(consumer_group=consumer_group_name, auto_create_topics=True)
topic = app.topic(name='raw-data')
## ... remaining code trunctated for brevity ...
# Query InfluxDB for the raw data and store it in a Dataframe
def get_data():
# Run in a loop until the main thread is terminated
while run:
try:
myquery = f'SELECT * FROM "{measurement_name}" WHERE time >= {interval}'
print(f'sending query {myquery}')
# Query InfluxDB 3 using influxql or sql
table = influxdb_raw.query(
query=myquery,
mode='pandas',
language='influxql')
#... remaining code trunctated for brevity ...
# Send the data to a Kafka topic for the downsampling process to consumer
def main():
"""
Read data from the Query and publish it to Kafka
"""
#... remaining code trunctated for brevity ...
for index, obj in enumerate(records):
print(obj) # Obj contains each row in the table includimng temperature
# Generate a unique message_key for each row
message_key = obj['machineId']
logger.info(f'Produced message with key:{message_key}, value:{obj}')
serialized = topic.serialize(
key=message_key, value=obj, headers={'uuid': str(uuid.uuid4())}
)
# publish each row returned in the query to the topic 'raw-data'
producer.produce(
topic=topic.name,
headers=serialized.headers,
key=serialized.key,
value=serialized.value,
)
您可以在Quix GitHub repository中找到此过程的完整代码。
创建消费者
与之前一样,为消费者提供以下凭据:
- host: InfluxDB Cloud Serverless 区域 URL (不带协议)
- org: InfluxDB 组织名称
- token: InfluxDB API 令牌,具有对您想要查询和写入的存储桶的读取和写入权限。
- database: InfluxDB 桶名称
该过程从Kafka主题 downsampled-data 读取消息,并将每条消息作为点字典写回InfluxDB。
# Instantiate an InfluxDBClient3 client configured for your downsampled database.
# When writing, the org= argument is required by the client (but ignored by InfluxDB).
influxdb_downsampled = InfluxDBClient3(
host='cloud2.influxdata.com',
token='API_TOKEN',
database='DOWNSAMPLED_BUCKET_NAME',
org=''
)
# os.environ['localdev'] = 'true' # Uncomment if you're using local Kafka rather than Quix Cloud
# Create a Quix Streams consumer application that connects to a local Kafka installation
app = Application(
broker_address=os.environ.get('BROKER_ADDRESS','localhost:9092'),
consumer_group=consumer_group_name,
auto_create_topics=True
)
# Override the app variable if the local development env var is set to false or is not present.
# This causes Quix Streams to use an application configured for Quix Cloud
localdev = os.environ.get('localdev', 'false')
if localdev == 'false':
# Create a Quix platform-specific application instead (broker address is in-built)
app = Application(consumer_group=consumer_group_name, auto_create_topics=True)
input_topic = app.topic('downsampled-data')
## ... remaining code trunctated for brevity ...
def send_data_to_influx(message):
logger.info(f'Processing message: {message}')
try:
## ... remaining code trunctated for brevity ...
# Construct the points dictionary
points = {
'measurement': measurement_name,
'tags': tags,
'fields': fields,
'time': message['time']
}
influxdb_downsampled.write(record=points, write_precision='ms')
sdf = app.dataframe(input_topic)
sdf = sdf.update(send_data_to_influx) # Continuously apply the 'send_data' function to each message in the incoming stream
## ... remaining code trunctated for brevity ...
您可以在Quix GitHub repository中找到此过程的完整代码。
获取完整的下采样代码文件
要获取本教程中引用的完整文件集,请克隆 Quix 的“降采样模板”代码库,或使用保存为 Jupyter Notebook 的本教程的交互版本。
克隆降采样模板库
要克隆下采样模板,请在命令行中输入以下命令:
git clone https://github.com/quixio/template-influxdbv3-downsampling.git
该仓库包含以下文件夹,存储整个流程的不同部分:
机器数据到 InfluxDB: 一个生成合成机器数据并将其写入 InfluxDB 的脚本。这在您还没有自己的数据,或者只想先使用测试数据时非常有用。
- It produces a reading every 250 milliseconds.
- This script originally comes from the InfluxCommunity repository but has been adapted to write directly to InfluxDB rather than using an MQTT broker.
InfluxDB V3 数据源: 一项定期从 InfluxDB 查询新数据的服务。它被配置为查找之前提到的合成机器数据生成器产生的测量值。它将原始数据写入名为“raw-data”的 Kafka 主题。
下采样器: 一项服务,在来自InfluxDB的数据上执行1分钟的滚动窗口操作,并每分钟发出“温度”读数的平均值。它将输出写入“下采样数据”Kafka主题。
InfluxDB V3 数据接收器: 一个从“降采样数据”主题读取并将降采样记录作为点写回到 InfluxDB 的服务。
使用下采样的Jupyter笔记本
您可以使用交互式笔记本 “使用 InfluxDB 和 Quix Streams 持续下采样数据” 来尝试自己进行下采样代码。它配置为在运行时环境中安装 Apache Kafka(例如 Google Colab)。
每个进程也被设置为在后台运行,以便一个正在运行的单元不会阻塞其余的教程。