创建一个topic

./kafka-topics.sh --create --zookeeper 192.168.1.244:2181,192.168.1.245:2181,192.168.1.246:2181 --replication-factor 1
--partitions 1 --topic topic_test_zk_minOffset_zkGroup

查看topic列表

./kafka-topics.sh --list --zookeeper 192.168.1.244:2181,192.168.1.245:2181,192.168.1.246:2181

producer 代码如下

package com.kafka.test;

import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord; /**
* @author:FengZhen
* @create:2018年8月9日
*/
public class Producer_zk { public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "192.168.1.244:6667,192.168.1.247:6667");
//props.put("zookeeper.connect", "192.168.1.244:2181,192.168.1.245:2181,192.168.1.246:2181");
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); KafkaProducer<String, String> producer = new KafkaProducer<String, String>(props);
for (int i = 30; i < 40; i++)
producer.send(new ProducerRecord<String, String>("topic_test_zk_minOffset_zkGroup", Integer.toString(i), "中文测试-"+Integer.toString(i))); producer.close();
} }

Streaming代码如下

package streaming

import kafka.api.{OffsetRequest, PartitionOffsetRequestInfo, TopicMetadataRequest}
import kafka.common.TopicAndPartition
import kafka.consumer.SimpleConsumer
import kafka.message.MessageAndMetadata
import kafka.serializer.StringDecoder
import kafka.utils.{ZKGroupTopicDirs, ZkUtils}
import org.I0Itec.zkclient.ZkClient
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka.{HasOffsetRanges, KafkaUtils, OffsetRange}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext} object KafkaLog_local_zk_minOffset_zkGroup { def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("KafkaLog_local_zk_minOffset_zkGroup").setMaster("local[2]")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
val ssc = new StreamingContext(sc, Seconds(5)) val broker_servers = "192.168.1.244:6667,192.168.1.247:6667"
val zk_host = "192.168.1.244:2181,192.168.1.245:2181,192.168.1.246:2181"
//消费的 topic 名字
val topic : String = "topic_test_zk_minOffset_zkGroup"
//创建 stream 时使用的 topic 名字集合
val topics : Set[String] = Set(topic) var kafkaParam:Map[String,String] = Map()
kafkaParam += ("bootstrap.servers" -> broker_servers)
kafkaParam += ("group.id" -> "test")
kafkaParam += ("enable.auto.commit" -> "true")
kafkaParam += ("auto.commit.interval.ms" -> "100") //创建一个 ZKGroupTopicDirs 对象,对保存
val topicDirs = new ZKGroupTopicDirs("topic_test_zk_minOffset_zkGroup_group", topic) //获取 zookeeper 中的路径,这里会变成 /consumers/test_spark_streaming_group/offsets/topic_name
// /consumers/topic_test_zk_minOffset_zkGroup_group/offsets/topic_test_zk_minOffset_zkGroup/0
val zkTopicPath = s"${topicDirs.consumerOffsetDir}" //zookeeper 的host 和 ip,创建一个 client
val zkClient = new ZkClient(zk_host)
//查询该路径下是否字节点(默认有字节点为我们自己保存不同 partition 时生成的)
val children = zkClient.countChildren(zkTopicPath) var kafkaStream : InputDStream[(String, String)] = null //如果 zookeeper 中有保存 offset,我们会利用这个 offset 作为 kafkaStream 的起始位置
var fromOffsets: Map[TopicAndPartition, Long] = Map() //如果保存过 offset,这里更好的做法,还应该和 kafka 上最小的 offset 做对比,不然会报 OutOfRange 的错误
if (children > 0) {
for (i <- 0 until children) {
val topic2 = List(topic)
val req = new TopicMetadataRequest(topic2, 0)
// 第一个参数是 kafka broker 的host,第二个是 port
val getLeaderConsumer = new SimpleConsumer("192.168.1.244", 6667, 10000, 10000, "OffsetLookup")
val res = getLeaderConsumer.send(req)
val topicMetaOption = res.topicsMetadata.headOption
val partitions = topicMetaOption match {
// 将结果转化为 partition -> leader 的映射关系
case Some(tm) =>
tm.partitionsMetadata.map(pm => (pm.partitionId, pm.leader.get.host)).toMap[Int, String]
case None =>
Map[Int, String]()
}
//去出分片对应的leader host
val brokerLeaderHost = partitions.get(i).toString.replace("Some(", "").replace(")","") val partitionOffset = zkClient.readData[String](s"${zkTopicPath}/${i}")
val tp = TopicAndPartition(topic, i) val requestMin = OffsetRequest(Map(tp -> PartitionOffsetRequestInfo(OffsetRequest.EarliestTime, 1)))
val consumerMin = new SimpleConsumer(brokerLeaderHost, 6667, 10000, 10000, "getMinOffset")
val curOffsets = consumerMin.getOffsetsBefore(requestMin).partitionErrorAndOffsets(tp).offsets
var nextOffset = partitionOffset.toLong
// 通过比较从 kafka 上该 partition 的最小 offset 和 zk 上保存的 offset,进行选择
if (curOffsets.length > 0 && nextOffset < curOffsets.head) {
nextOffset = curOffsets.head
}
//设置正确的 offset,这里将 nextOffset 设置为 0(0 只是一个特殊值),可以观察到 offset 过期的想想
fromOffsets += (tp -> nextOffset)
println("@@@@@@ topic[" + topic + "] partition[" + i + "] offset[" + partitionOffset + "] @@@@@@")
} //这个会将 kafka 的消息进行 transform,最终 kafak 的数据都会变成 (topic_name, message) 这样的 tuple
val messageHandler = (mmd : MessageAndMetadata[String, String]) => (mmd.topic, mmd.message())
kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder, (String, String)](ssc, kafkaParam, fromOffsets, messageHandler)
}
else {
//如果未保存,根据 kafkaParam 的配置使用最新或者最旧的 offset
kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParam, topics)
} var offsetRanges = Array[OffsetRange]()
//得到该 rdd 对应 kafka 的消息的 offset
kafkaStream.transform{ rdd =>
offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
rdd
}.foreachRDD { rdd => //.map(msg => Utils.msgDecode(msg))
for (o <- offsetRanges) {
val zkPath = s"${zkTopicPath}/${o.partition}"
//将该 partition 的 offset 保存到 zookeeper
ZkUtils.updatePersistentPath(zkClient, zkPath, o.fromOffset.toString)
println(s"@@@@@@ topic ${o.topic} partition ${o.partition} fromoffset ${o.fromOffset} untiloffset ${o.untilOffset} #######")
}
rdd.foreachPartition(
message => {
while(message.hasNext) {
println(s"@^_^@ [" + message.next() + "] @^_^@")
}
}
)
}
//开启流式计算
ssc.start()
//一直会阻塞,等待退出
ssc.awaitTermination()
}
}

