sparkStreaming连接kafak的receiver方式和direct方式
Receiver方式(已废弃)
从kafka的中获取的数据都是储存在sparkexecutor的内存中,然后sparkStreaming启动的job回去处理那些数据
然而,在默认的配置下,这种方式可能会因为底层的失败而丢失数据.
如果要启用高可靠的机制,让数据零丢失,就必须启用sparkStreaming预写日志机制(write ahead log,wal)
该机制会同步地将接收到的kafka数据写入到分布式文件系统中,(比如hdfs)上的预写日志中.
所以,即使底层节点出现了失败,也可能使用预写日志机制中的数据进行恢复,但是效率会下降.
def getReceiver(): Receiver[(K, V)] = {
if (!useReliableReceiver) {
//< 不启用 WAL
new KafkaReceiver[K, V, U, T](kafkaParams, topics, storageLevel)
} else {
//< 启用 WAL
new ReliableKafkaReceiver[K, V, U, T](kafkaParams, topics, storageLevel)
}
}
direct方式
direct这种方式会周期性的查询kafka,来获得每个topic+partition的最新的offset,从而定义每个batch的offter的范围
当处理数据的job启动时,就会使用kafka的简单consumer api来获取kafka指定offer范围的数据.
def main(args: Array[String]): Unit = {
//定义一个appName
val appName = "wordcount"
val groupId = "g10"
//创建一个spark环境,在套一层StreamingContext
val conf = new SparkConf()
.setAppName(appName)
.setMaster("local[*]")
val ssc = new StreamingContext(conf, Milliseconds(5000))
//设置运行时的日志级别
ssc.sparkContext.setLogLevel("WARN")
//创建一个直连的数据流设置主机名,连接方式,和各种参数
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "doit01:9092,doit02:9092,doit03:9092",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "gwc02",
"auto.offset.reset" -> "earliest",//从头开始读
"enable.auto.commit" -> (false: java.lang.Boolean) //不自动提交偏移量,如果不设置,默认为true
)
//这只topics的名字
val topics = Array("wordcount")
val histroyOffsets: Map[TopicPartition, Long] = OffsetUtils.queryHistoryOffsetFromMySQL(appName, groupId)
//创建Kafka数据流
val kafkaDStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent, //位置策略
ConsumerStrategies.Subscribe[String, String](topics, kafkaParams, histroyOffsets) //订阅策略
)
使用direct这种方式有以下的优点
1.简化并行读取
如果kafka中有多个partition,我们不需要创建多个DStream然后union
spark会自动创建跟kafka partition一样多的RDD partition,并且会并行的从kafka中读取数据.
所以kafka partition和RDD partition之间,有一个对一个的映射关系.
2.高性能
如果要保证数据零丢失,在基于receiver的方式中,需要开启WAL(预写日志机制).
这种机制其实效率低下,因为数据实际上被复制了两份,kafka自己本身就有高可靠的机制,会自己对数据复制一份,而这里又会复制一份到WAL中去.
而基于direct直连的方式,不依赖receiver,不需要开启WAL机制,只要kafka中做了数据的复制,那么就可以通过kafka的副本进行恢复.
3.一次且仅一次事物机制
receiver方式,是使用kafka高阶的api来在zookeeper中保存消费过的offset的,这是消费kafka数据的传统的方式
这种机制可以保证数据零丢失的高可靠性,但是却无法保证数据被处理一次且仅一次,可能会处理两次,因为spark和zookeeper之间是不可能同步的,浪费资源.
基于direct的方式,使用kafka的简单的api,sparkStreaming自己就负责追踪消费offset,并保存在checkpoint中,
因为spark自己一定是同步的,因为可以保证数据是消费一次且仅消费一次的,节省资源.