You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
6.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## Kafka:
### 消费者组到
#### __consumer_offsets 如果你曾经惊讶于 Kafka 日志路径下冒出很多 __consumer_offsets-xxx 这样的目录,那么现在应该明白了吧,这就是 Kafka 自动帮你创建的位移主题啊。
#### 位移主题分区 offsets.topic.num.partitions 它的默认值是 50
#### 位移主题的 Key 中应该保存 3 部分内容:<Group ID, 主题名,分区号> [groupId,topicName,partitionNumber]
#### 副本数或备份因子 offsets.topic.replication.factor 它的默认值是 3
#### enable.auto.commit如果值是 true则 Consumer 在后台默默地为你定期提交位移
auto.commit.interval.ms提交间隔
#### Log Compaction 策略来删除位移主题中的过期消息: The idea behind log compaction is selectively remove records where we have most recent update with the same primary key.
### 生产者压缩算法面面观
1. Producer
props.put("compression.type", "gzip");
2. Broker
情况一 Broker 端指定了和 Producer 端不同的压缩算法。
情况二 Broker 端发生了消息格式转换
3. Consumer
Kafka 会将启用了哪种压缩算法封装进消息集合中,这样当 Consumer 读取到消息集合时,它自然就知道了这些消息使用的是哪种压缩算法
Producer 端压缩、Broker 端保持、Consumer 端解压缩。
在实际使用中GZIP、Snappy、LZ4 甚至是 zstd 的表现各有千秋。但对于 Kafka 而言它们的性能测试结果却出奇得一致即在吞吐量方面LZ4 > Snappy > zstd 和 GZIP而在压缩比方面zstd > LZ4 > GZIP > Snappy。
### 生产者消息分区机制原理剖析
1. org.apache.kafka.clients.producer.Partitioner
int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
这里的topic、key、keyBytes、value和valueBytes都属于消息数据cluster则是集群信息
#### 分区策略
1. Round-robin
2. Randomness
3. Key-ordering
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return Math.abs(key.hashCode()) % partitions.size();
### 无消息丢失配置
##### 案例 1生产者程序丢失数据
1. producer.send(msg, callback)
2. 设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
3. 设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试
##### 案例 2生产者程序丢失数据
1. 设置 replication.factor >= 3。这也是 Broker 端的参数。其实这里想表述的是,最好将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余
2. 设置 min.insync.replicas > 1。这依然是 Broker 端参数,控制的是消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在实际环境中千万不要使用默认值 1
3. 确保 replication.factor > min.insync.replicas。如果两者相等那么只要有一个副本挂机整个分区就无法正常工作了。我们不仅要改善消息的持久性防止数据丢失还要在不降低可用性的基础上完成。推荐设置成 replication.factor = min.insync.replicas + 1。
#### 案例 3消费者程序丢失数据
1. 确保消息消费完成再提交。Consumer 端有个参数 enable.auto.commit最好把它设置成 false并采用手动提交位移的方式。就像前面说的这对于单 Consumer 多线程处理的场景而言是至关重要的。
维持先消费消息(阅读),再更新位移(书签)的顺序
### Kafka 拦截器
```
public interface ProducerInterceptor<K, V> extends Configurable {
ProducerRecord<K, V> onSend(ProducerRecord<K, V> var1);
//该方法会在消息发送之前被调用。如果你想在发送之前对消息“美美容”,这个方法是你唯一的机会。
void onAcknowledgement(RecordMetadata var1, Exception var2);
//该方法会在消息成功提交或发送失败之后被调用. onAcknowledgement 的调用要早于 callback 的调用
void close();
}
```
```
public interface ConsumerInterceptor<K, V> extends Configurable {
ConsumerRecords<K, V> onConsume(ConsumerRecords<K, V> var1);
//该方法在消息返回给 Consumer 程序之前调用
void onCommit(Map<TopicPartition, OffsetAndMetadata> var1);
//Consumer 在提交位移之后调用该方法
void close();
}
```
### Consumer Rebalance
1. Rebalance 影响 Consumer 端 TPS。这个之前也反复提到了这里就不再具体讲了。总之就是在 Rebalance 期间Consumer 会停下手头的事情,什么也干不了。
2. Rebalance 很慢。如果你的 Group 下成员很多,就一定会有这样的痛点。还记得我曾经举过的那个国外用户的例子吧?他的 Group 下有几百个 Consumer 实例Rebalance 一次要几个小时。在那种场景下Consumer Group 的 Rebalance 已经完全失控了。
3. Rebalance 效率不高。当前 Kafka 的设计机制决定了每次 Rebalance 时Group 下的所有成员都要参与进来,而且通常不会考虑局部性原理,但局部性原理对提升系统性能是特别重要的。
第一类非必要 Rebalance 是因为未能及时发送心跳,导致 Consumer 被“踢出”Group 而引发的。因此,你需要仔细地设置
session.timeout.ms 和 heartbeat.interval.ms 的值。我在这里给出一些推荐数值,你可以“无脑”地应用在你的生产环境中。
设置 session.timeout.ms = 6s
设置 heartbeat.interval.ms = 2s
要保证 Consumer 实例在被判定为“dead”之前能够发送至少 3 轮的心跳请求,即 session.timeout.ms >= 3 * heartbeat.interval.ms。将 session.timeout.ms 设置成 6s 主要是为了让 Coordinator 能够更快地定位已经挂掉的 Consumer。毕竟我们还是希望能尽快揪出那些“尸位素餐”的 Consumer早日把它们踢出 Group。希望这份配置能够较好地帮助你规避第一类“不必要”的 Rebalance
第二类非必要 Rebalance 是 Consumer 消费时间过长导致的。我之前有一个客户在他们的场景中Consumer 消费数据时需要将消息处理之后写入到 MongoDB。显然这是一个很重的消费逻辑。MongoDB 的一丁点不稳定都会导致 Consumer 程序消费时长的增加。此时max.poll.interval.ms 参数值的设置显得尤为关键。如果要避免非预期的 Rebalance你最好将该参数值设置得大一点比你的下游最大处理时间稍长一点。就拿 MongoDB 这个例子来说,如果写 MongoDB 的最长时间是 7 分钟,那么你可以将该参数设置为 8 分钟左右