结构化编程模型
输出终端/位置
默认情况下,Spark的结构化流支持多种输出方案:
1- console sink: 将结果数据输出到控制台。主要是用在测试中,并且支持3种输出模式
2- File sink: 输出到文件。将结果数据输出到某个目录下,形成文件数据。只支持append模式
3- foreach sink 和 foreachBatch sink: 将数据进行遍历处理。遍历后输出到哪里,取决于自定义函数。并且支持3种输出模式
4- memory sink: 将结果数据输出到内存中。主要目的是进行再次的迭代处理。数据大小不能过大。支持append模式和complete模式
5- Kafka sink: 将结果数据输出到Kafka中。类似于Kafka中的生产者角色。并且支持3种输出模式
File Sink
import os
from pyspark.sql import SparkSession
import pyspark.sql.functions as F
# 绑定指定的Python解释器
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'
if __name__ == '__main__':
# 1- 创建SparkSession对象
spark = SparkSession.builder\
.config("spark.sql.shuffle.partitions",1)\
.appName('file_sink')\
.master('local[1]')\
.getOrCreate()
# 输出到文件必须要设置checkpointLocation检查点路径
spark.conf.set("spark.sql.streaming.checkpointLocation", "hdfs://node1:8020/chk")
# 2- 数据输入
init_df = spark.readStream\
.format("socket")\
.option("host","192.168.88.161")\
.option("port","55555")\
.load()
# 3- 数据处理
# 4- 数据输出
# 5- 启动流式任务
"""
File Sink总结:
1- 输出到文件必须要设置checkpointLocation检查点路径
2- 因为结构化流底层是微批处理,如果不手动指定处理间隔,程序会尽可能缩短两个批处理间的间隔。那么会导致小文件问题的产生
3- 我们可以使用触发器trigger来指定批处理的时间间隔,用来减少小文件的产生
"""
init_df.writeStream\
.format("csv")\
.outputMode("append")\
.option("sep",",")\
.option("header","True")\
.option("encoding","UTF-8")\
.trigger(processingTime="20 seconds")\
.start("file:///export/data/child")\
.awaitTermination()
file sink总结:
1- 要设置检查点数据存放路径。否则会报如下错误
AnalysisException: checkpointLocation must be specified either through
2- 因为结构化流底层是微批处理,如果不手动指定处理间隔,程序会尽可能缩短两个批处理间的间隔。那么会导致小文件问题的产生
3- 可以通过触发器trigger解决小文件的问题。可以通过触发器来调整每一批次产生间隔的时间
4- 支持输出到本地文件系统和HDFS文件系统
foreach sink
允许对输出的数据进行任意的处理操作,具体如何处理由用户自定义函数决定。对输出的数据一个个进行处理操作。
使用方式主要有二种
方式一:
import os
from pyspark.sql import SparkSession
import pyspark.sql.functions as F
# 绑定指定的Python解释器
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'
if __name__ == '__main__':
# 1- 创建SparkSession对象
spark = SparkSession.builder\
.config("spark.sql.shuffle.partitions",1)\
.appName('foreach_sink')\
.master('local[*]')\
.getOrCreate()
# 2- 数据输入
init_df = spark.readStream\
.format("socket")\
.option("host","192.168.88.161")\
.option("port","55555")\
.load()
# 3- 数据处理
# 4- 数据输出
# 5- 启动流式任务
"""
每输入一条数据都会调用foreach中的函数。
取Row中某个字段的值:
方式一 row.字段名
方式二 row['字段名']
"""
def my_foreach_func(row):
# 打开数据库连接
# 存储到数据库中
print(row,row.value)
print(row['value'])
# 关闭数据库连接
init_df.writeStream.foreach(my_foreach_func).outputMode("append").start().awaitTermination()
方式二:这种方式的适用场景是需要和资源打交道的情况(例如:连接和关闭数据库、打开关闭文件等)。通过open和close来处理资源,通过process来对数据进行自定义的处理
import os
from pyspark.sql import SparkSession
import pyspark.sql.functions as F
# 绑定指定的Python解释器
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'
if __name__ == '__main__':
# 1- 创建SparkSession对象
spark = SparkSession.builder\
.config("spark.sql.shuffle.partitions",1)\
.appName('foreach_sink')\
.master('local[*]')\
.getOrCreate()
# 2- 数据输入
init_df = spark.readStream\
.format("socket")\
.option("host","192.168.88.161")\
.option("port","55555")\
.load