环境
kafka_2.11-0.10.0.1
hadoop-2.6.0-cdh5.7.0
spark-2.2.0-bin-2.6.0-cdh5.7.0
Receiver方式
- 环境不合适,只能简答描述特点
- 该方式只能为0-8版本到之后可以使用,到0-10版本就不好使了
- 构造函数中的numThreads参数,对应提高sparkstreaming的并行度并没有关系,提高只有kafka的分区数才能提高并行度,增加读写速度
- 这种情况可能会出现数据丢失问题,在driver端挂掉,Executor重启的时候必然会丢数据,在1.2时增加了一个Write Ahead Logs的机制(在写到Receiver时,写到一个日志中,通常为HDFS),这个机制可以避免数据丢失,但是写到HDFS或者文件系统必然会降低吞吐量。同时启动Write Ahead Logs时,存储结构设置为StorageLevel.MEMORY_AND_DISK_SER,不需要多副本,因为HDFS就是多副本机制
- 由于应用WAL,所以语义为至少一次
Direct Approach方式
1.没有Recevier,没有Recevier,没有Recevier,重要的事情说三遍
2.简化并行度:不需要指定numThreads的参数,RDD的分区直接与kafka的分区相等,提高并行度,加大分区。
3.零数据丢失,吞吐量更好,直接从kafka分区读取
4.当offset(偏移量)还没有写入kafka,driver就挂了,可能会导致重复写入,也就是会产生至少一次的语义。可以把偏移量交给sparkStreaming的checkpoint来维护,这样就会达到只有一次的效果。
5.缺点:在ssc中维护偏移量,那么kafka界面则无法显示,所以可以将偏移量重新写入到zk中,如下:
var offsetRanges = Array.empty[OffsetRange]
directKafkaStream.transform { rdd =>
offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
rdd
}.map {
...
}.foreachRDD { rdd =>
for (o <- offsetRanges) {
println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
}
...
}
代码实现Direct 方式对接SparkStreaming
环境准备:
1.启动ZK
zkServer.sh start
2.启动kafka
nohup kafka-server-start.sh config/server.properties &
3.创建kafka的blocker
kafka-topics.sh --create \
--zookeeper localhost:2181 \
--replication-factor 1 --partitions 1 --topic topicD
4.启动生产者
kafka-console-producer.sh --broker-list localhost:9092 --topic onlinelogs
5.启动消费者
kafka-console-consumer.sh \
--zookeeper localhost:2181 \
--from-beginning --topic topicD
代码实现:
object DkafkatoStreaming {
def main(args: Array[String]) {
val sparkconf=new SparkConf().setAppName("project").setMaster("local")
val ssc=new StreamingContext(sparkconf,Seconds(5))
val kafkaParams = Map[String, Object](
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG ->"hadoop:9092",
ConsumerConfig.GROUP_ID_CONFIG -> "test",
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer],
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer])
ssc.checkpoint("hdfs://hadoop:9000/data")
val topics = Array("topicD")
val stream = KafkaUtils.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String, String](topics, kafkaParams))
val lines=stream.map(_.value)
val word=lines.flatMap(_.split(",")).map(x=>(x,1)).reduceByKey(_+_).print
ssc.start()
ssc.awaitTermination()
}
}
查看测试结果: