0、kafka安装
1.1 解压
解压kafka_2.11-2.4.1.tgz至spark目录并重命名为kafka241
1.2 创建日志目录
在kafka241目录下新建文件夹kfklogs
1.3 编辑配置文件
进入config目录,编辑server.properties,在非编辑模式下输入/查找的搜索词然后回车即可跳到相应位置
#四台机器分别为1,2,3,4,依次递增
broker.id=1
#打开侦听端口,每台机器写自己的hostname
listeners=PLAINTEXT://single:9092
#日志存储目录
log.dirs=/opt/software/spark/kafka241/kfklogs
#配置连接zookeeper地址
zookeeper.connect=master01:2181,master02:2181,slave01:2181,slave02:2181/kafka
1、常用命令
1.0 启动关闭命令
server.properties在config目录下,要指定
启动:
kafka-server-start.sh -daemon server.properties
#-daemon是后台启动的意思,默认前台阻塞
#server.properties是指定配置文件
关闭:
kafka-server-stop.sh
1.1 查看主题
#查看主题
kafka-topics.sh --zookeeper master01:2181/kafka --list
1.2 创建主题
#创建主题“first”,分区数为3,备份因子为2
kafka-topics.sh --zookeeper master01:2181/kafka --create --topic first --partitions 3 --replication-factor 2
kafka-topics.sh --create --topic event_attendees_raw --zookeeper single:2181 --replication-factor 1 --partitions 1
1.2.1 自动删除
#删除topic,要现在server.properties里加入这个配置,不过一般不会删除这种东西
delete.topic.enable=true
1.2.2 手动删除
#手工删除topic,使用topic delete
kafka-topics.sh --zookeeper single:2181 --delete --topic event_attendees_raw
#进入kafka-logs目录 删除所有的__consumer_offset_x
#进入zkCli 进入zookeeper rmr删除对应的节点消息
1.3 查看主题的结构描述信息
#查看主题“first”的结构描述信息
kafka-topics.sh --zookeeper master01:2181/kafka --describe --topic first
1.4 进入生产者模式
#生产者模式,生产数据给多个消费者
kafka-console-producer.sh --broker-list master01:9092,master02:9092 --topic first
1.5 进入消费者模式
#消费者模式1,查看生产者发送过来的东西,只看启动生产的数据
#--bootstrap-server:指定kafka服务端,kafka的主机名:端口
kafka-console-consumer.sh --bootstrap-server master01:9092,master02:9092 --topic first
#消费者模式2,查看生产者发送过来的东西,可以看到启动前的数据,从头到尾
kafka-console-consumer.sh --bootstrap-server master01:9092,master02:9092 --topic first --from-beginning
1.6 查看消费者组
#查看消费者组
kafka-consumer-groups.sh --bootstrap-server master01:9092 --list
#console-consumer-71716
#console-consumer-32602
#console-consumer-45272
1.7 查看组详情
#查看组详情
kafka-consumer-groups.sh --bootstrap-server master01:9092 --describe --group console-consumer-????
#输出结果如下:
#GROUP消费者组,TOPIC主题,PARTITION分区,CURRENT-OFFSET当前偏移量,LOG-END-OFFSET下一条将要加入的日志偏移,LAG滞后延时,CONSUMER-ID消费者ID,HOST主机IP,CLIENT-ID客户端
1.8 生产压力测试
#生成压力测试,perf性能
kafka-producer-perf-test.sh --topic first --record-size 1000 --num-records 200000 --throughput -1 --producer-props bootstrap.servers=master01:9092,master02:9092,slave01:9092,slave02:9092
#--topic first 主题
#--record-size 1000 一条消息覆盖的字节
#--num-records 200000 一次性生成多少
#--throughput -1 :定量测延时,不定量测吞吐
#--producer-props 所有的机器
1.9 消费压力测试
#消费压力测试
kafka-consumer-perf-test.sh --topic first --fetch-size 10000 --messages 200000 --threads 1 --broker-list master01:9092,master02:9092,slave01:9092,slave02:9092
1.10 游标位置
#命令:查看读取的游标位置
kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list single:9092 --topic event_attendees_raw --time -1
#输出:前面的游标数,后面是总数
mydemo3:0:0
1.11 消费者组offset重置,游标重回earliest
kafka-consumer-groups.sh --bootstrap-server single:9092 --group xym --reset-offsets --all-topics --to-earliest --execute
更新到当前group最初的offset位置
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group test-group --reset-offsets --all-topics --to-earliest --execute
更新到指定的offset位置
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group test-group --reset-offsets --all-topics --to-offset 500000 --execute
更新到当前offset位置(解决offset的异常)
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group test-group --reset-offsets --all-topics --to-current --execute
offset位置按设置的值进行位移
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group test-group --reset-offsets --all-topics --shift-by -100000 --execute
offset设置到指定时刻开始
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group test-group --reset-offsets --all-topics --to-datetime 2017-08-04T14:30:00.000
1.12 解析查看日志
kafka-dump-log.sh --files ./00000000000000000000.index
1.13、修改分区数(increase)
kafka-topics.sh --zookeeper hd01:2181/kafka --alter --topic mydemo4 --partitions 3
2、查看目录结构
在kafka当中, 每一个topic会有一个单独文件夹, 在topic下会为每一个分区生成一个单独的文件夹, 将这二者合并命名,topic-分区号
, 例如test-0
, 在每一个分区下又会有多个segment, 既然已经有多个分区了, 为什么要再进行划分为多个segment?
答:①如果只存一个文件中, 文件会越来越大, ②Kafka中的数据默认存储7天, 每一天都会删除7天前的数据, 如果都存在一个文件当中, 会不好删
1.生成测试数据
kafka-producer-perf-test.sh --topic test --num-records 500000 --record-size 1000 --producer-props bootstrap.servers=singleNode:9092 --throughput 1000000000
2.查看index和log文件
kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.log --print-data-log
查看偏移量和总数量
kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.index --print-data-log
3、开发经验
3.1 kafka节点数量:
2*(峰值生产速度(MB)*副本数量/100)+1
峰值生产速度向客户提取。如果峰值生产速度:50M/s,副本数,2R/p,那么节点数量为2*(50*2/100)+1=3台。一般集群规模10台,则kafka为3~5台
3.2 Topic数量:
一般情况下,每种日志类型一个topic:行为日志一个,RMDBMS一个
3.3 分区数量:
分区数量是测出来的。创建单分区的topic,测producer和consumer的吞吐速度,假设生产者最小值Tp,消费者最小值为Tc,期望的吞吐为Tt,则分区数为Tt/Math.min(Tp,Tc)。原则是生产消费者的速度都要满足。一般分数区数副本设置根据集群的规模取值3-10个。
4、Kafka工作流程图
5、Kafka API
依赖:
<!--kafka-->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.11.0.2</version>
</dependency>
5.1 Producer API:
package cn.xym.spark
import java.util.Properties
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord}
object Kafka {
def main(args: Array[String]): Unit = {
// 创建配置
val props = new Properties()
// 设置Kafka集群
props.put("bootstrap.servers", "chust01:9092")
// 设置ack
// 0: 不需要leader partition确认接收成功, 将消息发送到leader partition即可
// 1:需要等待leader partition确认接收成功
// -1(all): 需要等待leader partition以及ISR列表中的follower都确认接收成功
props.put("acks", "all")
// 设置key和value的序列化
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer")
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer")
// 创建一个Kafka的生产者
val producer = new KafkaProducer[String,String](props)
for (elem <- 1 to 100) {
// 发送消息 三个参数: ①topic, ②key, ③value
producer.send(new ProducerRecord[String,String]("test","1001","zhangsan"))
Thread.sleep(10000)
}
// 关闭资源
producer.close()
}
}
参数名称 | 说明 | 默认值 |
---|---|---|
bootstrap.servers | kafka集群的broker-list | |
acks | 确保生产者可靠性设置acks=0:不等待成功返回acks=1:等Leader写成功返回acks=all:等Leader和所有ISR中的Follower写成功返回,all也可以用-1代替 | -1 |
key.serializer | key的序列化器 | |
value.serializer | value的序列化器 | |
retries | 发送失败尝试重发次数 | 0 |
batch.size | 每个partition的未发送消息大小 | 16384 |
partitioner.class | 分区类,可以自定义分区类,实现partitioner接口 | 默认是哈希值%partitions |
max.block.ms | 最大阻塞时长 | 60000 |
5.2 Consumer API
package cn.xym.spark
import java.util.Properties
import org.apache.kafka.clients.consumer.KafkaConsumer
object KafkaConsumer{
def main(args: Array[String]): Unit = {
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.clients.consumer.ConsumerRecords
import java.util
//创建一个配置
val props = new Properties()
//配置Kafka集群的ip和端口号
props.put("bootstrap.servers", "chust01:9092")
//设置消费者组的id,如果有多个相同id的消费者程序,那么他们将在一个组中
props.put("group.id", "testGroup1")
//开启自动提交,默认是开启
props.put("enable.auto.commit", "true") //默认值true
//默认每隔5000ms提交一次
props.put("auto.commit.interval.ms", "1000") //默认值5000
//key和value的序列化
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer")
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer")
//创建一个Consumer客户端
val consumer = new KafkaConsumer[String,String](props)
//consumer消费
val topics = new util.ArrayList[String]()
topics.add("test")
consumer.subscribe(topics)
//为了能够一直从Kafka中消费数据,使用while死循环
while (true){
//拉取数据,kafka推过来的一堆数据
val records = consumer.poll(1000)
//对这些数据转化为迭代器进行遍历输出
val iter = records.iterator()
while (iter.hasNext){
val next = iter.next()
println(s"partition = ${next.partition()},offset = ${next.offset()},value = ${next.key()},value = ${next.value()}")
}
}
}
}
5.3 Consumer Offset API 偏移
package cn.xym.spark
import java.util
import java.util.Properties
import org.apache.kafka.clients.consumer.{ConsumerRecord, ConsumerRecords, KafkaConsumer}
object KafkaConsumer2 {
def main(args: Array[String]): Unit = {
// 创建一个配置
val props = new Properties()
// 配置Kafka集群的ip和端口号
props.put("bootstrap.servers", "chust01:9092")
// 设置消费者组的id, 如果有多个相同id的消费者程序, 那么他们将在一个组当中
props.put("group.id", "testGroup1")
// 关闭自动提交[默认就是开启]
props.put("enable.auto.commit", "false")
// key和value的反序列化
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer")
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer")
// 创建一个Consumer客户端
val consumer = new KafkaConsumer[String, String](props)
// consumer消费
val topics = new util.ArrayList[String]()
topics.add("test")
// 订阅topic
consumer.subscribe(topics)
// 创建一个集合, 用来存放消息的个数
val buffer = new util.ArrayList[ConsumerRecord[String, String]]()
// 为了能够一直从Kafka中消费数据, 使用 while true 死循环
while (true) {
// 设置超时时间, 单位是毫秒, 返回一些条数据
val records: ConsumerRecords[String, String] = consumer.poll(1000)
// 对这些数据进行遍历输出
val iter: util.Iterator[ConsumerRecord[String, String]] = records.iterator()
while (iter.hasNext) {
val next: ConsumerRecord[String, String] = iter.next()
println(s"partition = ${next.partition()}, offset=${next.offset()}\nkey = ${next.key()}, value = ${next.value()}")
buffer.add(next)
}
if(buffer.size()>5){
// 手动提交offset有两种, 一种是同步阻塞方式, 一种是异步非阻塞方式
consumer.commitAsync()
// consumer.commitSync()
buffer.clear()
}
}
}
}
onsumerRecord[String, String] = iter.next()
println(s"partition =
n
e
x
t
.
p
a
r
t
i
t
i
o
n
(
)
,
o
f
f
s
e
t
=
{next.partition()}, offset=
next.partition(),offset={next.offset()}\nkey = ${next.key()}, value = ${next.value()}")
buffer.add(next)
}
if(buffer.size()>5){
// 手动提交offset有两种, 一种是同步阻塞方式, 一种是异步非阻塞方式
consumer.commitAsync()
// consumer.commitSync()
buffer.clear()
}
}
}
}