1.spark程序中的kafka的host也定要写成hostname的格式而不是写ip的格式:
例如:"bootstrap.servers" -> "os1:9092,os2:9092,os3:9092",而不是ip的格式,否则会报错
2.flume的spooldir的source问题:
如果使用spooldir进行监听日志文件夹,如果文件夹中的文件是递增的形式进行存入日志,这样使用spooldir的方式就会报错,因为spooldir在读取文件的时候,不能进行对文件进行更改,否则就报错
解决办法:可以使用 taildir source
3.增加spark-streaming的并发处理能力:
增加kafka的某个topic分区数量,然后再spark提交任务的时候的executor的数量最好和topic的数量一致,这样每个executor都可以进行消费,否者就会存在executor浪费的
4.spark-streaming消费kafka数据的时候偏移量的存储:
偏移量存储的一般自己维护,因为使用spark-streaming - kafka0.10是将offset存储在kafka的__consumer_offsets这个topic中,但是这样不便于我们进行偏移量的维护,虽然说新版 kafka 中已经无需使用 zookeeper 管理偏移量了,
但是使用 zookeeper 管理偏移量相比 kafka 自行管理偏移量有如下几点好处:
可以使用 zookeeper 管理工具轻松查看 offset 信息;
无需修改 groupId 即可从头读取消息;
特别情况下可以人为修改 offset 信息。
借助 zookeeper 管理工具可以对任何一个节点的信息进行修改、删除,如果希望从最开始读取消息,则只需要删除 zk 某个节点的数据即可。
相应的offset维护代码:
package utils import kafka.utils.{ZKGroupTopicDirs, ZkUtils} import org.I0Itec.zkclient.ZkClient import org.apache.kafka.common.TopicPartition import org.apache.log4j.{Level, Logger} import org.apache.spark.SparkConf import org.apache.spark.rdd.RDD import org.apache.spark.streaming.kafka010.{ConsumerStrategies, HasOffsetRanges, KafkaUtils, LocationStrategies} import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.kafka.common.serialization.StringDeserializer object KafkaDirect_ZK_Offset { def main(args: Array[String]): Unit = { Logger.getLogger("org.apache.spark").setLevel(Level.OFF) val conf: SparkConf = new SparkConf().setAppName("KafkaDirect_ZK_Offset").setMaster("local[*]") val ssc: StreamingContext = new StreamingContext(conf,Seconds(5)) val groupId = "cmcc_test2" /** * kafka参数列表 */ val kafkaParams = Map[String,Object]( "bootstrap.servers" -> "os1:9092,os2:9092,os3:9092", "key.deserializer" -> classOf[StringDeserializer], "value.deserializer" -> classOf[StringDeserializer], "group.id" -> groupId, "auto.offset.reset" -> "earliest", "enable.auto.commit" -> (false:java.lang.Boolean) ) val topic = "cmcc1" val topics = Array(topic) /** * 如果我们自己维护偏移量 * 问题: * 1.程序在第一次启动的时候,应该从什么开始消费数据?earliest * 2.程序如果不是第一次启动的话,应该从什么位置开始消费数据? * 上一次自己维护的偏移量接着往后消费,比如上一次存储的offset=88 */ val zKGroupTopicDirs: ZKGroupTopicDirs = new ZKGroupTopicDirs(groupId,topic) /** * 生成的目录结构 * /customer/g1/offsets/wordcount */ val offsetDir: String = zKGroupTopicDirs.consumerOffsetDir //zk字符串连接组 val zkGroups = "os1:2181,os2:2181,os3:2181" //创建一个zkClient连接 val zkClient: ZkClient = new ZkClient(zkGroups) //子节点的数量 val childrenCount: Int = zkClient.countChildren(offsetDir) //子节点的数量>0就说明非第一次 val stream = if(childrenCount>0){ println("已经启动过") //用来存储我们已经读取到的偏移量 var fromOffsets = Map[TopicPartition,Long]() (0 until childrenCount).foreach(partitionId => { val offset = zkClient.readData[String](offsetDir+s"/$partitionId") fromOffsets += (new TopicPartition(topic,partitionId) -> offset.toLong) }) KafkaUtils.createDirectStream(ssc, LocationStrategies.PreferConsistent, ConsumerStrategies.Assign[String,String](fromOffsets.keys.toList,kafkaParams,fromOffsets) ) } else{ println("第一次启动") KafkaUtils.createDirectStream(ssc, LocationStrategies.PreferConsistent, ConsumerStrategies.Subscribe[String,String](topics,kafkaParams) ) } stream.foreachRDD( rdd => { //转换rdd为Array[OffsetRange] val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges val maped: RDD[(String, String)] = rdd.map(record => (record.key,record.value)) //计算逻辑 //maped.foreach(println) //自己存储数据,自己管理 for(o <-offsetRanges){ //写入到zookeeper,第二个参数为是否启动安全 ZkUtils(zkClient,false).updatePersistentPath(offsetDir+"/"+o.partition,o.untilOffset.toString) } } ) ssc.start() ssc.awaitTermination() } }