Spark Streaming

Spark Streaming

1. SparkStreaming概述

Spark Streaming用于流式数据的处理,Spark Streaming有高吞吐量和容错能力强等特点。Spark Streaming支持的数据输入源很多,例如:Kafka、Flume、Twitter、ZeroMQ和简单的TCP套接字等等。数据输入后可以用Spark的高度抽象原语如:map、reduce、join、window等进行运算。而结果也能保存在很多地方,如HDFS,数据库等。另外Spark Streaming也能和MLlib(机器学习)以及Graphx完美融合。

2. DStream概述

Discretized Stream是Spark Streaming的基础抽象,代表持续性的数据流和经过各种Spark原语操作后的结果数据流。在内部实现上,DStream是一系列连续的RDD来表示。每个RDD含有一段时间间隔内的数据----->对数据的操作也是按照RDD为单位来进行的------>计算过程由Spark engine来完成

3. Spark-streaming kafka数据接收两种方式对比

Receiver方式

a、其实是通过zookeeper连接kafka队列;

b、kafka中topic的partition数目与sparkrdd中partition数目没有关系,增加topic的partition数,不会增加spark的处理并行度,仅仅是增加获取数据的Receiver;

c、Receiver方式,先把数据从kafka中读取出来,然后据都存储在Spark Executor的内存中,一旦spark停止运行(如机器崩溃),数据将无法恢复,只有则不开启WAL(write ahead log)机制,及预写日志的方式同步将数据写到分布式系统中,虽然可以恢复,但是效率低,每份数据需要复制两份;

d、API

val kafkaStream = KafkaUtils.createStream (streamingContext, 
     [ZK quorum], [consumer group id], [per-topic number of Kafka partitions to consume])

eg:
// 从Kafka中读取数据
  val kafkaStream = KafkaUtils.createStream(
  ssc,
  "192.168.126.31:2181,192.168.126.32:2181,192.168.126.33:2181", // Kafka集群使用的zookeeper
  "launcher-streaming", // 该消费者使用的group.id
  Map[String, Int]("test" -> 0), // 日志在Kafka中的topic及其分区
  StorageLevel.MEMORY_AND_DISK_SER).map(_._2)  // 获取日志内容

Director方式

a、这是spark1.3引进的新方法,直接从kafka的broker分区中读数据,跳过zookeeper,也没有receiver;

b、kafka中topic的partition与spark rdd的partition一一对应,也就是说增加kafka中topic的partition数目,就增加了spark的并行处理度;

c、当batch任务触发时,由Executor读取数据,并参与到其他Executor的数据计算过程中去。driver来决定读取多少offsets,并将offsets交由checkpoints来维护。该方式不会复制两份数据,因为kafka本身就有高可用,kafka会做数据备份,宕机后,可以利用kafka副本恢复;

d、这种方式数据的offset存在spark的checkpoint中,不然每次重启spark,就会从kafka的最新offset位置读数据,会丢数据;所以,spark需要设置checkpoint,在创建JavaStreamingContext时,建议使用

JavaStreamingContext.getOrCreate(sparkChkDir,newStreamContextFunction());//第一个参数:checkpoint路径;第二个参数,返回新的JavaStreamingContext;

