作者介绍:简历上没有一个精通的运维工程师。请点击上方的蓝色《运维小路》关注我,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。
我们上一章介绍了中间件:Zookeeper,本章将介绍另外一个中间件:Kafka。目前这2个中间件都是基于JAVA语言的。
我们上前面介绍了Topic的基本概念和涉及到Topic核心的分区和副本概念,但是我们还得往里面写入数据才行,然后数据写进入以后我们还得把里面的数据读出来,我们今天首先介绍的负责向Kafka写入消息角色:生产者(Producer)。
生产者(Producer)
将数据(消息)发布到 Kafka 的 Topic 中的Leader分区里面。生产者可以发送带有或不带有键的消息,并且可以选择这些消息应该被发送到哪个分区。下面是几个关于消息的核心特性。
消息路由
-
轮询(Round Robin):当消息没有指定 key 时,生产者会采用轮询的方式均匀地将消息分布到所有的分区上。
-
哈希(Key-based):如果消息包含了一个 key,那么生产者会根据这个 key 的哈希值选择一个分区。默认情况下,Kafka 使用 murmur2 算法计算哈希值,然后对分区总数取模以确定具体的分区。
-
自定义分区策略:实现
Partitioner
接口自定义逻辑。
生产者首先需要连接到 Kafka 集群中的一个或多个 Broker,并获取关于目标 Topic 的元数据信息,包括该 Topic 所有分区及其对应的Leader Broker。根据上述的路由策略,生产者会选择合适的目标分区,并直接与该分区所在的 Leader Broker 建立连接,将消息发送出去。也就是链接Broker和发送的Topic所在Broker并没有直接关系。
可靠性保证
-
acks=0
:生产者不会等待任何来自服务器的确认。这意味着如果发送过程中发生错误(例如网络故障),则消息可能会丢失。 -
acks=1
:生产者会等待 Leader 副本确认收到消息后才认为发送成功。这种模式下,只要 leader 副本写入成功,即使后续副本同步失败也不会影响消息的确认状态,但是存在 leader 宕机且未完成副本同步的风险,可能导致数据丢失
-
acks=all
:生产者会等待 ISR 中的所有副本都确认收到了消息后才认为发送成功。提供了最高的可靠性保障,但同时也可能增加消息发送的延迟。
重试机制
生产者可以通过配置 retries 参数启用自动重试机制。当遇到临时性的发送失败(如网络抖动)时,生产者可以尝试重新发送消息。结合 retry.backoff.ms 参数,可以设置每次重试前的等待时间,避免过于频繁地重试造成更大的压力。
高性能优化
-
批处理(Batching):生产者将多条消息合并为一批发送,减少网络开销(通过
linger.ms
和batch.size
控制)。 -
压缩(Compression):支持
gzip
、snappy
、lz4
等压缩算法减少网络传输量。这里需要注意,生产者和消费者必须同时支持相同的算法,并不需要服务端支持。gzip 压缩率高,但 CPU 开销大,可能增加生产者和消费者的延迟。lz4/snappy 压缩速度快,适合高吞吐低延迟场景。
发送消息
#或者可以多次发送不同或者相同的内容
echo "hello world" | ./bin/kafka-console-producer.sh \
--topic my-topic \
--bootstrap-server 192.168.31.143:9092
也可以进入交互模式再输入内容。
# 启动控制台生产者(带 Key 支持)
./bin/kafka-console-producer.sh \
--topic my-topic \
--bootstrap-server 192.168.31.143:9092 \
--property "parse.key=true" \
--property "key.separator=:"
# 进入交互模式后,输入以下消息(按回车发送):
> user1:Hello Kafka
> order123:{"id": 123, "status": "created"}
> sensor01:25.5
下面是使用程序代码发送。
#python3代码 需要下安装 kafka模块
#pip3 install kafka -i https://mirrors.aliyun.com/pypi/simple/
import time
import json
from datetime import datetime
from kafka import KafkaProducer
from kafka.errors import KafkaError
# 配置 Kafka 生产者
producer = KafkaProducer(
bootstrap_servers=['192.168.31.143:9092','192.168.31.144:9092','192.168.31.145:9092'], # Kafka 服务器地址
value_serializer=lambda v: json.dumps(v).encode('utf-8') # 将消息序列化为 JSON
)
topic = 'my-topic1' # 目标 Topic
try:
count = 0
while True:
# 生成消息内容(示例:包含时间戳和计数)
message = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"count": count,
"data": f"Message-{count}"
}
# 发送消息(异步)
future = producer.send(topic, value=message)
# 可选:等待消息发送完成(同步确认)
# record_metadata = future.get(timeout=10)
# print(f"发送成功 → 分区: {record_metadata.partition}, 偏移量: {record_metadata.offset}")
print(f"已发送: {message}")
count += 1
time.sleep(1) # 每秒发送一次
except KafkaError as e:
print(f"Kafka 错误: {e}")
except KeyboardInterrupt:
print("用户终止程序")
finally:
producer.close() # 关闭生产者
如果系统不存在这个Topic,它将自动创建,默认只有1个分区和1个副本,这2个参数我们前面也讲过。
#副本数量,默认是1,参数默认不存在,如果要修改配置需要手工添加
default.replication.factor
#分区数量,默认是1,参数默认存在
num.partitions
#如果生产者向不存在的Topic发送消息,
#默认是true,就是允许创建Topic
#参数默认不存在
auto.create.topics.enable
通过下面的命令可以查看到各个分区里面有多少数据。
#查看topic的消息数量,一般不会用,这里为了方便统计才使用
./bin/kafka-run-class.sh \
kafka.tools.GetOffsetShell \
--broker-list 192.168.31.143:9092
--topic my-topic123 \
--time -1
[root@localhost kafka_2.13-2.8.2]# ./bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list 192.168.31.143:9092 --topic my-topic123 --time -1
OpenJDK 64-Bit Server VM warning: If the number of processors is expected to increase from one, then you should configure the number of parallel GC threads appropriately using -XX:ParallelGCThreads=N
my-topic123:0:0
my-topic123:1:0
my-topic123:2:0
运维小路
一个不会开发的运维!一个要学开发的运维!一个学不会开发的运维!欢迎大家骚扰的运维!
关注微信公众号《运维小路》获取更多内容。