kafka window 启动_Spark+Kafka+WebSocket+eCharts实时分析-完全记录(3)

本系列内容:

  1. Kafka环境搭建与测试
  2. Python生产者/消费者测试
  3. Spark接收Kafka消息处理,然后回传到Kafka
  4. Flask引入消费者
  5. WebSocket实时显示

版本:

spark-2.4.3-bin-hadoop2.7.tgz

kafka_2.11-2.1.0.tgz

------------------第3小节:Spark接收Kafka消息处理,然后回传到Kafka--------------------

import sys

from pyspark.sql.types import IntegerType

from pyspark.sql import SparkSession
from pyspark.sql.functions import explode, concat, array_join, concat_ws
from pyspark.sql.functions import split
from pyspark.sql.functions import window

if __name__ == "__main__":
    # broker地址
    bootstrapServers = "192.168.147.128:9092"
    # subscribe:订阅
    subscribeType = "subscribe"
    # 主题
    topics = "bigdata"
    # 窗口大小:30秒
    windowSize = 30
    # 滑动窗口大小:15秒
    slideSize = 15
    windowDuration = '{} seconds'.format(windowSize)
    slideDuration = '{} seconds'.format(slideSize)

    spark = SparkSession.builder.appName("KafkaWordCount").getOrCreate()

    # 读取流数据,并生成dataframe
    # spark获取到的流数据,将放到这个dataframe中的value列
    # dataframe包含:key、value、topic、partition、offset、timestamp、timestampType
    # 这些列成为dataframe元素数据
    # value:是二进制的字节数组,在使用时需要转为字符串
    lines = spark.readStream.format("kafka"). 
        option("kafka.bootstrap.servers", bootstrapServers). 
        option("subscribe", topics). 
        option('includeTimestamp', 'true').load()

    # lines:中value列收到的数据格式如下:
    # value = b'火车北站,11,25,47,56,70,83,84'
    # 现在需要将这些数据进行分割,每一个数据作为一个单独的列
    # 这里构造的words 也是一个dataframe,并且包含时间戳
    # words结构转换为:["che_zhan","zhou1","zhou2"。。。。。] ===> ["车站","周一","周二"。。。。。]
    words = lines.select(
        split(lines.value, ',').getItem(0).alias('che_zhan'),
        split(lines.value, ',').getItem(1).cast("int").alias('zhou1'),
        split(lines.value, ',').getItem(2).cast("int").alias('zhou2'),
        split(lines.value, ',').getItem(3).cast("int").alias('zhou3'),
        split(lines.value, ',').getItem(4).cast("int").alias('zhou4'),
        split(lines.value, ',').getItem(5).cast("int").alias('zhou5'),
        split(lines.value, ',').getItem(6).cast("int").alias('zhou6'),
        split(lines.value, ',').getItem(7).cast("int").alias('zhou7'),
        lines.timestamp
    )
    words.printSchema()

    # 根据时间窗口的数据,按车站名称进行分组,统计每天的客流量
    # windowedCounts结构转换为:["che_zhan","sum(zhou1)","sum(zhou2)"。。。。。]
    windowedCounts = words.groupBy(
        window(words.timestamp, windowDuration, slideDuration),
        words.che_zhan
    ).sum('zhou1', 'zhou2', 'zhou3', 'zhou4', 'zhou5', 'zhou6', 'zhou7')

    # windowedCounts中的列sum(zhou1),sum(zhou2)等列进行重命名
    windowedCounts = windowedCounts.withColumnRenamed('sum(zhou1)', 'zhou1'). 
        withColumnRenamed('sum(zhou2)', 'zhou2'). 
        withColumnRenamed('sum(zhou3)', 'zhou3'). 
        withColumnRenamed('sum(zhou4)', 'zhou4'). 
        withColumnRenamed('sum(zhou5)', 'zhou5'). 
        withColumnRenamed('sum(zhou6)', 'zhou6'). 
        withColumnRenamed('sum(zhou7)', 'zhou7')
    windowedCounts.printSchema()

    # 将数据输出到控制台。这种方式不支持在dataframe中使用聚合函数。这里做个备注
    # query = windowedCounts.writeStream.outputMode('append').format('console').start()
    # query.awaitTermination()

    # 注意:这里是重点,将数据写回kafka
    # windowedCounts的所有列组合在一起,作为一个新的列value
    # 为什么要这么做,以及注意事项见下图
    last_df = windowedCounts.select(concat_ws(",", windowedCounts.che_zhan, windowedCounts.zhou1,
                                              windowedCounts.zhou2, windowedCounts.zhou3,
                                              windowedCounts.zhou4, windowedCounts.zhou5,
                                              windowedCounts.zhou6, windowedCounts.zhou7).alias("value"))
    last_df.printSchema()
    # 将df输出到kafka
    # outputMode:取值有 complete、update、Append
    #        complete:当dataframe结果表有更新时,全部输出到外部设备
    #        update:当dataframe结果表有更新时,受影响的行输出到外部设备
    #        Append:当dataframe结果表有更新时,把新加入的行输出到外部设备
    #        操作中不存在聚合函数,那么受影响的只有新行,这时update和Append效果一样
    # checkpointLocation:在流式处理中,在某一时刻出现故障、掉电等意外情况,流式计算就被中断。
    #        当修复故障重新启动计算时,流式处理引擎可以从中断的位置继续计算。这是使用检查点和预写日志
    #        的方式搞定的。这个检查点必须是HDFS兼容文件系统中的路径。为什么必须设计检查点,参见官方文档:
    #        http://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#starting-streaming-queries
    #        Starting Streaming Queries 这一小节
    # topic: 将生成的结果输出到 result 主题
    query = last_df 
        .writeStream 
        .outputMode("update") 
        .format("kafka") 
        .option("checkpointLocation", "hdfs://localhost:9000/struct_streaming/checkpoint4") 
        .option("kafka.bootstrap.servers", bootstrapServers) 
        .option("topic", "result") 
        .start()
    query.awaitTermination()

92d7cd3d09660f2a324a93d21a91bb48.png

程序编写完成,放到centos系统,使用如下命令运行:

步骤01:启动hdfs,因为检查点在hdfs上

./sbin/start-dfs.sh

步骤02:提交Spark应用

bin/spark-submit /tools/structured_kafka_wordcount.py 192.168.147.128:9092 subscribe bigdata

出现错误如下:

py4j.protocol.Py4JJavaError: An error   occurred while calling o117.start.
:   org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.hdfs.server.namenode.SafeModeException):   Cannot create directory /struct_streaming/checkpoint4. Name node is in safe mode.
The reported blocks 23169 needs   additional 824 blocks to reach the threshold 0.9990 of total blocks 24018.

退出安全模式

hdfs dfsadmin -safemode leave

重新提交,其中:

第1个root:words的结构,此时已经构造好了列

第2个root:windowedCounts的结构,这是滑动窗口调用聚合函数构造的列

第3个root:last_df的结构,这是合并列后,将要放入kafka的dataframe

注意,日志打印到这里,若是不包错就不会继续打印了。这种状态就是正确的,这个spark算子将作为一个常住服务运行,直到你终止。

46277dbb81ee21fa779192b7e8b3f21f.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值