SpringBoot3 使用配置文件整合 Apache Avro

1、介绍

Apache Avro 是一种高效的数据序列化系统,用于在不同的应用程序和平台之间传输和存储数据。它提供了一种紧凑且高效的二进制数据编码格式,相比其他常见的序列化方式,Avro能够实现更快的序列化和更小的数据存储。

而Confluent Schema Registry是由Confluent公司提供的一个开源组件,旨在解决分布式系统中的数据模式演化和兼容性的问题。它是建立在Apache Avro之上的一个服务,可以用于集中管理和存储Avro数据的模式(Schema),确保分布式系统中的数据一致性和兼容性。它广泛应用于事件流处理平台(如Kafka),为数据流的可靠性和互操作性提供了支持。

本文将介绍如何在Spring Boot应用程序中整合Apache Avro和Confluent Schema Registry,以实现高效的数据序列化和管理。

本文代码示例: GitHub仓库地址


2、Confluent Schema

1、下载

软件下载地址:Previous Versions - Confluent

本次使用:confluent-community-7.3.3 社区版,下载上传至Linux解压。

2、修改配置

修改配置文件:在 confluent-7.3.3/etc 文件夹下

confluent-7.3.3/etc/schema-registry/schema-registry.properties

# 配置Confluent Schema Registry 服务的访问IP和端口
listeners=http://0.0.0.0:8081

# 修改 Kafka集群指定引导服务器
kafkastore.bootstrap.servers=PLAINTEXT://xx.xx.xx.xx:9092,xx.xx.xx.xx:9192
# kafkastore.connection.url 配置zookeeper地址方式已弃用 

# 存储 schema 的 topic
kafkastore.topic=_schemas

# If true, API requests that fail will include extra debugging information, including stack traces
debug=false

3、启动

./bin/schema-registry-start ./etc/schema-registry/schema-registry.properties

4、验证

curl -X POST 'http://localhost:8081/subjects/topic-test/versions' \ 
	 -H "Content-Type: application/vnd.schemaregistry.v1+json" \
     -d '{"schema": "{\"type\": \"string\"}"}'

 返回结果:{"id":1}

2、Springboot整合

1、引入xml

    <dependencies>    
		<!--kafka-->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>3.0.7</version>
        </dependency>

        <!--avro-->
        <dependency>
            <groupId>org.apache.avro</groupId>
            <artifactId>avro</artifactId>
            <version>1.11.1</version>
        </dependency>
        <dependency>
            <groupId>io.confluent</groupId>
            <artifactId>kafka-avro-serializer</artifactId>
            <version>7.4.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.confluent/kafka-schema-registry-client -->
        <!--schema-registry-client-->
        <dependency>
            <groupId>io.confluent</groupId>
            <artifactId>kafka-schema-registry-client</artifactId>
            <version>7.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
    </dependencies>

    <!--schema-registry-client 远程仓库-->
    <repositories>
        <!-- other maven repositories the project -->
        <repository>
            <id>confluent</id>
            <url>https://packages.confluent.io/maven/</url>
        </repository>
    </repositories>

2、导入Avro构建插件

  <plugin>
      <groupId>org.apache.avro</groupId>
      <artifactId>avro-maven-plugin</artifactId>
      <version>1.11.1</version>
      <executions>
          <execution>
              <phase>generate-sources</phase>
              <goals>
                  <goal>schema</goal>
              </goals>
              <configuration>
                  <!--schema 文件所在目录-->
                  <sourceDirectory>${project.basedir}/src/main/resources/avro/</sourceDirectory>
                  <!--根据schema 文件生成的类文件目录-->
                  <outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
              </configuration>
          </execution>
      </executions>
  </plugin>

3、生成实体类

  • 在 ${project.basedir}/src/main/resources/avro/ 下创建 user.avsc文件。

  • 通过 Maven mvn compile 命令生产实体类

{
  "namespace": "com.jinunn.kraft.avro",  // 实体类存放路径
  "type": "record",
  "name": "User",     // 实体类文件名
  "fields": [       // 实体类属性
    {
      "name": "id",  // 属性名
      "type": "int"  // 类型
    },
    {
      "name": "name",
      "type": "string"
    },
    {
      "name": "age",
      "type": "int"
    }
  ]
}

什么是Avro

4、SpringBoot配置文件

server:
  port: 8086

spring:
  application:
    name: kafka

  kafka:
  	# 集群地址
    bootstrap-servers: xx.xx.xx.xx:9092,xx.xx.xx.xx:9192,xx.xx.xx.xx:9292
    producer:
      # 设置key的序列化类
      key-serializer: io.confluent.kafka.serializers.KafkaAvroSerializer
      # 设置value的序列化类
      value-serializer: io.confluent.kafka.serializers.KafkaAvroSerializer
      # ack策略
      # 0:生产者发送消息就不管了,效率高,但是容易丢数据,且没有重试机制
      # 1:消息发送到Leader并落盘后就返回,如果Leader挂了并且Follower还没有同步数据就会丢失数据
      #-1:消息要所有副本都拷贝才返回,保证数据不丢失(但是有可能重复消费)
      acks: 1
      # 失败重试次数
      retries: 3
      # 批量提交的数据大小
      batch-size: 16384
      # 生产者暂存数据的缓冲区大小
      buffer-memory: 33554432
    consumer:
      # key的反序列化类
      key-deserializer: io.confluent.kafka.serializers.KafkaAvroDeserializer
      value-deserializer: io.confluent.kafka.serializers.KafkaAvroDeserializer
      # 是否自动提交偏移量,如果要手动确认消息,就要设置为false
      enable-auto-commit: false
      # 消费消息后间隔多长时间提交偏移量(ms)
      auto-commit-interval: 100
      # 默认的消费者组,如果不指定就会用这个
      group-id: groupId
      # kafka意外宕机时的消息消费策略
      # earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
      # latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
      # none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
      auto-offset-reset: latest
    listener:
      # 手动确认消息
      ack-mode: manual_immediate
      # 消费者运行的线程数
      concurrency: 2
    properties:
      # confluent schema 地址
      schema:
        registry:
          url: http://xx.xx.xx.xx:8081

