spark streaming小实战之kafka读取与存储

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37637511/article/details/80330358

本次小实战主要介绍一下spark streaming如何读取kafka数据

涉及理论部分在这就不多说了,自己也刚入门

先说下需求

待处理日志格式为

ouMrq2r_aU1mtKRTmQclGo1UzY,3251210381,2018/11/29 13:46,上海,上海,210.2.2.6,7038004
ouMrq2r_aU1mtKRTmQclGo1UzY,3251210381,2018/09/18 08:37,上海,上海,210.2.2.6,7038004
ouMrq2r_aU1mtKRTmQclGo1UzY,3251210381,2018/02/19 01:16,上海,上海,210.2.2.6,7038004

需要做的是统计每隔5分钟内被访问的数量

数据从kafka中读出,通过spark streaming处理,然后再写会kafka

接下来将从两部分入手说明项目完成过程

一、模拟kafka流

二、spark streaming处理最后写回kafka


第一部分

目的:模拟真实kafka流情况

思路:使用kafka的connect监听source文件,如果发生修改,写入topic

先打开zookeeper,kafka

接着进入kafka目录下,使用如下命令开启connect

bin/connect-standalone.sh config/connect-standalone.properties config/connect-file-source.properties

监听的文件位置在  config/connect-file-source.propertie 中配置

打开该文件,内容如下

name=local-file-source
connector.class=FileStreamSource
tasks.max=1
file=/Users/huangxiao/test_streaming/xbk_log.txt#监听的文件位置
topic=kafka_for_example#topic名称(需要与spark中的一致)

connect运行成功如下



第二部分

直接看spark streaming的程序吧,用scala写的,写了很多注释,就不多说

import java.sql.Date
import java.text.SimpleDateFormat
import java.util.Properties
import kafka.serializer.StringDecoder
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.{Duration, StreamingContext}
import org.apache.spark.streaming.kafka.KafkaUtils
import com.alibaba.fastjson.JSON
import kafka.producer.ProducerConfig
import kafka.producer.Producer
import kafka.producer.KeyedMessage
import org.apache.spark.{SparkConf, SparkContext}
//1.打开zk,kafka。2.启动kafka-connect(source部分)3.运行此文件
object Kafka_Spark {
  def main(args: Array[String]) {
    val sparkConf = new SparkConf().setMaster("local[2]").setAppName("kafka-spark-demo")
    val scc = new StreamingContext(sparkConf, Duration(5000))//new一个spark-streaming的上下文

    //    scc.checkpoint(".") // 暂时用不到
    val topics = Set("kafka_spark2") //我们需要消费的kafka数据的topic
    val kafkaParam = Map(
      "metadata.broker.list" -> "localhost:9092", // kafka的broker list地址
      "auto.offset.reset" -> "smallest"//这个参数可以让streaming消费topic的时候从头开始消费
    )
    val stream: InputDStream[(String, String)] = createStream(scc, kafkaParam, topics)//建立流,读数据,传入上下文,kafka配置,主题名字
    val wordCount = stream.map(l => (json_an(l._2), 1)).reduceByKey(_ + _) //对数据进行处理
    wordCount.print()//输出到控制台看看结果
    
    //发送数据(对外部服务器连接必须要用这种方式,不然会报错:任务无法序列化)
    wordCount.foreachRDD { rdd =>
      rdd.foreachPartition { partitionOfRecords =>
        //配置说明
        val producerProperties = new Properties()
        producerProperties.put("serializer.class", "kafka.serializer.StringEncoder")
        producerProperties.put("metadata.broker.list", "localhost:9092")
        producerProperties.put("request.required.acks", "1")
        val config: ProducerConfig = new ProducerConfig(producerProperties)
        //与kafka进行连接。此处用的是kafka自家的Producer,用spark的kafkaproducer也可以,但传送的方式不同
        val producer = new Producer[String,String](config)
        partitionOfRecords.foreach(record =>
          //发送数据,在这里key简单的用了相同的。实际情况应该用别的
          producer.send(new KeyedMessage("cunchu","key",record.toString()))
        )

      }
    }

    scc.start() // 真正启动程序
    scc.awaitTermination() //阻塞等待
  }
  /**
    * 创建一个从kafka获取数据的流.
    *
    * @param scc        spark streaming上下文
    * @param kafkaParam kafka相关配置
    * @param topics     需要消费的topic集合
    * @return
    */
  def createStream(scc: StreamingContext, kafkaParam: Map[String, String], topics: Set[String]) = {
    KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](scc, kafkaParam, topics)
  }
  //处理时间
  def formatData(line: String) = {
    val date = new SimpleDateFormat("yyyy/MM/dd H:mm")
    val d = new SimpleDateFormat("yyyy/MM/dd")
    val dateFormated = date.parse(line)
    val dateFormated3 = date.parse(line.split(" ")(0) + " 0:0")

    val dateFormated2 = date.format(dateFormated)
    val dateFormated4 = date.format(dateFormated3)

    val dateFf = date.parse(dateFormated2).getTime
    val dateFf2 = date.parse(dateFormated4).getTime
    val r = dateFf - dateFf2
    val hash = r / 300000
    val final_date = new Date(hash.toInt * 300000 + dateFf2)
    date.format(final_date)
  }

  //字符串处理。在这里是提取时间
  def json_an(str: String) = {
    if (str.length < 10) {
      1
    }
    else {
      val json = JSON.parseObject(str)

      val main_v = json.get("payload")
      if (main_v.toString.split(",").length == 7) {
        formatData(main_v.toString.split(",")(2))
      }
      else {
        "NAN"
      }
    }
  }
}

运行即可


出现Time:xxx的时间戳时说明已经成功运行了

下面我们往文件中加数据看看是否能读到并处理


可以看到处理已经成功

那么我们再检查一下是否成功存储了呢。代码中我写回kafka的topic名字是cunchu

因此我们打开一个消费者看看cunchu中是否有我们处理好的数据

在kafka目录下执行如下命令

bin/kafka-console-consumer.sh -zookeeper localhost:2181 --from-beginning --topic cunchu

结果


大功告成!


展开阅读全文

没有更多推荐了,返回首页