一、简介
分布式的消息和订阅系统,高性能、高吞吐量。
内置分区(对数据做分片处理)、实现集群、有容错能力和数据复制能力。
二、产生背景
领英要对用户的行为进行统计。
三、应用场景
行为跟踪:收集用户的操作行为。可以根据爱好做推送。
日志收集。
四、术语
Topic
存储消息的逻辑概念。Partition
- 每个
topic
可以划分多个分区。 - 相同
topic
下的不同分区的消息是不同的。如果是集群,分区会均分在集群机子上。
设置分区目的:减少消息容量,从而提升io性能。
五、架构
一个kafka
集群会包含多个broker
,它又依赖zookeeper
的集群,去实现broker
之间的master-slaver
机制。
多个producer
,多个consumer
。
消费者去broker
里pull消息的。而mq
是主动推送给消费者。
六、消息的同步发送和异步发送
- 异步发送:
kafka1.0以后,默认的client使用的都是异步发送消息。消息通过kafka producer发送后,这个消息放到了后台一个消息队列里,然后通过一个线程不断的从队列里取出消息进行发送。消息发送成功后,会进行一个callback回调。 - 同步发送:
通过future和get。get方法是个阻塞。同步去获得结果。
七、消息分发策略
消息由key
和value
组成,key
是可选项,producer
会根据key
和partition
机制来判断当前这条消息应该放到哪个partition
里面。默认算法是哈希取模。如果key
为null,则随机分配,根据metadata.max.age.ms
来,十分钟更新一次。
分区分配策略:
Range
(范围) -> 默认
针对于同一个topic
中的多个partition
而言的。首先会对这个topic
中的partition
进行排序,然后partition
数量除以consumer
数量,加入有0-9个partition
,3个consumer
,那consumer1会消费前4个分区。如果是多个topic
,那每次多消费的都是第一个。- 轮询
Round-Robin
把所有的partition
和consumer
数量列出来,然后按照hashcode
进行排序,通过轮询算法分配partition
和consumer
。
也可以自定义分发规则,implements Partitioner。
如果增减consumer
、broker
、partition
,会导致rebalance
。
如:
1、对于同一个consumer group
,新增consumer
2、consumer
离开
3、consumer
取消订阅
4、topic
中新增分区
谁来执行rebalance
,以及管理consumer group
?
coordinator
八、消息的存储策略
- 消息保存的路径
默认tmp,也可以自定义。 - 消息的写入性能
顺序写入
零拷贝 - 消息的存储机制
日志分段,方便清理和压缩。根据时间(默认保留7天)或者 大小,满足其中之一,就会被清理掉。
九、Partition副本机制
分区是对数据内容的分片,每个分区里的内容不一样,当一个分区不可用时,有一部分消息就没办法消费。所以为了提高分区的可用性,就实现了冗余的备份,就是副本。如果有多个副本,一定会有个leader副本和follower副本。命令中通过--replication-factor
参数去设置。
第i个分区的第j个副本,会落在 第 (i+j)% broker counts 个broker上。
查看分区状态
get /brokers/topics/topic_name/partitions/partition_num/state
其中 isr
维护的是当前分区,所有的副本集。follower
的内容必须跟leader
的在一定阈值范围内保持一致,如果不一致,就会被踢出去,直到follower
的内容与leader
内容保持在一定阈值范围内,follower
才会被加进来。
leader
副本 负责接收客户端的写入和读取请求。
follower
副本 负责从leader
副本中读取数据。
十、消费消息
consumer
可以指定消费哪个partition
。如果不指定,会按照一定的策略进行负载,比如三个consumer
、三个partition
,就一个consumer
消费一个partition
。如果consumer
数量小于partition
数量,则有的consumer
会多消费一些partition
。如果consumer
数量大于partition
数量,则有的consumer
会消费不到消息,会造成一定的浪费,所以不建议设置太多的consumer
。因为在一个partition
上是不允许并发的。consumer
的数量最好是partition
数量的整数倍。如果consumer
从多个partition
上读到消息,是不保证顺序的。
十一、集群
集群的构建是基于zookeeper
的。
修改config/server.properties
中三个地方:
zookeeper
的地址。broker.id
(在kafka集群中必须是唯一的)。listeners
,是为了各个节点互相通信,所以需要写各个节点自己的ip。
启动集群各节点kafka
后,会看到zookeeper
上多了几个节点。
启动zookeeper
客户端:
./zookeeper-3.5.4-beta/bin/zkCli.sh
查看所有kafka集群节点的id:
ls /brokers/ids
查看kafka主节点:
get /controller
写请求会进入master
节点,读请求进入其他节点。
选举规则:最小的节点,也就是最早注册的节点是leader
。
十二、使用api
- 引入kafka依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.1.0</version>
</dependency>
- 消息发送端
(1) 设置kafka配置信息(集群地址、序列化),创建KafkaProducer。
可设置参数如:
-
ProducerConfig.ACKS_CONFIG
,
0:消息发送给broker以后,不需要确认(性能较高,但是数据会丢失)。
1:只需要获得kafka集群中leader节点的确认,即可返回。
all(-1):需要集群中的所有节点确认。(最安全,性能最低的) -
batch.size
(默认16kb)
producer对于同一个分区来说,会按照batch.size的大小进行统一收集后,批量发送。 -
linger.ms
按时间间隔,进行统一收集后,批量发送。
如果都设置了,满足其中任意一个条件,消息就会立马发送。
为了解决大量的小数据包频繁的发送,这个问题。 -
max.request.size
(默认1M)
控制请求的大小
(2) 通过send方法发送消息,入参为ProducerRecord(topic,message)。
- 消息接受端
(1) 设置kafka配置信息(集群地址、序列化),创建KafkaConsumer。
不同的组,只要订阅了,每个组都可以获取消息。
同一个组内的消费者们,只能有一个消费者能获取到消息,其他成员不会获取到。
可设置参数如:
AUTO_OFFSET_RESET_CONFIG
:
earliest:对于新的group id来说,它会从最早的消息开始消费。对于已经消费过消息的group id来说,它还是会从已经消费过的最大的offset里去取。
latest:对于新的group_id来说,直接从已经消费过并且提交的最大的偏移量开始取。
ENABLE_AUTO_COMMIT_CONFIG
:自动提交AUTO_COMMIT_INTERVAL_MS_CONFIG
:自动提交的间隔毫秒
就是说每xx毫秒,对这个时间段内的所有消息,进行提交确认。
消息消费完以后,要进行提交确认。如果设置为false的话,消费一次后,还允许再次消费。设置为true,则消费一次后,不会被再次消费到。MAX_POLL_RECORDS_CONFIG
:每一次调用poll,获取到的消息数。这样可以根据消费端的处理性能,来预设一个数量,减少poll的次数,提升性能。
(2) 通过subscribe方法订阅消息。