基于pyspark3.4.1的Structured Streaming进行文件源的实时监控
在Spark Structured Streaming中,File源支持多种文件格式,包括text、csv、json、orc、parquet等。以下是一些常用的File源选项及其解释:
- path:指定输入目录的路径,这是所有文件格式共有的选项。
- maxFilesPerTrigger:每个触发器(trigger)中考虑的新文件的最大数量,默认没有最大值限制。
- latestFirst:是否首先处理最新的文件,这在有大量积压文件时非常有用,默认值为false。
- fileNameOnly:是否仅基于文件名而不是完整路径来检查新文件,默认值为false。如果设置为true,那么即使文件路径不同,只要文件名相同,就会被认为是同一个文件。
- maxFileAge:可以找到的文件的最大年龄,超过这个年龄的文件将被忽略。对于第一个批次,所有文件都被认为是有效的。如果
latestFirst设置为true且maxFilesPerTrigger被设置,那么这个参数将被忽略,因为可能会忽略掉应该被处理的有效旧文件。默认值为1周。 - cleanSource:处理完成后如何清理文件。可用选项有"archive"、"delete"、"off"。如果未提供此选项,默认值为"off"。如果选择"archive",还需要提供
sourceArchiveDir选项,该值不能与源模式在深度上匹配,以确保归档的文件不会被包括为新的源文件。 - sourceArchiveDir:当
cleanSource设置为"archive"时,需要提供此选项,用于指定归档文件的目录。
这些选项可以控制文件源的行为,例如如何处理新文件、旧文件,以及如何处理已经处理过的文件。通过合理配置这些选项,可以优化Spark Structured Streaming在处理文件数据时的性能和行为。
File监控数据源案例分析
- 创建程序生成JSON格式的file源测试数据 pyspark_filesourcetest.py
# -*- coding: utf-8 -*-
# @Time : 2024/11/28 21:07
# @Author : pblh123@126.com
# @File : pyspark_filesourcetest.py
# @Describe : 产生测试用的文件
# 导入需要用到的模块
import os
import shutil
import random
import time
from tqdm import tqdm # 导入tqdm库
TEST_DATA_TEMP_DIR = r'D:\PycharmProjects\2024\pyspark\chapter8\tmp\'
TEST_DATA_DIR = r'D:\PycharmProjects\2024\pyspark\chapter8\testdata\'
ACTION_DEF = ['login', 'logout', 'purchase']
DISTRICT_DEF = ['fujian', 'beijing', 'shanghai', 'guangzhou']
JSON_LINE_PATTERN = '{{"eventTime": {}, "action": "{}", "district": "{}"}}\n'
# 测试的环境搭建,判断文件夹是否存在,如果存在则删除旧数据,并建立文件夹
def test_setUp():
if os.path.exists(TEST_DATA_DIR):
shutil.rmtree(TEST_DATA_DIR, ignore_errors=True)
os.mkdir(TEST_DATA_DIR)
# 测试环境的恢复,对文件夹进行清理
def test_tearDown():
if os.path.exists(TEST_DATA_DIR):
shutil.rmtree(TEST_DATA_DIR, ignore_errors=True)
# 生成测试文件
def write_and_move(filename, data):
with open(TEST_DATA_TEMP_DIR + filename, "wt", encoding="utf-8") as f:
f.write(data)
shutil.move(TEST_DATA_TEMP_DIR + filename, TEST_DATA_DIR + filename)
if __name__ == '__main__':
test_setUp()
for i in tqdm(range(1000), desc='Generating files'): # 使用tqdm显示进度条
filename = 'e-mall-{}.json'.format(i)
content = ''
rndcount = list(range(100))
random.shuffle(rndcount)
for _ in rndcount:
content += JSON_LINE_PATTERN.format(
str(int(time.time())),
random.choice(ACTION_DEF),
random.choice(DISTRICT_DEF))
write_and_move(filename, content)
time.sleep(1) # 模拟文件生成时间
test_tearDown()
- 创建程序对数据进行统计
# -*- coding: utf-8 -*-
# @Time : 2024/11/28 21:21
# @Author : pblh123@126.com
# @File : pyspark_sparkSSFilesource.py
# @Describe : structuredStreaming文件源
import os
import shutil
from pprint import pprint
from pyspark.sql import SparkSession
from pyspark.sql.functions import window, asc
from pyspark.sql.types import StructType, StructField, TimestampType, StringType
from utils.window_Utils import windows_enviroment_set
# 解决Caused by: java.net.SocketTimeoutException: Accept timed out问题
# 建议使用配置文件或环境变量管理工具来设置这些环境变量
windows_enviroment_set()
# 定义JSON文件的路径常量,建议使用相对路径或配置文件管理
TEST_DATA_DIR_SPARK = r'D:\PycharmProjects\2024\pyspark\chapter8\testdata'
def main():
"""
主函数,用于执行结构化流处理任务。
本函数尝试创建SparkSession,设置日志级别,定义数据模式,
读取JSON数据流,进行窗口化聚合,并输出结果。
在发生异常时,打印错误信息,并确保在结束时正确停止SparkSession。
""" try:
# 创建SparkSession
spark = SparkSession.builder \
.appName("PythonStructureStreaming_spark341") \
.master("local[2]") \
.getOrCreate()
# 设置日志级别
spark.sparkContext.setLogLevel("WARN")
# 定义模式,为时间戳类型的eventTime、字符串类型的操作和省份组成
schema = StructType([
StructField("eventTime", TimestampType(), True),
StructField("action", StringType(), True),
StructField("district", StringType(), True)])
# 读取JSON格式文件流,应用定义的模式,并设置每次触发时最多读取的文件数
lines = spark \
.readStream \
.format("json") \
.schema(schema) \
.option("maxFilesPerTrigger", 100) \
.load(TEST_DATA_DIR_SPARK)
# 定义窗口
windowDuration = '1 minutes'
# 对读取的数据进行过滤、窗口化分组聚合,并按窗口排序
windowedCounts = lines \
.filter("action = 'purchase'") \
.groupBy('district', window('eventTime', windowDuration)) \
.count() \
.sort(asc('window'))
# 配置并启动查询,输出聚合结果,设置触发器为每10秒处理一次
query = windowedCounts \
.writeStream \
.outputMode("complete") \
.format("console") \
.option('truncate', 'false') \
.trigger(processingTime="10 seconds") \
.start()
# 等待查询终止
query.awaitTermination()
except Exception as e:
# 打印异常信息
print(f"An error occurred: {e}")
finally:
# 确保资源正确释放
if 'spark' in locals():
spark.stop()
if __name__ == '__main__':
main()