spark structured stream的Append模式例子

本例子实现一个从源kafka消费消息进行分组聚合后重新输出到目的kafka的例子,参见代码:

from pyspark import SparkConf
from pyspark.sql import SparkSession
import traceback
# import builtins as py_builtin
from pyspark.sql.functions import max
from pyspark.sql.functions import desc
from pyspark.sql.types import StructField, StructType, StringType, LongType
from pyspark.sql.types import *
from pyspark.sql.functions import col, column, expr
from pyspark.sql.functions import *
from pyspark.sql import Row

appname = "test"  # 任务名称
master = "local[*]"  # 单机模式设置
'''
local: 所有计算都运行在一个线程当中,没有任何并行计算,通常我们在本机执行一些测试代码,或者练手,就用这种模式。
local[K]: 指定使用几个线程来运行计算,比如local[4]就是运行4个worker线程。通常我们的cpu有几个core,就指定几个线程,最大化利用cpu的计算能力
local[*]: 这种模式直接帮你按照cpu最多cores来设置线程数了。
'''
# spark_driver_host = "10.0.0.248"

try:
    conf = SparkConf().setAppName(appname). \
        set('spark.jars.packages', 'org.apache.spark:spark-sql-kafka-0-10_2.11:2.3.0') \
        .set("spark.jars.repositories", 'http://maven.aliyun.com/nexus/content/groups/public/') \
        .setMaster(master)  # 本地
    spark = SparkSession.builder.config(conf=conf).getOrCreate()
    df = spark \
        .readStream \
        .format("kafka") \
        .option("kafka.bootstrap.servers", "localhost:9092") \
        .option("startingOffsets", "latest") \
        .option("subscribe", "mykafkatest") \
        .load()
    words = df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)", "offset", "timestamp")
    schema = StructType() \
        .add("topic", StringType()) \
        .add("age", StringType()) \
 \
    # 通过from_json,定义schema来解析json
    # res = words.where(instr("value", '{') ==0).select(from_json("value", schema).alias("data")).select("data.*")
    streamSource = words.where(instr("value", 'topic') > 0).select(from_json("value", schema).alias("data"),
                                                                   col("timestamp")) \
        .select("data.*", 'timestamp')
    streamSource = streamSource.select(col('age').cast('int').alias('age'), col('topic'), col('timestamp'))

    windowedCounts = streamSource.withWatermark("timestamp", "10 seconds") \
        .groupBy(window(col("timestamp"), '10 seconds', '10 seconds'), col("topic")).count()

    # query = windowedCounts \
    #     .writeStream \
    #     .outputMode('complete') \
    #     .format('console') \
    #     .option('truncate', 'false') \
    #     .start()
    res = windowedCounts.withColumn('constfield', lit('1'))
    query = res.select(
        to_json(struct("topic", "window")).cast('string').alias("key"),
        to_json(struct("topic", "window", "count")).cast('string').alias("value"))\
        .writeStream \
        .format("kafka") \
        .option("kafka.bootstrap.servers", "localhost:9092") \
        .option("topic", "mykafkatestdest") \
        .option("checkpointLocation", '''D:\spark\spark\spark-2.3.0-bin-hadoop2.7\checkpoint\kafkatest2''') \
        .trigger(processingTime='3 seconds') \
        .start()
    query.awaitTermination()

    print('计算成功!')
except:
    traceback.print_exc()  # 返回出错信息
    print('连接出错!')

运行结果:
在这里插入图片描述

结论:写入kafka sink时使用的是默认的append输出模式,也就是窗口的信息输出是等到对应的wartermark达到时才会输出的,此时也会顺便清理state,防止state过大。不过在这个例子中也发现一个问题:最后的几条消息一直在等待wartermark的到来,现象就是最后几条消息一直没有聚合输出,不论等待了多少个trigger触发时间,除非再次输入新的几条消息才输出,然而这样新的这几条消息又会一直再次等待wartermark的到达,—这里感觉有点问题,如果只是最后一条消息等待wartermark这能理解,因为append输出模式的语义来说未确定wartermark之前确实不能输出,但是有几条小子都在等待wartermark这里就很难理解了,难道是不同的执行器还有自己的wartermark?需要每个执行器都等到了自己的wartermark才触发?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值