(三)2:SparkStreaming集成kafka0.10及高版本:scala

SparkStreaming集成kafka0.10及高版本


streaming+kafka官网介绍

1:maven依赖

groupId = org.apache.spark
artifactId = spark-streaming-kafka-0-10_2.11
version = 2.3.2

2:Sparkstreaming+kafka

2.1:创建stream

包括结合kafka和从外部恢复偏移量
初始化StreamingContext

方式1:sparkconf
val conf = new SparkConf().setAppName(appName).setMaster(master)
val ssc = new StreamingContext(conf, Seconds(1))
方式2:SparkContext
val sc = SparkContext(master, appName)
val ssc = StreamingContext(sc, 1)

可以通过sparkconf设置运行中的参数

conf.set("spark.app.id","appid1");
conf.set("spark.streaming.kafka.maxRatePerPartition","10000")
conf.set("spark.streaming.concurrentJobs", SparkConstants.SPARK_STREAMING_COCURRENTJOBS);
conf.set("spark.worker.timeout", "1800");
conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer");
//可以通过spark.executor.extraJavaOptions配置额外的变量或者文件加载到运行环境
比如将log4j配置加载到运行环境
conf.set("spark.executor.extraJavaOptions",“-Dlog4j.configuration=log4j.properties”);

1:kafka读取

val stream = KafkaUtils.createDirectStream[String, String](
  streamingContext,
  LocationStrategies.PreferConsistent,
  Subscribe[String, String](topics, kafkaParams)
)

2:外部数据恢复偏移量

外部数据库读取偏移量恢复流

val fromOffsets = selectOffsetsFromYourDatabase.map { resultSet =>
  new TopicPartition(resultSet.string("topic"), resultSet.int("partition")) -> resultSet.long("offset")
}.toMap

val stream = KafkaUtils.createDirectStream[String, String](
  streamingContext,
  PreferConsistent,
  Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets)
)

2.2:分区位置策略

新版本的Kafka消费者API会将预先获取的消息写入缓存。
因此,Spark在Executor端缓存消费者(而不是每次都重建)对于性能非常重要 ,并且系统会自动为分区分配在同一主机上的消费者进程(如果有的话)

1:LocationStrategies(本地策略)

一般来讲,你最好像上面的Demo一样使用LocationStrategies的PreferConsistent方法。
它会将分区数据尽可能均匀地分配给所有可用的Executor。

2:策略的特殊情况。

情况一
Executor和kafka broker在同一台机器上,可以用PreferBrokers,这将优先将分区调度到kafka分区leader所在的主机上。
题外话:Executor是随机分布的,我怎么知道是不是在同一台服务器上?除非是单机版的are you明白?
情况二
分区之间的负荷有明显的倾斜(数据倾斜),可以用PreferFixed。这个允许你指定一个明确的分区到主机的映射(没有指定的分区将会使用连续的地址)。

2.3:kafka消费者策略

1:topic指定

新的Kafka消费者API有许多不同的方式来指定主题

ConsumerStrategies允许订阅确切指定的一组Topic。
ConsumerStrategies的Subscribe方法通过一个确定的集合来指定Topic
ConsumerStrategies的SubscribePattern方法允许你使用正则表达式来指定Topic,
最后,ConsumerStrategies.Assign()方法允许指定固定的分区集合。
所有三个策略(Subscribe,SubscribePattern,Assign)都有重载的构造函数,允许您指定特定分区的起始偏移量。
如果上述不满足您的特定需求,可以对ConsumerStrategy进行扩展、重写

2:获取偏移量

stream.foreachRDD { rdd =>
  val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
  rdd.foreachPartition { iter =>
    val o: OffsetRange = offsetRanges(TaskContext.get.partitionId)
    println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
  }
}

请注意,HasOffsetRanges仅当在第一个方法(对的结果进行调用)中完成时,类型转换才会成功而不是随后在方法链中进行。请注意,在任何随机播放或重新分区的方法(例如reduceByKey()或window())之后,RDD分区和Kafka分区之间的一对一映射不会保留。

3:存储偏移

有三种方式来存储Kafka的偏移量。

3.1:第一种方式:Checkpoint检查点机制

这种方式很容易做到,但是有以下的缺点:
多次输出,结果必须满足幂等性(什么意思自己Google)
如果您的应用程序代码已更改,则无法从检查点恢复。必须清楚之前的检查点文件重启程序
缺点:无法从Spark Streaming中的检查点恢复累加器和广播变量
实战demo

 RD0.foreachRDD((VoidFunction<JavaRDD<ConsumerRecord<String, byte[]>>>) consumerRecordJavaRDD -> {
                String checkPoint = "" + System.currentTimeMillis();
                HasOffsetRanges hasOffsetRanges = (HasOffsetRanges) (consumerRecordJavaRDD.rdd());
                StringBuilder sb = new StringBuilder();
                for (OffsetRange of : hasOffsetRanges.offsetRanges()) {
                    sb.append(of.topic() + "-" + of.partition() + "=" + of.fromOffset() + "\n");
                }
                localCheckPoint.saveCheckPoint(checkPoint, sb.toString());
 });
3.2:二种方式:Kafka
0.10x新版kafka提交到kafka有两种方式:自动+手动

相对于checkpoint

offset存储到kafka的好处是:
1:可以用kafka的监控工具,如kafka manager等,存到checkpoint是不行的
2:kafka既是一个容错的存储系统,也是可以避免代码变更带来的麻烦。然而,Kafka不是事务性的,所以你的输出必须仍然是幂等的

Kafka提供的有api,可以将offset提交到指定的kafkatopic。
默认情况下,新的消费者会周期性的自动提交offset到kafka。但是有些情况下,这也会有些问题,因为消息可能已经被消费者从kafka拉去出来,但是spark还没处理,这种情况下会导致一些错误。这也是为什么例子中stream将enable.auto.commit设置为了false。然而在已经提交spark输出结果之后,你可以手动提交偏移到kafka。

1:手动:

stream.foreachRDD(new VoidFunction<JavaRDD<ConsumerRecord<String, String>>>() {
  @Override
  public void call(JavaRDD<ConsumerRecord<String, String>> rdd) {
    OffsetRange[] offsetRanges = ((HasOffsetRanges) rdd.rdd()).offsetRanges();
    //此api在kafka0.10版本后才有,也就是异步提交,提高效率
    ((CanCommitOffsets) stream.inputDStream()).commitAsync(offsetRanges);
  }
});

2:自动
enable.auto.commit=true

2.4:消费的并行度

shuffle之前task个数和kafka的partition个数一一对应。可以调整分区数调优

2.5:处理数据

获取数据:JavaDStream<byte[]> RD1 = rdd.map(record -> record.value());
RD1 .再进行数据处理

3:SSL/TLS配置使用

全称是传输层安全性协定,用于Spark与Kafka进行安全通信
启用它只需要 在执行createDirectStream / createRDD之前设置kafkaParams

Map<String, Object> kafkaParams = new HashMap<String, Object>();
// the usual params, make sure to change the port in bootstrap.servers if 9092 is not TLS
kafkaParams.put("security.protocol", "SSL");
kafkaParams.put("ssl.truststore.location", "/some-directory/kafka.client.truststore.jks");
kafkaParams.put("ssl.truststore.password", "test1234");
kafkaParams.put("ssl.keystore.location", "/some-directory/kafka.client.keystore.jks");
kafkaParams.put("ssl.keystore.password", "test1234");
kafkaParams.put("ssl.key.password", "test1234");

4:部署启动

spark-submit进行部署启动

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值