5、消息生产者

@RestController
@RequestMapping("/send")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class Producer {

    private final KafkaTemplate<String, User> kafkaTemplate;

    @GetMapping("/test")
    public void sendMsg() {
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i);
            user.setName("Jin" + i);
            user.setAge(35 + i);
            kafkaTemplate.send(AvroConsumer.TOPIC_NAME, user);
        }
    }
}

6、消息消费者

@Slf4j
@Component
public class AvroConsumer {

    public static final String TOPIC_NAME = "test";

    @KafkaListener(topics = TOPIC_NAME, groupId = "test-group")
    public void consume(ConsumerRecord<String, User> record, Acknowledgment ack) {
        log.info("value #=>:{}", record.value());
        // 手动提交ack
        ack.acknowledge();
    }
}

7、结果

消费消息

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Apache Avro是一个数据序列化系统,旨在支持快速、节省空间的数据交换和远程过程调用。它使用JSON格式定义数据结构,并支持动态类型,使其易于在不同编程语言之间进行交互。 使用Avro的步骤如下: 1. 定义数据结构:使用Avro的JSON格式定义数据结构,包括字段名、类型和默认值等信息。 例如,定义一个Person对象: ``` { "namespace": "example.avro", "type": "record", "name": "Person", "fields": [ {"name": "name", "type": "string"}, {"name": "age", "type": "int"} ] } ``` 2. 生成代码:使用Avro工具生成指定语言的代码,包括数据结构类和序列化/反序列化类等。 例如,使用Avro工具生成Java代码: ``` $ java -jar avro-tools-1.9.2.jar compile schema person.avsc . ``` 3. 序列化数据:使用生成的代码将数据序列化为字节数组。 例如,使用Java代码创建Person对象并序列化: ``` Person person = new Person("Alice", 30); ByteArrayOutputStream out = new ByteArrayOutputStream(); DatumWriter<Person> writer = new SpecificDatumWriter<Person>(Person.class); Encoder encoder = EncoderFactory.get().binaryEncoder(out, null); writer.write(person, encoder); encoder.flush(); byte[] bytes = out.toByteArray(); ``` 4. 反序列化数据:使用生成的代码将字节数组反序列化为数据对象。 例如,使用Java代码反序列化字节数组: ``` ByteArrayInputStream in = new ByteArrayInputStream(bytes); DatumReader<Person> reader = new SpecificDatumReader<Person>(Person.class); Decoder decoder = DecoderFactory.get().binaryDecoder(in, null); Person person2 = reader.read(null, decoder); ``` 这样,就完成了数据的序列化和反序列化。 以下是一个完整的Java代码示例: ``` import org.apache.avro.Schema; import org.apache.avro.Schema.Parser; import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericRecord; import org.apache.avro.io.DatumReader; import org.apache.avro.io.DatumWriter; import org.apache.avro.io.Decoder; import org.apache.avro.io.DecoderFactory; import org.apache.avro.io.Encoder; import org.apache.avro.io.EncoderFactory; import org.apache.avro.specific.SpecificDatumReader; import org.apache.avro.specific.SpecificDatumWriter; import org.apache.avro.specific.SpecificRecord; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class AvroExample { public static void main(String[] args) throws IOException { // Define schema String schemaJson = "{\n" + " \"namespace\": \"example.avro\",\n" + " \"type\": \"record\",\n" + " \"name\": \"Person\",\n" + " \"fields\": [\n" + " {\"name\": \"name\", \"type\": \"string\"},\n" + " {\"name\": \"age\", \"type\": \"int\"}\n" + " ]\n" + "}"; Schema schema = new Parser().parse(schemaJson); // Serialize data Person person = new Person("Alice", 30); ByteArrayOutputStream out = new ByteArrayOutputStream(); DatumWriter<Person> writer = new SpecificDatumWriter<Person>(Person.class); Encoder encoder = EncoderFactory.get().binaryEncoder(out, null); writer.write(person, encoder); encoder.flush(); byte[] bytes = out.toByteArray(); // Deserialize data ByteArrayInputStream in = new ByteArrayInputStream(bytes); DatumReader<Person> reader = new SpecificDatumReader<Person>(Person.class); Decoder decoder = DecoderFactory.get().binaryDecoder(in, null); Person person2 = reader.read(null, decoder); System.out.println(person2.getName()); // Alice System.out.println(person2.getAge()); // 30 } public static class Person implements SpecificRecord { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } @Override public void put(int i, Object o) { } @Override public Object get(int i) { if (i == 0) { return name; } else if (i == 1) { return age; } return null; } @Override public Schema getSchema() { return null; } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值