这种方法相较于Receiver方式的优势在于:

  • 简化的并行:在Receiver的方式中我们提到创建多个Receiver之后利用union来合并成一个Dstream的方式提高数据传输并行度。而在Direct方式中,Kafka中的partition与RDD中的partition是一一对应的并行读取Kafka数据,这种映射关系也更利于理解和优化。

  • 高效:在Receiver的方式中,为了达到0数据丢失需要将数据存入Write Ahead Log中,这样在Kafka和日志中就保存了两份数据,浪费!而第二种方式不存在这个问题,只要我们Kafka的数据保留时间足够长,我们都能够从Kafka进行数据恢复。

  • 精确一次:在Receiver的方式中,使用的是Kafka的高阶API接口从Zookeeper中获取offset值,这也是传统的从Kafka中读取数据的方式,但由于Spark Streaming消费的数据和Zookeeper中记录的offset不同步,这种方式偶尔会造成数据重复消费。而第二种方式,直接使用了简单的低阶Kafka API,Offsets则利用Spark Streaming的checkpoints进行记录,消除了这种不一致性。

      eg:
      val brokers = "192.168.126.31:9092, 192.168.126.32:9092, 192.168.126.33:9092"
      val topics = "test"
      
      val sparkconf = new SparkConf().setAppName("kafkastreaming") 
      val ssc = new StreamingContext(sparkconf,Seconds(5))
      
      ssc.checkpoint("192.168.126.31:9000/ck")  //checkpoint 路径
      
      val topicSet = topics.split(",").toSet
      val kafkaParams = Map[String, String]("metadata.broker.list" -> brokers)
      
      val lines = KafkaUtils.createDirectStream[String, String,StringDecoder, StringDecoder](ssc,kafkaParams,topicSet)
      
      val message = lines.map(_._2) //map(_._2) 才是Kafka里面打入的数据
    

4. Dstream 的分类

  1. DStream 中 有一个HashMap[Time,RDD[T]]类型的对象 generatedRDDs,其中Key为作业开始时间,RDD为该DStream对应的RDD

  2. Dstream 主要分为三大类:
    Input DStream
    Transformed DStream
    Output DStream

  3. Transformed DStream

     DStreams支持许多用在一般Spark RDD上的转换。其中一些常用的如下: 
    
     map(func):将源DStream中的每个元素通过一个函数func从而得到新的DStreams。 
    
     flatMap(func):和map类似,但是每个输入的项可以被映射为0或更多项。 
    
     filter(func):选择源DStream中函数func判为true的记录作为新DStreams 
    
     repartition(numPartitions):通过创建更多或者更少的partition来改变此DStream的并行级别。
    
     union(otherStream):联合源DStreams和其他DStreams来得到新DStream 
    
     count:统计源DStreams中每个RDD所含元素的个数得到单元素RDD的新DStreams。 
    
     reduce(func):通过函数func(两个参数一个输出)来整合源DStreams中每个RDD元素得到单元素RDD的DStreams。这个函数需要关联从而可以被并行计算。 
    
     countByValue:对于DStreams中元素类型为K调用此函数,得到包含(K,Long)对的新DStream,其中Long值表明相应的K在源DStream中每个RDD出现的频率。 
    
     reduceByKey(func, [numTasks]):对(K,V)对的DStream调用此函数,返回同样(K,V)对的新DStream,但是新DStream中的对应V为使用reduce函数整合而来。Note:默认情况下,这个操作使用Spark默认数量的并行任务(本地模式为2,集群模式中的数量取决于配置参数spark.default.parallelism)。你也可以传入可选的参数numTaska来设置不同数量的任务。 
     join(otherStream,[numTasks]):两DStream分别为(K,V)和(K,W)对,返回(K,(V,W))对的新DStream。 
    
     cogroup(otherStream,[numTasks]):两DStream分别为(K,V)和(K,W)对,返回(K,(Seq[V],Seq[W])对新DStreams 
    
     transform(func):将RDD到RDD映射的函数func作用于源DStream中每个RDD上得到新DStream。这个可用于在DStream的RDD上做任意操作。 
    
     updateStateByKey(func):得到”状态”DStream,其中每个key状态的更新是通过将给定函数用于此key的上一个状态和新值而得到。这个可用于保存每个key值的任意状态数据。 
    
  4. Output DStream

foreachRDD

foreachRDD通常用来把SparkStream运行得到的结果保存到外部系统比如HDFS、Mysql、Redis等等,一个分区使用一个连接池来维护连接对象。

dstream.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
    // ConnectionPool is a static, lazily initialized pool of connections
    val connection = ConnectionPool.getConnection()
    partitionOfRecords.foreach(record => connection.send(record))
    ConnectionPool.returnConnection(connection)  // return to the pool for future reuse
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值