Spark Streaming offset的管理那些事!
1.Kafka 消息的管理办法
(1) topic
topic中包含多个分区,建议分区是Kafka broker的整数倍,或者是磁盘的整数倍
分区数是Kafka存储的主要概念,key的Hash&numPartition,在分区里存储的时候,offset-msg
消费者 消费消息 首先会自己确定offset的范围,然后使用该范围去kafka
指定topic的指定分区去取消息
消费者消费消息的两个条件:
(1)自己清楚自己消费消息的offset范围,一消费者要自己维护offset在内存,然后定期提交到
第三方存储比如:zk(第三方),kafka。
主动去kafka拉去消息
(内存中有个offset《记录范围》告诉 这次消费到哪里,下次要取消息的时候从哪里开始消费)
高阶Api:
AutoCommit offset
有个线程定期去提交offset(高级 自动维护管理offset)
低阶Api:
它也管理和维护着offset,但是他没有自动去提交offset到 zk或者kafka
2.spark streaming 与kafka结合offset的管理(由于SparkStreaing是分布式的,也即最终输出的时候会有多个任务在多个地方输出到结果库
这样可以实现在单个任务或者分区内部一个事务同时提交offset和结果,但是多个任务或者分区
无法实现在同一个事务内将结果和offset提交到支持事务的结果库)
flink支持分布式事务,Sparkstreaming不支持,那么只能在最后输出的时候进行rpartition成1个分区(超大型数据库不靠谱)
(1) 消费语义的问题:
前提:应用回复故障的时候会从offset存储的地方获取最后提交的offset
仅一次消费(实现起来比较困难)
offset和结果同时一个事务内提交
最多一次消费《丢失数据的,并不是我们所希望的》
先提交offset,再提交结果,这实现最多一次
最少一次消费《很容易实现的》
为什么最少一次消费容易实现:—>
先提交结果,再提交offset,这个时候就是最少消费一次消费语义
《数据重复消费》
(在结果输出之后并且offset输出之前应用程序挂掉,故障恢复后从最后一次提交成功的offset处恢复,会导致数据重复处理。)
那么为了避免有些业务不能重复输出结果,要求我们
1.结果输出要支持幂等性,
2.也可以进行去重
3.要么就是存在更新不存在删除
Receive的形式=>
消费者会定期提交offset到zk中去
版本0.8X 0.1X 用的比较多,其他版本都不敢用 怕不稳定
kafka 010
kafka 082 Receiver
kafka 082 Direct 不给它offset的话, 或者会采用auto.offset.reset
如果,消费者首次启动,内存中没有offset
设置(auto.offset.reset)-自动重置offset
010
largest(旧) 最大或者是最新的offset latest (新)
smallest(旧) 最小或者是最老的offset earliest (新)
010
largest 最大或者是最新的offset
earliest 最小或者是最老的offset
如下为Kafka的一个工具类:
在这里插入代码片object MyKafkaUtil {
private val properties: Properties = PropertiesUtil.load("config.properties")
val broker_list = properties.getProperty("kafka.broker.list")
// kafka消费者配置
val kafkaParam = Map(
"bootstrap.servers" -> broker_list,//用于初始化链接到集群的地址
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
//用于标识这个消费者属于哪个消费团体
"group.id" -> "gmall_consumer_group",
//如果没有初始化偏移量或者当前的偏移量不存在任何服务器上,可以使用这个配置属性
//可以使用这个配置,latest自动重置偏移量为最新的偏移量
"auto.offset.reset" -> "latest",
//如果是true,则这个消费者的偏移量会在后台自动提交,但是kafka宕机容易丢失数据
//如果是false,会需要手动维护kafka偏移量
"enable.auto.commit" -> (true: java.lang.Boolean)
)
// 创建DStream,返回接收到的输入数据
// LocationStrategies:根据给定的主题和集群地址创建consumer
// LocationStrategies.PreferConsistent:持续的在所有Executor之间分配分区
// ConsumerStrategies:选择如何在Driver和Executor上创建和配置Kafka Consumer
// ConsumerStrategies.Subscribe:订阅一系列主题
def getKafkaStream(topic: String,ssc:StreamingContext): InputDStream[ConsumerRecord[String,String]]={
val dStream = KafkaUtils.createDirectStream[String,String](ssc, LocationStrategies.PreferConsistent,ConsumerStrategies.Subscribe[String,String](Array(topic),kafkaParam))
dStream
}***********************************
}