基于pyspark3.4.1的Structured Streaming进行文件源的实时监控

141 阅读4分钟

基于pyspark3.4.1的Structured Streaming进行文件源的实时监控

在Spark Structured Streaming中,File源支持多种文件格式,包括text、csv、json、orc、parquet等。以下是一些常用的File源选项及其解释:

  1. path:指定输入目录的路径,这是所有文件格式共有的选项。
  2. maxFilesPerTrigger:每个触发器(trigger)中考虑的新文件的最大数量,默认没有最大值限制。
  3. latestFirst:是否首先处理最新的文件,这在有大量积压文件时非常有用,默认值为false。
  4. fileNameOnly:是否仅基于文件名而不是完整路径来检查新文件,默认值为false。如果设置为true,那么即使文件路径不同,只要文件名相同,就会被认为是同一个文件。
  5. maxFileAge:可以找到的文件的最大年龄,超过这个年龄的文件将被忽略。对于第一个批次,所有文件都被认为是有效的。如果latestFirst设置为true且maxFilesPerTrigger被设置,那么这个参数将被忽略,因为可能会忽略掉应该被处理的有效旧文件。默认值为1周。
  6. cleanSource:处理完成后如何清理文件。可用选项有"archive"、"delete"、"off"。如果未提供此选项,默认值为"off"。如果选择"archive",还需要提供sourceArchiveDir选项,该值不能与源模式在深度上匹配,以确保归档的文件不会被包括为新的源文件。
  7. sourceArchiveDir:当cleanSource设置为"archive"时,需要提供此选项,用于指定归档文件的目录。

这些选项可以控制文件源的行为,例如如何处理新文件、旧文件,以及如何处理已经处理过的文件。通过合理配置这些选项,可以优化Spark Structured Streaming在处理文件数据时的性能和行为。

File监控数据源案例分析

  1. 创建程序生成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()

image.png

  1. 创建程序对数据进行统计
# -*- 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()

image.png