消息队列kafka常见问题

194 阅读6分钟

1.使用场景

业务场景

  1. 消息队列 - 解耦,异步,削封

    数据变更会触发很多业务的重新计算,通过异步调用进行解耦

  2. 数据处理,例如日志数据

特点

极致性能,生态兼容

为什么性能好

多分区并发 + 零拷贝(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. 1 个 Topic 只对应一个 Partition。
  2. (推荐)发送消息的时候根据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会进行检查去重。

重要参数

  1. acks
  • acks = 0 : 不接收发送结果
  • acks = all 或者 -1: 表示发送消息时,不仅要写入本地日志,还要等待所有副本写入成功。
  • acks = 1: 写入本地日志即可,是上述二者的折衷方案,也是默认值。
  1. retries
    • 默认为 0,即不重试,立即失败。
    • 一个大于 0 的值,表示重试次数。
  1. buffer.memory
    • 指定 producer 端用于缓存消息的缓冲区的大小,默认 32M;
    • 适当提升该参数值,可以增加一定的吞吐量。
  1. batch.size
    • producer 会将发送分区的多条数据封装在一个 batch 中进行发送,这里的参数指的就是 batch 的大小。
    • 该参数值过小的话,会降低吞吐量,过大的话,会带来较大的内存压力。
    • 默认为 16K,建议合理增加该值。

ISR

一些概念:

  • AR(Assigned Repllicas)一个 partition 的所有副本(就是 replica,不区分 leaderfollower
  • 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 总是 -1producer只管发不管发送成功与否。延迟低,容易丢失数据。
  • acks=1:表示 leader 写入成功(但是并没有刷新到磁盘)后即向 producer 响应。延迟中等,一旦 leader 副本挂了,就会丢失数据。
  • acks=all:等待数据完成副本的复制, 等同于 -1. 假如需要保证消息不丢失, 需要使用该设置. 同时需要设置 unclean.leader.election.enabletrue, 保证当 ISR 列表为空时, 选择其他存活的副本作为新的 leader.