1.使用场景
业务场景
-
消息队列 - 解耦,异步,削封
数据变更会触发很多业务的重新计算,通过异步调用进行解耦
-
数据处理,例如日志数据
特点
极致性能,生态兼容
为什么性能好
多分区并发 + 零拷贝(DMA,SendFile)+顺序读写
先写操作系统的页缓存(Page Cache),然后由操作系统自行决定何时刷到磁盘。
2.分区
Partition(分区) : 用于提升并发度,Partition 属于 Topic 的一部分。一个 Topic 可以有多个 Partition ,并且同一 Topic 下的 Partition 可以分布在不同的 Broker 上
Replica: (副本):多副本提升系统可靠性
副本间分为leader和follower,先把消息发给leader,在同步给follower,leader故障后会重新选择leader
3.ZK和kafka
提供了kafka元数据管理的功能
Broker 注册 :在 Zookeeper 上会有一个专门用来进行 Broker 服务器列表记录的节点。每个 Broker 在启动时,都会到 Zookeeper 上进行注册,即到/brokers/ids 下创建属于自己的节点。每个 Broker 就会将自己的 IP 地址和端口等信息记录到该节点中去
Topic 注册 : 在 Kafka 中,同一个Topic 的消息会被分成多个分区并将其分布在多个 Broker 上,这些分区信息及与 Broker 的对应关系也都是由 Zookeeper 在维护。比如我创建了一个名字为 my-topic 的主题并且它有两个分区,对应到 zookeeper 中会创建这些文件夹:/brokers/topics/my-topic/Partitions/0、/brokers/topics/my-topic/Partitions/1
负载均衡 :上面也说过了 Kafka 通过给特定 Topic 指定多个 Partition, 而各个 Partition 可以分布在不同的 Broker 上, 这样便能提供比较好的并发能力。 对于同一个 Topic 的不同 Partition,Kafka 会尽力将这些 Partition 分布到不同的 Broker 服务器上。当生产者产生消息后也会尽量投递到不同 Broker 的 Partition 里面。当 Consumer 消费的时候,Zookeeper 可以根据当前的 Partition 数量以及 Consumer 数量来实现动态负载均衡。
4.顺序消费
Kafka 只能为我们保证统一哥 Partition(分区) 中的消息有序,Kafka 通过偏移量(offset)来保证消息在分区内的顺序性。
- 1 个 Topic 只对应一个 Partition。
- (推荐)发送消息的时候根据key指定 Partition。保证相同userId的请求发送到同一个partition
5.消息丢失
生产者
生产者发送的消息可能会由于网络波动,没有发送出去,使用带有callback的api发送消息,可以通过get方法获取发送结果
配置重 试次数,一般配置为3次
消费者
默认自动提交offset,可能会导致没有消费却被提交了
手动提交:关闭自动提交,消息处理完成后,手动提交,问题:可能导致重复消费,需要保证幂等性。
如何保证幂等性:通过数据库操作日志表,记录完成的操作
Kafka丢失
数据发送给broker后,写入磁盘之前,走的是操作系统缓存,异步刷盘可能会导致数据丢失
假如 leader 副本所在的 broker 突然挂掉,那么就要从 follower 副本重新选出一个 leader ,但是 leader 的数据还有一些没有被 follower 副本的同步的话,就会造成消息丢失。
设置 acks = all
解决办法就是我们设置 acks = all。acks 是 Kafka 生产者(Producer) 很重要的一个参数。
acks 的默认值即为1,代表我们的消息被leader副本接收之后就算被成功发送。当我们配置 acks = all 代表则所有副本都要接收到该消息之后该消息才算真正成功被发送。
设置 replication.factor >= 3
为了保证 leader 副本能有 follower 副本能同步消息,我们一般会为 topic 设置 replication.factor >= 3。这样就可以保证每个 分区(partition) 至少有 3 个副本。虽然造成了数据冗余,但是带来了数据的安全性。
设置 min.insync.replicas > 1
一般情况下我们还需要设置 min.insync.replicas> 1 ,这样配置代表消息至少要被写入到 2 个副本才算是被成功发送。min.insync.replicas 的默认值为 1 ,在实际生产中应尽量避免默认值 1。
消息重复消费
生产者
通过开启生产者的幂等性完成
props.put(“enable.idempotence”, true) ,默认是false不开启
原理:发送消息的时候同时生成pid和sequenceNumber,broker会进行检查去重。
重要参数
- acks
- acks = 0 : 不接收发送结果
- acks = all 或者 -1: 表示发送消息时,不仅要写入本地日志,还要等待所有副本写入成功。
- acks = 1: 写入本地日志即可,是上述二者的折衷方案,也是默认值。
- retries
-
- 默认为 0,即不重试,立即失败。
- 一个大于 0 的值,表示重试次数。
- buffer.memory
-
- 指定 producer 端用于缓存消息的缓冲区的大小,默认 32M;
- 适当提升该参数值,可以增加一定的吞吐量。
- batch.size
-
- producer 会将发送分区的多条数据封装在一个 batch 中进行发送,这里的参数指的就是 batch 的大小。
- 该参数值过小的话,会降低吞吐量,过大的话,会带来较大的内存压力。
- 默认为 16K,建议合理增加该值。
ISR
一些概念:
AR(Assigned Repllicas)一个partition的所有副本(就是replica,不区分leader或follower)ISR(In-Sync Replicas) 能够和Leader保持同步的follower + leader本身 组成的集合。OSR(Out-Sync Relipcas)不能和Leader保持同步的follower集合
分区中的所有副本统称为AR(Assigned Repllicas)。所有与leader副本保持一定程度同步的副本(包括Leader)组成ISR(In-Sync Replicas),ISR集合是AR集合中的一个子集
与leader副本同步滞后过多的副本(不包括leader)副本,组成OSR(Out-Sync Relipcas),由此可见:AR=ISR+OSR。
AR、HW、LEO、LSO、LW的含义
这里就不得不提 Producer 的一个重要的参数:acks
acks=0:不需要等待服务器的确认. 这是retries设置无效. 响应里来自服务端的offset总是-1,producer只管发不管发送成功与否。延迟低,容易丢失数据。acks=1:表示leader写入成功(但是并没有刷新到磁盘)后即向producer响应。延迟中等,一旦leader副本挂了,就会丢失数据。acks=all:等待数据完成副本的复制, 等同于-1. 假如需要保证消息不丢失, 需要使用该设置. 同时需要设置unclean.leader.election.enable为true, 保证当ISR列表为空时, 选择其他存活的副本作为新的leader.