1. 上单机DEMO
yaml
version: "2"
services:
kafka:
image: docker.io/bitnami/kafka:3.6
ports:
- "9092:9092"
volumes:
- "kafka_data:/bitnami"
environment:
# KRaft settings
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
# Listeners
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.0.102:9092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
kafka-ui:
image: 'provectuslabs/kafka-ui'
ports:
- '9099:8080'
environment:
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9092
- DYNAMIC_CONFIG_ENABLED=true
depends_on:
- kafka
volumes:
kafka_data:
driver: local
以上是从bitnami官方文档中截取出来的,解释下参数:
-
KAFKA_CFG_NODE_ID=0
: 指定 Kafka broker 的节点 ID。每个 Kafka broker 都有一个唯一的节点 ID。 -
KAFKA_CFG_PROCESS_ROLES=controller,broker
: 指定 Kafka broker 的角色。可选值broker
controller
broker,controller
-
broker: 数据复制、处理producer、consumer请求
-
controller: 负责集群的管理和协调,分区相关。
-
设置为
controller,broker
表示这个节点负责数据相关,同时可以成为controller,但并不一定!
-
-
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
: 负责选举的controller节点。格式:{node_id}@{node_host:port} -
KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
: 定义 Kafka broker 监听客户端连接的网络地址。在这里,broker 听取 9092 端口上的 PLAINTEXT 协议连接,而 CONTROLLER 听取 9093 端口上的连接。PLAINTEXT和CONTROLLER仅仅表示协议 -
KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.0.102:9092
: 定义 Kafka broker 对外宣传的监听地址。在这里,broker 在 192.168.0.102 地址的 9092 端口上 PLAINTEXT 协议连接。- 有些文章写 XXX://0.0.0.0:xx 表示可以被任意外部网络访问,但在
3.6
版本测试报错不允许使用0.0.0.0
,故必须配置为宿主机IP
- 有些文章写 XXX://0.0.0.0:xx 表示可以被任意外部网络访问,但在
-
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
: 定义 Kafka broker 监听器之间的安全协议映射。在这里,将 CONTROLLER 监听器映射到 PLAINTEXT 协议,将 PLAINTEXT 监听器映射到 PLAINTEXT 协议。- 协议可选值:
PLAINTEXT
、SSL
、SASL_PLAINTEXT
、SASL_SSL
- 协议可选值:
-
KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
: 指定 Kafka controller 监听器的名称。 -
KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
: 定义 broker 之间通信所使用的监听器的名称。在这里,broker 之间使用 PLAINTEXT listener 进行通信。
2. 再搞搞 Kraft 集群
3 Broker + 每个Topic 3个Partition + 每个 Partition 两个副本 (1leader+2foller)
yaml
version: '3'
services:
kafka-0:
image: bitnami/kafka:latest
container_name: kafka-0
networks:
- kafka-network
environment:
- KAFKA_KRAFT_CLUSTER_ID=abcdefghijklmnopqrstuv
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=BROKER://:9092,CONTROLLER://:9093
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,BROKER:PLAINTEXT
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=BROKER
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=BROKER://192.168.0.102:9092
- KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR=3
- KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_CFG_NUM_PARTITIONS=3
- KAFKA_CFG_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=3
- KAFKA_CFG_TRANSACTION_STATE_LOG_MIN_ISR=2
ports:
- "9092:9092"
kafka-1:
image: bitnami/kafka:latest
container_name: kafka-1
networks:
- kafka-network
environment:
- KAFKA_KRAFT_CLUSTER_ID=abcdefghijklmnopqrstuv
- KAFKA_CFG_NODE_ID=1
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=BROKER://:9092,CONTROLLER://:9093
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,BROKER:PLAINTEXT
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=BROKER
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=BROKER://192.168.0.102:9093
- KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR=3
- KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_CFG_NUM_PARTITIONS=3
- KAFKA_CFG_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=3
- KAFKA_CFG_TRANSACTION_STATE_LOG_MIN_ISR=2
ports:
- "9093:9092"
kafka-2:
image: bitnami/kafka:latest
container_name: kafka-2
networks:
- kafka-network
environment:
- KAFKA_KRAFT_CLUSTER_ID=abcdefghijklmnopqrstuv
- KAFKA_CFG_NODE_ID=2
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=BROKER://:9092,CONTROLLER://:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=BROKER
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,BROKER:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=BROKER://192.168.0.102:9094
- KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR=3
- KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
- KAFKA_CFG_NUM_PARTITIONS=3
- KAFKA_CFG_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=3
- KAFKA_CFG_TRANSACTION_STATE_LOG_MIN_ISR=2
ports:
- "9094:9092"
kafka-ui:
image: 'provectuslabs/kafka-ui'
container_name: kafka-ui
ports:
- '9099:8080'
environment:
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka-0:9092,kafka-1:9092,kafka-2:9092
- DYNAMIC_CONFIG_ENABLED=true
networks:
- kafka-network
depends_on:
- kafka-0
- kafka-1
- kafka-2
networks:
kafka-network:
上面把PLAINTEXT改为了BROKER,更语义化一些。解释下新出现的参数:
-
KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR=3
:- 这个参数指定了偏移量主题(_consumer_offsets ,默认带的主题)的复制因子。偏移量主题用于存储消费者组的消费进度信息,确保在发生故障时不会丢失消费者的进度。复制因子指定了此主题的副本数量,这里设置为3,表示偏移量主题将有3个副本。
-
KAFKA_CFG_DEFAULT_REPLICATION_FACTOR=3
:- 这个参数指定了Kafka中新创建主题的默认复制因子。当创建一个新的主题而没有显式地指定复制因子时,系统将会使用此默认值。在这里,设置为3,表示新创建的主题将会有3个副本。
-
KAFKA_CFG_NUM_PARTITIONS=3
:- 这个参数指定了每个主题的分区数。主题被分割成多个分区,以便实现并行处理和水平扩展。在这里,设置为3,表示每个新创建的主题将会有3个分区。
-
KAFKA_CFG_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=3
:- 这个参数指定了事务状态日志(transaction state log)的复制因子。事务状态日志用于存储Kafka事务的状态信息,确保在发生故障时不会丢失事务的状态。复制因子指定了此日志的副本数量,这里设置为3,表示事务状态日志将有3个副本。
-
KAFKA_CFG_TRANSACTION_STATE_LOG_MIN_ISR=2
:- 这个参数指定了事务状态日志的最小ISR(in-sync replicas)数。ISR是指能够跟上 leader 副本进度的副本集合。这个参数指定了 ISR 中必须包含的最少副本数量。在这里,设置为2,表示事务状态日志的最小ISR数量为2个。
最后,关于每个topic分区数的问题:引用佬的回答:一般来说如果有3个broker,那么至少就应该有3个分区,最小化保证每个broker都能参与到队列分流并行过程中。不过根据数据量的写入量,磁盘io的消耗占比,网络带宽的承载能力,我们可以适当增加每个broker的分区数,可以用每台broker分区最小数的倍数进行设置并测试吞吐,例如3,6,9,12...
3. 场景问题
3.1 顺序消息
一句话:生产者定key,消费者可以concurrency,可以batch拉取,但不要batch拉取后代码内批处理
3.2 消息不丢
生产者->Kafka服务端-> 消费者,箭头位置是消息可能丢失的地方,其余地方丢失几率不大或者不能叫做消息丢失
yaml
# 修改三个地方
spring:
kafka:
bootstrap-servers: xxx
producer:
acks: 1 # 0,1,-1/all # !
retries: 2 # 发送失败后重试两次 # !
listener:
ack-mode: manual_immediate # 手动确认 !
consumer:
解释:
- producer.acks: 生产者到Kafka服务器的确认策略
- 0:生产者在发出消息后不会等待任何来自服务器的确认。
- 1:仅leader写入成功后,发回确认
- -1/all:ISR所有元素成功写入则发回确认
- listener.ack-mode: 消费端手动确认
3.3 重复消息
一句话:kafka自带精确一次消费太大,我用业务端去保证(如数据库唯一键)
3.4 消息积压
定问题,加机器!
3.5 事务消息
kotlin
@Component
class KafkaProducer {
@Bean
fun txTemplate(default: DefaultKafkaProducerFactory<String, String>): KafkaTemplate<String, String> {
// 事务消息必须设置以下两个参数!覆盖一下默认配置
val map = mapOf(Pair("acks", "all"), Pair(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "tx_"))
val copy = default.copyWithConfigurationOverride(map)
return KafkaTemplate(copy)
}
@Bean
fun nonTxTemplate(default: DefaultKafkaProducerFactory<String, String>): KafkaTemplate<String, String> {
// val map = mapOf(Pair("acks", "1")) // 不设置也可以,配置文件就是默认配置
val copy = default.copyWithConfigurationOverride(map)
return KafkaTemplate(copy)
}
}
yaml
spring:
kafka:
bootstrap-servers: 127.0.0.1:9092
producer:
batch-size: 16KB
properties:
linger.ms: 500 # 最长等待发送时间
acks: 1 # 0,1,-1/all
retries: 2 # 发送失败后重试两次
listener:
ack-mode: batch # 自动提交
concurrency: 10
consumer:
isolation-level: read_committed # 确保事务消息不会脏读