14.1 Kafka架构 14.2 应用
14.1 Kafka架构
基于TCP,一个分布式流处理平台
处理大数据,scala编写高水平扩展,高吞吐量分布式消息系统
数据以Topic归类,Producer,Consumer
kafka集群由多个kafka实例组成,每个服务器称broker
依赖Zookeeper保证系统可用,为集群保存一些meta元信息
每条记录包含一个key,一个value和一个timestamp(保存文件,JAVA Object序列化)
kafka不支持事务,支持Zookeeper动态扩容,处理海量数据
4大API
Producers API (一个应用程序一串流式数据到一个或多个kafka topic)
Consumers API (一个应用程序订阅一个或多个topic,并对发布的流数据处理)
Stream API (一个应用程序作为一个流处理器,消费输入流,输入流到输出流有效转换)
Connectors API (允许构建并运行可重用的生产者或消费者,并将topic连到已存在程序或系统中(如连数据库,捕捉表变更内容))
后面具体介绍
Kafka类似AMQP协议
producer ->(push) broker (pull)<- consumer
Topics
Topics是数据主题,数据记录发布的地方。
Topics总是多订阅模式,一个topic可以有一或多消费者订阅他数据
每个topic会维持一个分区(partition)日志
writes写入多个Partition
Parition1: 0 1 2 3 4 5 <--
Parition2: 0 1 2 3 4 <-- writes
Parition3: 0 1 2 3 <--
Paritition
0 1 2 3 4 5 6 <-- (producers writes追加7写入)
ConsumerA的offset可能为4,ConsumerB的offset可能为6
老版本offset存在Zookeeper中,zk压力太大。新版offset由消费者自己维护,zk记录最后一次offset
日志分区分布集群到多个服务器上,每个服务器处理它的分区。根据配置每个分区还可以复制到其他服务器上备份容错。每个分区有一个leader,零或多个follower。leader处理读写,follower被动复制数据。一台服务器可能同时一个分区leader,又是另一个分区follower来达到平衡负载。
Producers
负责发布到Topic上哪一个分区,开发者选择分区算法。默认Hash轮询
Consumers
1 所有消费者在同一个group中,消费记录会负载均衡每一个消费者实例
2 所有消费者不在同一group中,会广播所有消费者进程。Parition只对同一组group中一个消费者有效
架构
kafka支持点对点(1对1)模式,和发布订阅(多对多)模式
因为队列,所以顺序消费,每个partition只能由消费者group中一个消费者消费
一个paritition可以被不同group中两个消费者消费,但不能被同一个group中两个消费者消费
14.2 应用
服务器上kafka配置启动
1 下载kafka并解压
2 启动kafka
配置参数在文件中
broker.id = ~
log.dirs = ~ //kafka日志存放路径
num.partitions= 1 //topic在当前broker上分区个数
delete.topic.enable = true
zookeeper.connect = localhost:2180 //zk集群地址
启动
bin/kafka-server-start.sh config/server.properties (集群配置文件)
3 创建topic
创建一个叫test的topic,配置一个分区,一个副本
.>bin/kafka-topics.sh--create-zookeeper localhost:2180--replication(副本)--factor 1--partitions(分区) 1--topic test(名称)
4 发送消息
>bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
>输入hello
5 消费者接受消息
>bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
>输出hello
JAVA端应用
Producer
public static void producerSend(){
Properties properties = new Properties();
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.220.128:9092");
properties.put(ProducerConfig.ACK_CONFIG,"all");
properties.put(ProducerConfig.BATCH_SIZE_CONFIG,"1024");
...//properties配置信息
Producer<String,String> producer = new KafkaProducer<>(properties); //主对象
ProducerRecord<String,String> record = new ~<>(TOPIC_NAME,0,"value"); //消息对象
//1 正常发送消息
producer.send(record);
//2 future返回异步对线发送消息
Future<RecordMetadata> send = producer.send(record);
RecordMetadata record = send.get();
System.out.println(record.partition()+record.offset());
//3 异步回调发送
producer.send(record,new Callback(){
@Override
~ onCompletion(RecordMetadata, Execption ~){
//返回record.partition()和record.offset()
}
})
}
Producer简单源码
线程安全
1 MetricConfig配置信息
2 加载负载均衡器 (partition)
3 初始化Serializer (序列化)
4 初始化 RecordAccumlator 类似计数器
5 启动newSender 守护线程,线程安全,批量发送
producer.send(record)
计算分区,消息进哪个分区
计算批次,accumlator.append批量发送
守护线程,批量到一定数量则发送
自定义分区负载均衡
properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,"xxx.samplePartition");
public class samplePartition implements Partitioner{
@Override{
//具体逻辑
}
}
消息传递保障
properties.put(ProducerConfig.ACK_CONFIG,"all");
ACK有 0,1,all三个选择
0表示发出后就不管了
1表示必须获得leader响应(如果follwer还未备份,leader挂了,数据会丢失)
all表示必须获得leader和follower的响应
Consumer
//1 自动提交
Properties props = new ~;
props.setProperty("bootstrap.servers","localhost:9092");
props.setProperty("group.id","test");
props.setProperty("enable.auto.commit","true");
...
KafkaConsumer<String,String> consumer = new ~(props);
//consumer.subscribe(某topic) 也可以订阅某topic下某个分区
while(true){
ConsumerRecord<String,String> record = consumer.poll(Duration.ofMills(10000));
输出记录offset key value
}
//2 手动提交
props.setProperty("enable.auto.commit","false");
while(true){
ConsumerRecord<String,String> records = consumer.poll(Duration.ofMills(10000));
for(ConsumerRecord<String,String> record:records){
//保存数据到MYSQL,如果保存失败,则不提交
consumer.commitAsync();//保存成功则手动提交
}
}
//手动提交offset并控制partition
while(true){
ConsumerRecord<String,String> records = consumer.poll(Duration.ofMills(10000));
for(TopicPartition partition : records.partitions()){//分区单独处理
ConsumerRecord<String,String> pRecord = records.records(partition);
for(ConsumerRecord<String,String> record : pRecord){
//单个分区中offset并提交
Map<TopicPartition,OffsetAndMetadata> offset = new ~;
offset.put(partition,pRecord.get(pRecord.size()-1).offset()+1)
consumer.commitSync(offset);
}
}
}
kafka Consumer线程不安全,需要自己解决
1 每个线程单独创建一个kafkaConsumer保证线程安全,但是会创建太多
2 用线程池
Consumer限流
拿桶令牌
zookeeper会自动动态扩容
Kafka Stream
是处理分析存储在kafka数据客户端程序库,通过state store可高效状态操作。支持processor和高层抽象DSL
各种快捷的流操作
Kafka Connector
连接SQL等,下载connector包,配置mysql写入kafkaConnectSource,kafka可写入mysql
Kafka下领导者选举
kafka不是投票选举,因为大数据速度慢,分区消息也会不一致
kafka动态维护一组leader数据副本 (ISR), leader挂了后,ISP中谁和Zookeeper连接快谁当leader
如果leader和ISR都挂了,会unclean leader选举,一般禁用。手动知道最小ISR
相关问题
什么是Producer、Consumer、Broker、Topic、Partition?
ack三种机制
Zookeeper对于kafka作用
kafka高可用机制
上面都已经回答过
kafka如何不重复消费数据 -- 每个消费者会记录自己offset,zk也会保存他们最终offset
怎么保证消息队列消费的幂等性
--偶尔offset失灵了,比如redis天然幂等,mysql可以检查是否已经存在这个数据
--kafka也提供了保证幂等性的配置,会通过执行的唯一id和是否已执行过两个变量来处理。
当配置事务时,自动开启幂等性配置
kafka分布式下如何保证消息顺序消费
-一个topic只有一个partition,不推荐
--通过相同key值定义,都是一个key可以保证都在同一个partition上并按照顺序排列
Kafka 如何保证消息不丢失 -- future异步/回调
kafka主从同步机制
0.11版本前根据水位线,水位线代表所有从节点都已经同步到的数据点。之后改成leader epoch,主节点维护leader epoch并保存在zk中
主未收到从fetch请求,或者一段时间仍赶上leader同步速度则剔除该从节点
kafka吞吐量高?
零拷贝,顺序写入(不是mysql那种插入写入),
每个log有专门index文件可以通过offset index快速定位