1、简介
1.1、消息队列
消息队列一般用于消息的临时存取,遵循先入先出原则。常用的消息中间件:Kafka(大数据领域,性能高)、ActivateMQ、RabbitMQ、RocketMQ等。
应用场景:
- 异步处理
- 系统解耦
- 流量削峰
- 日志处理
生产者消费者模型
消息队列模式
-
点对点模式
-每个消息只有一个接收者,发送者和接收者没有依赖性,接收者在成功接收消息后需向队列应答成功。 -
发布/订阅模式
每个消息有多个订阅者,发布者和订阅者在时间上有依赖性。某主题的订阅者,必须先创建订阅者才能消费消息。同时,订阅者需要提前订阅该角色主题并保持在线。
1.2、Kafka
Kafka是一个分布式流平台。分布式流平台应包括:发布订阅数据流、冗余持久化数据流、处理数据流。各个消息队列对比。
工具:
- kafka可视化工具:Offset Explorer 2.0或Kafka Tool
- 监控Kafka集群工具:Kafka-eagle
2、下载安装
下载地址:https://kafka.apache.org/downloads
所有节点配置环境变量
先
vim /etc/profile
添加配置
export KAFKA_HOME=/export/soft/kafka_2.13-2.5.0
export PATH=$PATH:${KAFKA_HOME}
最后
source /etc/profile
版本根据Zookeeper的版本进行匹配,在kafka的lib目录有Zookeeper的jar包,通过jar查看对应的Zookeeper版本。
解压后修改config文件夹下的配置文件server.properties
broker.id=0
log.dirs=/export/soft/kafka_2.13-2.5.0/data
用scp -r命令分发文件夹到其他节点
scp -r kafka_2.13-2.5.0/ hadoop101:$PWD
scp -r kafka_2.13-2.5.0/ hadoop102:$PWD
分别将broker.id改为1和2
后台启动命令(所有节点)
nohup /export/soft/kafka_2.13-2.5.0/bin/kafka-server-start.sh /export/soft/kafka_2.13-2.5.0/config/server.properties &
停止命令
/export/soft/kafka_2.13-2.5.0/bin/kafka-server-stop.sh /export/soft/kafka_2.13-2.5.0/config/server.properties &
3、生产、消费消息测试
创建topic test
/export/soft/kafka_2.13-2.5.0/bin/kafka-topics.sh --create --bootstrap-server hadoop101:9092 --topic test
查看topic
/export/soft/kafka_2.13-2.5.0/bin/kafka-topics.sh --bootstrap-server hadoop101:9092 --list
创建生产者
/export/soft/kafka_2.13-2.5.0/bin/kafka-console-producer.sh --broker-list hadoop101:9092 --topic test
创建消费者
/export/soft/kafka_2.13-2.5.0/bin/kafka-console-consumer.sh --bootstrap-server hadoop101:9092 --topic test --from-beginning
可视化工具
Kafka tool或者Offset Explorer 2.0,两者差不多,可通过可视化界面查看Kafka集群信息和消息队列中的数据
4、Java操作Kafka
创建生产者
@Test
public void testKafkaProducer(){
Properties properties = new Properties();
properties.put("bootstrap.servers","192.168.132.100:9092");
properties.put("ack","all");
properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
ProducerRecord<String, String> record = new ProducerRecord<>("test", "testKafka2", "334455");
producer.send(record,new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e==null) {
System.out.println(recordMetadata.timestamp());
}else {
e.printStackTrace();
System.out.println("发送失败");
}
}
});
producer.close();
}
创建消费者
@Test
public void testKafkaConsumer(){
Properties properties = new Properties();
properties.put("bootstrap.servers","192.168.132.100:9092");
properties.put("group.id","test");
//自动提交offset
properties.put("enable.auto.commit","true");
//自动提交offset的时间间隔
properties.put("auto.commit.interval.ms","1000");
properties.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
properties.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
consumer.subscribe(Arrays.asList("test"));
while (true){
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(5));
for (ConsumerRecord<String, String> record : records) {
System.out.println(record);
}
}
}
5、架构组成
5.1、基础架构
5.2、broker
一个Kafka集群通常由多个broker组成,方便实现负载均衡和冗余。broker 是无状态的,由ZK管理集群状态,每个broker每秒可处理十万次读写。
5.3、消费者组consumer group
consumer group是kafka提供的可扩展且具有容错性的消费者机制。一个消费者组可包含多个消费者,组内消费者一起消费主题的所有分区数据。一个topic如果只有一个分区,那么这个分区只能被某个消费者消费。有多少分区就可以被同一组的多少个消费者消费。消费组中一个消费者可以消费多个分区,但一个分区不能被同一个消费组中的多个消费者消费。
5.4、分区partition
kafka的消息组织形式分为三级结构,topic->分区->消息
查看分区数
./kafka-topics.sh --zookeeper 127.0.0.1:2181 --describe --topic test
修改分区数
./kafka-topics.sh --zookeeper 127.0.0.1:2181 -alter --partitions 3 --topic test
可在config文件夹中修改server.properties中的num.partitions(默认分区数)和default.replication.factor(默认副本数)
可在可视化工具新建topic时设置分区数量和副本数量,分区会均匀分布在broker上达到负载均衡的效果。
分区架构
副本架构
一个主题对应多个分区,副本可以确保单个节点故障时,数据依然可用。
对比
HBase | Kafka |
---|---|
Region Server | broker |
Region | partition |
table | topic |
5.5、topic
主题是一个逻辑的概念,是唯一的必须标识符,一般主题包含某一类信息,消息不能被更改。
5.6、偏移量offset
offset记录下一条要发送给消费者的序号。默认存在ZK中。每个分区有一个offset用来标记消费到哪一条了。
6、幂等性
当kafka不具备幂等性,在ACK返回失败时会重复存储数据。
通过引入producer id和sequence number防止消息重复存储。当seq没有增加的情况下,不会存储消息。
7、分区和副本
7.1、生产者分区策略
- 轮询分区策略:key为null使用,数据会乱序
- 随机分区策略:基本不用,数据会乱序
- 按key分区策略:对key取hash对分区数取余,可能数据倾斜,注意key值,数据一定程度上有序
- 自定义分区策略
7.2、消费者rebalance机制
触发条件:consumer个数变化、订阅的topic个数变化、订阅的分区数变化。
rebalance时所有同一组中的consumer停止,直到rebalance完成。尽量不要触发rebalance。
7.2、消费者消费策略
-
range范围分配策略(默认)
确保每个消费者消费尽量均衡,n=分区数量/消费者数量,m=分区数量%消费者数量,前m个消费者消费n+1个,剩余消费者消费n个
-
RoundRobin轮询策略
按照字典顺序排序,通过轮询方式逐个将分区分配给每个消费者。
-
stricky粘性分配策略
在发生rebalance时,保留原消费者和分区的对应关系,将多出来的分区均匀分配给其他好用的consumer。
7.3、副本机制
当某个broker分区的数据丢失或故障,依然可保障数据可用。
ACK参数:当生产者生产消息时,写入到副本的要求严格程度。
ACK=0:不等broker确认直接发送下一条数据,性能最高,但可能数据丢失。
ACK=1:等leader副本确认后再发送下一条数据,性能中等,但可能数据丢失。(leader和follower是针对分区 而言的,只有leader能读写数据)
ACK=-1/all:等所有节点的副本都同步完再发送下一条数据,性能最差,数据不会丢。
8、Kafka原理
8.1、分区的leader和follower
每个topic都有多个分区和多个副本。每个分区有一个leader、0个或多个follower。在创建topic时,每个分区的leader会均匀分布在broker上。leader负责所有的读写操作,所有的follower拉取leader对应分区的数据并保存在日志数据文件中。在leader故障时,follower会被选举为leader。
分区的leader不一定和分区在同一个节点上,因为多个副本不一定选举谁为leader
8.2、AR、ISR、OSR
follower按照不同状态分为三类
- 分区的所有副本称为AR(Assigned Replicas 已分配的副本)
- 所有与leader副本保持一定程度同步的副本(含leader副本)组成ISR (In-SyncReplicas-在同步中的副本)
- follower副本同步滞后过多的副本组成OSR(Out-Of-SyncReplicas)
AR=ISR+OSR
8.3、leader的选举
controller:在Kafka启动时,会在所有broker中选择一个controller,leader、follower是针对分区而言的,而 controller是针对broker而言的。
创建topic、添加分区、管理副本、leader选举等管理工作都由controller处理。controller相当于集群中的master。
controller选举:
- 在Kafka集群启动时,每个broker都会去尝试注册成controller(ZK上的临时节点)
- 只有一个成功,其他节点注册成该节点的监视器
- 满足高可用,其他节点监视到controller崩溃,会重新注册为controller
controller选举leader:**(不通过ZK进行选举)
leader选举由controller决定,controller将leader的改变通过RPC方式进行通知,controller读取ISR,只要有一个副本好使,就选其中一个做leader,如果所有副本宕机后,新leader为-1。
leader负载均衡:在leader分配不均匀时,–partition可指定需要重新分配leader的partition编号
8.4、工作流程
1、数据写入流程
生产者先从zk找到leader,broker进程上的leader将消息顺序写入本地log中,follower从leader上拉取消息,写入本地log,并向leader发送ACK,leader接收到所有的ISR中Replica的ACK后,并向生产者返回ACK。
2、数据消费流程
两种消费流程:推(push)、拉(pull)。
offset自动提交即使该条数据未处理成功也会提交从而offset+1,原offset数据未被成功消费导致消息丢失。
当offset提交到ZK未成功,会导致重复消费问题。可将通过offset存到mysql利用事务机制防止重复消费。
拉模式:由消费者自己记录消费状态,每个消费者互相独立、顺序拉取每个分区消息。消费者可以按照任意的顺序消费信息。
推模式:消息队列记录所有消费的状态,某一条消息如果被标记为已消费,则消费者不能再次消费。
拉取数据流程:
每个消费者都可根据分配策略,获得要消费的分区。获取到消费者对应的offset,找到该分区的leader并拉取数据,消费者提交offset。
8.5、数据存储形式
一个topic由多个分区组成,一个分区由多个segment组成,一个segment由多个文件组成,segment由多个文件组成(数据文件log、稀疏索引index、时间索引timeindex)。
读取数据时,先根据全局的offset找到segment段,再将全局的offset转成段内的局部offset,根据局部offset从稀疏索引(index)找到数据位置,进行顺序读取。
数据定期清理,一次删除一个segment段的日志文件。
8.6、数据清理
日志删除是以segment段为单位进行定期清理的。
日志保留策略:1、基于时间(默认7天) 2、基于日志大小 3、基于日志起始偏移量