客从远方来,遗我双鲤鱼。呼儿烹鲤鱼,中有尺素书。
——佚名《饮马长城窟行》
本文已同步CSDN、掘金平台、知乎等多个平台,图片依然保持最初发布的水印(如CSDN水印)。(以后属于本人原创均以新建状态在多个平台分享发布)
多年前,由于工作的性质,发现这系列没有写完,想了想,做人做事还是要有始有终。实在是借口太多了,太不像话了…由于时间过得太久了,这篇开始,可能很多技术以最新或最近的几个版本为主了。
在Kafka中,生产者与消费者之间传输消息时,通常需要对数据进行序列化和反序列化。常见的序列化方式如JSON或String存在以下问题:
而Avro作为一种高效的二进制序列化框架,通过Schema定义数据结构,可实现紧凑存储、动态兼容性和强类型校验,成为Kafka生态中推荐的序列化方案27。
Schema驱动
Avro要求所有数据必须与预定义的Schema文件(.avsc)匹配。Schema以JSON格式描述数据结构,例如:
{
"type": "record",
"name": "User",
"namespace": "com.example.avro",
"fields": [
{"name": "id", "type": "int"},
{"name": "name", "type": "string"}
]
}
然后使用 avro-maven-plugin
生成 Java 类:
org.apache.avro
avro-maven-plugin
1.11.0
generate-sources
schema
执行 mvn clean compile
后,com.example.avro.User
类会被自动生成。
生产者与消费者需共享同一Schema,确保序列化与反序列化的一致性。
二进制编码
Avro将数据转换为紧凑的二进制格式,相比JSON减少约30%-50%的存储与传输开销。例如,整型字段直接以二进制存储,无需字段名冗余7。
Schema Registry
为实现Schema动态管理,通常搭配Schema Registry(如Confluent或Apicurio)使用。其核心功能包括:
以下以Java代码为例,展示Kafka集成Avro的配置方法:
org.springframework.kafka
spring-kafka
org.apache.avro
avro
io.confluent
kafka-avro-serializer
7.2.1
运行 HTML
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", StringSerializer.class.getName());
props.put("value.serializer", KafkaAvroSerializer.class.getName());
props.put("schema.registry.url", "http://localhost:8081"); // Schema Registry地址
Producer producer = new KafkaProducer<>(props);
// 构建Avro消息
GenericRecord user = new GenericData.Record(schema);
user.put("id", 1);
user.put("name", "Alice");
producer.send(new ProducerRecord<>("user-topic", user));
------ SpringBoot框架 直接用配置application.yml 和生产者服务类--------------
spring:
kafka:
bootstrap-servers: localhost:9092
properties:
schema.registry.url: http://localhost:8081
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: io.confluent.kafka.serializers.KafkaAvroSerializer
@Service
public class UserProducer {
private final KafkaTemplate kafkaTemplate;
@Value("${kafka.topic.user}")
private String topic;
public UserProducer(KafkaTemplate kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void sendUser(User user) {
kafkaTemplate.send(topic, user.getId().toString(), user);
}
}
在 Spring Boot 启动后,我们可以使用以下代码发送一个 User 消息:
User user = User.newBuilder()
.setId(1)
.setName("Alice")
.build();
userProducer.sendUser(user);
控制台应该能够看到消费者成功接收到 User 数据
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "avro-consumer");
props.put("key.deserializer", StringDeserializer.class.getName());
props.put("value.deserializer", KafkaAvroDeserializer.class.getName());
props.put("schema.registry.url", "http://localhost:8081");
Consumer consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("user-topic"));
while (true) {
ConsumerRecords records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord record : records) {
System.out.println("Received: " + record.value().get("name"));
}
}
------ SpringBoot框架 直接用配置application.yml 和消费者服务类--------------
在 application.yml 中配置消费者参数:
spring:
kafka:
consumer:
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: io.confluent.kafka.serializers.KafkaAvroDeserializer
properties:
specific.avro.reader: true
然后编写 Kafka 消费者代码:
@Service
@KafkaListener(topics = "user_topic", groupId = "user_group")
public class UserConsumer {
@KafkaHandler
public void consume(User user) {
System.out.println("Received user: " + user.getName());
}
}
BACKWARD
),允许新增字段并设置默认值7。avro-maven-plugin
自动生成Java类,并确保生成路径在编译范围内2。DatumWriter
和DatumReader
实例,避免重复初始化开销7。Avro通过Schema定义与二进制编码,为Kafka提供了高效、类型安全的序列化方案。结合Schema Registry可实现动态兼容性管理,适用于复杂业务场景下的数据演进需求。实践中需注意Schema版本控制与性能调优,具体工具链配置可参考Confluent官方文档27。
下期预告,敬请关注:
(八)消息队列-Kafka 生产者