出现的问题

使用simpleConsumer时报错

Exception in thread "main" java.nio.channels.ClosedChannelException
at kafka.network.BlockingChannel.send(BlockingChannel.scala:100)
at kafka.consumer.SimpleConsumer.liftedTree1$1(SimpleConsumer.scala:78)
at kafka.consumer.SimpleConsumer.kafka$consumer$SimpleConsumer$$sendRequest(SimpleConsumer.scala:68)
at kafka.consumer.SimpleConsumer.getOffsetsBefore(SimpleConsumer.scala:127)
at streaming.KafkaLog_local_zk_minOffset$$anonfun$main$1.apply$mcVI$sp(KafkaLog_local_zk_minOffset.scala:64)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:160)
at streaming.KafkaLog_local_zk_minOffset$.main(KafkaLog_local_zk_minOffset.scala:44)
at streaming.KafkaLog_local_zk_minOffset.main(KafkaLog_local_zk_minOffset.scala)
解决将Kafka config下的server.properties的参数修改下
num.network.threads=3
zookeeper.connection.timeout.ms=6000

 再次尝试即可.

Kafka+SparkStreaming+Zookeeper(ZK存储Offset,解决checkpoint问题)的更多相关文章

  1. Kafka在zookeeper中存储结构和查看方式

    Zookeeper 主要用来跟踪Kafka 集群中的节点状态, 以及Kafka Topic, message 等等其他信息. 同时, Kafka 依赖于Zookeeper, 没有Zookeeper 是 ...

  2. kafka在zookeeper中存储结构

    1.topic注册信息 /brokers/topics/[topic] : 存储某个topic的partitions所有分配信息 Schema:   {    "version": ...

  3. filebeat+kafka+SparkStreaming程序报错及解决办法

    // :: WARN RandomBlockReplicationPolicy: Expecting replicas with only peer/s. // :: WARN BlockManage ...

  4. Kafka学习之路 (五)Kafka在zookeeper中的存储

    一.Kafka在zookeeper中存储结构图 二.分析 2.1 topic注册信息 /brokers/topics/[topic] : 存储某个topic的partitions所有分配信息 [zk: ...

  5. Kafka(四)Kafka在zookeeper中的存储

    一 Kafka在zookeeper中存储结构图 二 分析 2.1 topic注册信息 /brokers/topics/[topic] : 存储某个topic的partitions所有分配信息 [zk: ...

  6. 深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列

    消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件. 本场 Chat 主要内容: Kafk ...

  7. Kafka 和 ZooKeeper 的分布式消息队列分析

    1. Kafka 总体架构 基于 Kafka-ZooKeeper 的分布式消息队列系统总体架构如下: 如上图所示,一个典型的 Kafka 体系架构包括若干 Producer(消息生产者),若干 bro ...

  8. sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)

    版本声明: kafka:1.0.1 spark:2.1.0 注意:在使用过程中可能会出现servlet版本不兼容的问题,因此在导入maven的pom文件的时候,需要做适当的排除操作 <?xml ...

  9. sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)-- 2

    参考上篇博文:https://www.cnblogs.com/niutao/p/10547718.html 同样的逻辑,不同的封装 package offsetInZookeeper /** * Cr ...

随机推荐

  1. 一个fork的面试题——fork + 缓存区

    update : 20140512 题目:请问下面的程序一共输出多少个“-”? #include <stdio.h> #include <sys/types.h> #inclu ...

  2. ES6学习笔记之变量声明let,const

    最近用淘宝的weex做了个项目,最近稍微闲下来了.正好很久没有接触RN了,所以趁这个机会系统的学习一下ES6的相关知识. 孔子说:没有对比就没有伤害.所以我们要拿ES6和ES5好好对比的学习.这样才能 ...

  3. 《从零开始学Swift》学习笔记(Day 18)——有几个分支语句?

    原创文章,欢迎转载.转载请注明:关东升的博客       分支语句又称条件语句,Swift编程语言提供了if.switch和guard三种分支语句. if语句 由if语句引导的选择结构有if结构.if ...

  4. On the fly test

    on the fly test就是边开发边测试的意思.test code不是早就生成好的,在一边生成code的同时一边做test running,最大的好处是,flexiable ,每一次可以选择不同 ...

  5. coursera 《现代操作系统》 -- 第十周 文件系统(2)

    身份验证 Authentication 知道用户是谁.通过账号密码.Id 这样的识别出来. 访问控制 Permission 知道用户是谁后. 主动控制 记录用户ID和对应的访问权限 --> 记录 ...

  6. 什么是PHP闭包???

    闭包函数:临时创建一个没有名称的函数,经常作为回调函数来用. 通俗的说就是:子函数可以使用父函数中的局部变量,这种行为叫做闭包. 1.匿名函数赋值 $demo=function($str){ echo ...

  7. 两个offer如何做选择?年薪20万vs年薪15万

    (附注:本文转载于:http://www.eoeandroid.com/thread-296678-1-1.html) 前些天和一个年轻的朋友谈跳槽.朋友说她需要在两个offer里面做选择.一个是年薪 ...

  8. Python线程包装器

    import threading import subprocess import time def need_thread(func, *args, **kwargs): def fun(): pr ...

  9. Latex技巧:在图表序号中加入章节号(实现诸如“图1.1.2”这样的图表序号)

    平时看书经常看到"图1.2"这样的编号,含义是第1章的第2幅插图:或者"图1.1.2",含义是第1章第1节的第2幅插图.而在LaTeX中如果直接插图的话只会显示 ...

  10. bootstrap模板

    一.bootstrap基本介绍 1.什么是bootstrap? bootstrap就是个前端快速开发的工具,该工具是个简单.直观.强悍的前端开发框架,让web开发更加迅速简单,同时也是个响应式布局,兼 ...