【发布时间】: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'])
但是这里的问题是方法:foreachBatch 在do_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_method:kafka_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