【问题标题】:How to pass SparkSession object to Kafka-Spark streaming's foreachBatch method?如何将 SparkSession 对象传递给 Kafka-Spark 流的 foreachBatch 方法?
【发布时间】:2022-07-02 20:40:48
【问题描述】:

我有一个 python 脚本loader.py,它由创建sparkSession 对象的主类组成​​,如下所示,并调用各种方法来执行不同的操作。 from utils import extract_kafka_data, do_some_transformation

def main():
    try:
        spark = SparkSession.builder.appName(config['kafka_transformations']).enableHiveSupport().getOrCreate()
        kafka_df = extract_kafka_data(spark=spark, config=config, topic_name=topic_name)
        do_some_transformation(kafka_df, spark)
    except Exception as exc:
        print(f'Failed with Exception:{exc}')
        traceback.print_exc()
        print('Stopping the application')
        sys.exit(1)


if __name__ == '__main__':
    main()

extract_kafka_data、do_some_transformation 方法存在于不同的 Python 脚本中:utils.py 我的 utils.py 文件中还有很多其他方法可以执行各种转换。以下是该场景中需要解决的几种方法。

def extract_kafka_data(spark: SparkSession, config: dict, topic_name: str):
    jass_config = config['jaas_config'] + " oauth.token.endpoint.uri=" + '"' + config['endpoint_uri'] + '"' + " oauth.client.id=" + '"' + config['client_id'] + '"' + " oauth.client.secret=" + '"' + config['client_secret'] + '" ;'
    stream_df = spark.readStream \
        .format('kafka') \
        .option('kafka.bootstrap.servers', config['kafka_broker']) \
        .option('subscribe', topic_name) \
        .option('kafka.security.protocol', config['kafka_security_protocol']) \
        .option('kafka.sasl.mechanism', config['kafka_sasl_mechanism']) \
        .option('kafka.sasl.jaas.config', jass_config) \
        .option('kafka.sasl.login.callback.handler.class', config['kafka_sasl_login_callback_handler_class']) \
        .option('startingOffsets', 'earliest') \
        .option('fetchOffset.retryIntervalMs', config['kafka_fetch_offset_retry_intervalms']) \
        .option('fetchOffset.numRetries', config['retries']) \
        .option('failOnDataLoss', 'False') \
        .option('checkpointLocation', checkpoint_location) \
        .load() \
        .select(from_json(col('value').cast('string'), schema).alias("json_dta")).selectExpr('json_dta.*')
    return stream_df

def do_some_transformation(spark: SparkSession, kafka_df: Dataframe):
    kafka_df.writeStream \
        .format('kafka') \
        .foreachBatch(my_transformation_method) \
        .option('checkpointLocation', checkpoint_location) \
        .trigger(processingTime='10 minutes') \
        .start()
        .awaitTermination()

def my_transformation_method(kafka_df: Dataframe, batch_id: int):
    base_delta = DeltaTable.forPath(spark, config['delta_path'])
    base_delta.alias("base") \
        .merge(source=kafka_df.alias("inc"), condition=build_update_condition(config['merge_keys'], config['inc_keys'])) \
        .whenMatchedUpdateAll() \
        .whenNotMatchedInsertAll() \
        .execute()

我在这里面临的问题是方法:my_transformation_method。 内部方法:my_transformation_method 我正在将我的 kafka 数据帧与我的增量表合并。 为了读取基表数据,我需要运行以下语句: base_delta = DeltaTable.forPath(spark, config['delta_path']) 但是这里的问题是方法:foreachBatchdo_some_transformation 方法中调用的方法:my_transformation_method 只能接收两个方法参数:1. Dataframe 2. batch_id 按照火花流的语法。

我可以将 spark 会话对象设为全局,但我不想这样做,因为它似乎不是标准方式。 当我从 do_some_transformation 调用它时,有什么方法可以使 sparkSession 对象 spark 可用于方法 my_transformation_method ? 非常感谢任何帮助。

【问题讨论】:

  • 您可以传递任意数量的参数...您是否正在寻找类似foreachBatch(lambda (df, id): my_transformation_method(spark, df, id)) 的东西?或者,如果您的配置从不更改,为什么不在该函数之外定义 base_delta
  • foreachBatch(lambda (df, id): my_transformation_method(spark, df, id)) 这行不通,因为数据帧被分成更小的批次,并且数据帧中的那批数据被传递。所以我不能像这样将kafka_df 作为参数传递给my_transformation_methodkafka_df.writeStream.format('kafka') .foreachBatch(lambda df, id: my_transformation_method(spark, kafka_df, id)) \ .option('checkpointLocation', checkpoint_location) \ .trigger(processingTime='10 minutes') \ .start() .awaitTermination()
  • 不确定“我可以将 spark 会话对象设为全局,但我不想这样做,因为它似乎不是标准方式”是什么意思。在 Spark 应用程序中,您通常有一个“session.py”或您定义“spark = SparkSession.builder.config(conf=spark_conf).getOrCreate()”的任何模块,它是一个单例并在需要的地方导入/使用。 "from myapp.session import spark" 有些人使用 DI 库或框架,但干净的代码绝对不需要。
  • 这是因为有近 20 种其他方法接收 spark session 作为参数。我不想在每个 .py 脚本中创建一个 SparkSesison,或者只是在每个方法中创建全局变量来初始化它们并使脚本混乱。
  • 嗯,实际上就是这样:你没有。以 numpy 为例:“import numpy as np”。您不会在每个方法中将“np”作为参数传递,您只需在代码中使用“np.method”。 Spark 会话也不例外。

标签: apache-spark pyspark apache-kafka spark-structured-streaming


【解决方案1】:

DataFrame API ptovides 可以使用的 sparkSession 方法:

spark = kafka_df.sparkSession()

【讨论】:

    猜你喜欢
    • 2020-03-08
    • 2017-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 2023-02-22
    • 2023-03-09
    相关资源
    最近更新 更多