分片机制:主要解决了单台服务器存储容量有限的问题
当数据量非常大的时候,一个服务器存放不了,就将数据分成两个或者多个部分,存放在多台服务器上。每个服务器上的数据,叫做一个分片
副本:副本备份机制解决了数据存储的高可用问题
当数据只保存一份的时候,有丢失的风险。为了更好的容错和容灾,将数据拷贝几份,保存到不同的机器上。
kafka中对于分片的数量可以是任意的,建议不要超过节点数,但对于副本的数量,最多等于节点数
解决方案:当producer发送数据到kafka后,kafka应该给予一个响应信息, ACK校验机制
ack校验机制的三种状态码:
a) 0:生产者只负责发送数据,不关心kafka是否接受的到
b) 1:某个partition的leader收到数据给出响应
c) -1:某个partition的所有副本都收到数据后给出响应
在生产环境下设置状态码的时候, 根据数据的效率和重要性判断设置标准
数据在发送的过程中主要有二种模式: 同步模式 和 异步模式
同步模式:
发送一条数据, 等待响应, 响应成功后, 发送下一条数据即可
在同步模式下, 如果broker一直不给于响应, 设置超时(10s), 超时后, 进行重试(3)即可,最终会报错
异步模式:
引入缓冲池, 实现批量发送数据, 当达到一定数据的阈值或者时间阈值后, 进行批量发送数据即可
内部和同步一样, 都会有超时和重试策略, 不同点在于生产者不断的发送数据, 将数据写入到缓冲池中
如果缓冲池满了, 可以选择直接将其清空, 或者将其保存第三方的容器中a) 先将数据保存在生产者端的buffer中。Buffer大小是2万条。 32M
b) 满足数据阈值或者时间阈值其中的一个条件就可以发送数据。
c) 发送一批数据的大小是500条。16Kb
模拟异步发送数据:
直接调send方法, 其实就是一个异步发送方式:
// 异步的方式方式
kafkaProducer.send(producerRecord);
或者:
kafkaProducer.send(producerRecord, new Callback() {
// onCompletion什么会调用呢?
// 当一批数据发送成功后, 回调这个函数, Exception ==null
// 当一批数据经过重试后, 依然失败, 也会回调这个函数, 此时 Exception不为null
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
// 如果 Exception 不为null, 表示数据经过重试后, 发送失败了
// 在这个代码块中编写失败后的业务代码即可
}
});设置缓存池:
props.put("buffer.memory", 33554432);
设置批量数据;
props.put("batch.size", 16384);
设置时间阈值:
props.put("linger.ms", 1);
多副本机制+ ack=-1
给予offset(数据消费的偏移量)
通过offset commit 来保证数据的不丢失,kafka自己记录了每次消费的offset数值,下次继续消费的时候,会接着上次的offset进行消费。
每一个消费者都会有一个groupid,将多个消费者放置到同一个groupid中
如果是第一次访问,我们默认从当前时间进行消费
开始监控数据,一旦有数据过来,直接获取
在broker端 会记录这着每一个消费者组消费的偏移量的信息,
当消费者消费完数据后, 需要通知给broker, broker更新偏移量的数据
当消费者出现宕机后, 从broker中获取上次的消费偏移量, 从这个偏移量开始消费即可偏移量记录记录在topic中的consumer_offset,默认有50个分区,1个副本
重复消费问题:
当consumer消费完成后,并没有即使响应给broker偏移量数据(宕机)
假设一个partition有10T数据
kafka中是多文件存储
kafka作为消息中间件,只负责消息的临时存储,并不是永久存储,需要删除过期数据
数据存储在一个文件中,需要删除数据变得非常麻烦,并且读写的效率比较慢,因为每次都需要打开一个大文件
切分为多个文件后,根据文件的日期属性删除即可
默认保留7天(168小时)
kafka数据多文件存储以segment段的形式,当segment段中的log文件达到1G,就会生产新的segment段
segment段的组成:
log文件:数据存储文件
index文件:索引文件,当前这个log文件的索引信息,提升查询效率.(存储log文件中消息对应的偏移量所在文件中的物理位置)
文件名:表示消息在文件中的其实偏移量
如何查询633254这条数据:?
1) 确定这条数据在哪个segment段(根据文件名确定)
2) 根据偏移量信息,到index文件中查询这个偏移量对应的log文件中数据的物理偏移量
3) 根据物理偏移量到log文件中查询数据
当producer生产数据的时候,消息应该发送给哪个分片?
kafka在数据生产的时候,有一个数据分发策略。默认的情况使用DefaultPartitioner.class类。
1) 轮询策略
当发送数据的时候,如果只发送value数据,默认采用轮询策略
2) hash取模计算
当发送数据的时候,必须传递key数据,即可使用hash取模 key.hashCoe()%分区数量
3)指定分区号
当发送数据,在producerRecord中指定分区号(分区好从零开始)
4)自定义分发策略
自定义一个类,继承partitioner,实现其中的方法即可,然后将自定义类配置到生产者中
props.put("partition.class","com.itheima.kafka.MyPartitioner");
前三中都是使用一个类:
ProducerRecord类实现分发
PartitionerClass获取分区
生产者每分钟生产120条数据,消费者每分钟消费40条数据,在kafka中造成大量数据堆叠无法消费
解决办法:多搞几个消费者,让消费者同属于一个消费者组(3个)
注意:规定一个topic中的一个分区的数据,只能被一个消费者组消费
当生产者每分钟发送160条数据!!!
消费者组中消费者的数量和partition数量最多保持一致,多出来的消费者将会一直处于空闲状态
解决方案: 增加分区的数量,增加消费速度
基于这个消费者概念, 其实通过kafka模拟一个点对点和发布订阅的方式:
点对点 : 一个生产者将数据生产到kafka中, 让多个消费者处于同一个消费者组中即可
发布订阅: 一个生产者将数据生产到kafka中, 让多个消费者都处于不同的组当中
cd /export/servers/flume/conf
vim spooldir_source_kafka_sink.conf
#为我们的source channel sink起名
a1.sources = r1
a1.channels = c1
a1.sinks = k1
#指定我们的source收集到的数据发送到哪个管道
a1.sources.r1.channels = c1
#指定我们的source数据收集策略
a1.sources.r1.type = spooldir
a1.sources.r1.spoolDir = /export/servers/flumedata
a1.sources.r1.deletePolicy = never
a1.sources.r1.fileSuffix = .COMPLETED
a1.sources.r1.ignorePattern = ^(.)*\\.tmp$
a1.sources.r1.inputCharset = GBK
#指定我们的channel为memory,即表示所有的数据都装进memory当中
a1.channels.c1.type = memory
#指定我们的sink为kafka sink,并指定我们的sink从哪个channel当中读取数据
a1.sinks.k1.channel = c1
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.topic = test
a1.sinks.k1.kafka.bootstrap.servers = node01:9092,node02:9092,node03:9092
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
启动flume
bin/flume-ng agent --conf conf --conf-file conf/spooldir_source_kafka_sink.conf --name a1 -Dflume.root.logger=INFO,console
启动kafka
nohup ./kafka-server-start.sh /export/servers/kafka/config/server.properties 2>&1 &
bin/kafka-console-consumer.sh --from-beginning --topic test --zookeeper node01:2181,node02:2181,node03:2181
将文件移动到flumedata中,查看消费者