记一次SpringBoot2.x整合Kafka及ProtoBuf实现信息序列化传输

前言

ProtoBuf简介

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。
简单来讲, ProtoBuf 是结构数据序列化 方法,可简单类比于 XML,其具有以下特点:

  • 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
  • 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
  • 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序

序列化:将结构数据或对象转换成能够被存储和传输(例如网络传输)的格式,同时应当要保证这个序列化结果在之后(可能在另一个计算环境中)能够被重建回原来的结构数据或对象。

ProtoBuf文件说明

文件范本示例:

syntax = "proto3"; #语法格式

package demo;  #包
option java_package = "com.kp.demo"; #可赋值非必须赋值 java_package:java包名
option java_outer_classname="UserProto"; #java类名

#message定义所需要序列化的数据的格式。每一个Message都是一个小的信息逻辑单元,包含了一些列的name-value对
message User{
	required int32 id = 1;
	required string name = 2;
	optional string age = 3;
	
	enum sexType { #枚举
	    	MAN= 0;
	    	WOMAN = 1;
	}
	  
	message sex {
	    	required string code= 1;
	    	optional sexType type = 0 [default = MAN];
	}
	
  	required  sex phone = 4;
}

字段规则:
required -> 字段只能也必须出现 1 次
optional -> 字段可出现 0 次或多次
repeated -> 字段可出现任意多次(包括 0)

类型:
int32、int64、sint32、sint64、string、32-bit …
字段编号:
0 ~ 536870911(除去 19000 到 19999 之间的数字)
声明格式:
字段规则 类型 名称 = 字段编号; eq:required string name = 2;

Kafka简介

一个分布式的基于发布\订阅模式的消息队列,主要应用与大数据实时处理领域。

集成

1)引入依赖pom.xml

	<properties>
        <java.version>1.8</java.version>
        <grpc.version>1.6.1</grpc.version>
        <protobuf.version>3.13.0</protobuf.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>${grpc.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>${protobuf.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2)idea引入插件,配合使用protoBuf
在这里插入图片描述
下载安装后重启,配置proto自动转换规则
在这里插入图片描述
在这里插入图片描述
如果使用协议“proto3”,需要配置参数--experimental_allow_proto3_optional
3)编写proto文件

syntax = "proto3";

option java_outer_classname="UserProto";

message User{
	int32 id = 1;
	string name = 2;
	string age = 3;
}

这里可以不指明字段规则
4)转换成java文件
右键文件后生成文件UserProto.java
在这里插入图片描述
5)测试文件是否可执行

public class TestMain {

    public static void main(String[] args) throws InvalidProtocolBufferException {
        UserProto.User.Builder user = new UserProto.User.Builder();
        user.setAge("25");
        user.setName("125");
        user.setId(1111);
        byte[] s = user.build().toByteArray();
        System.out.println("序列化" + s.length);
        UserProto.User user1 = UserProto.User.parseFrom(s);
        System.out.println("反序列化" + user1.toString());
    }

}

6)创建公用接口IProtobuf

public interface IProtobuf {

    //将对象转为字节数组
    byte[] encode();

}

7)创建自定义的User

public class User implements IProtobuf {

    private static Logger logger = LoggerFactory.getLogger(User.class);

    int id;
    String name;
    String age;

    //有参构造及无参构造
    public User(){}

    public User(int id,String name,String age){
        super();
        this.id = id;
        this.name = name;
        this.age = age;
    }

    //重写toString


    @Override
    public String toString() {
        return "User{" +
                "ID=" + id +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }

    /**
     * @Author fyy
     * @Description 类序列化
     * @Date 2020/10/28 9:29
     */
    @Override
    public byte[] encode() {
        UserProto.User.Builder builder = UserProto.User.newBuilder();
        builder.setId(id);
        builder.setName(name);
        builder.setAge(age);
        return builder.build().toByteArray();
    }

    /**
     * @Author fyy
     * @Description 反序列化
     * @Date 2020/10/28 9:32
     */
    public User(byte[] bytes){
        try {
            UserProto.User user = UserProto.User.parseFrom(bytes);
            this.id = user.getId();
            this.name = user.getName();
            this.age = user.getAge();
        } catch (InvalidProtocolBufferException e) {
            logger.error("User反序列化失败:{}",e.getMessage());
        }
    }

    //set get 方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }


}

8)定义kafka序列化基于protobuf实现ProtobufSerializer

public class ProtobufSerializer implements Serializer<IProtobuf> {

    @Override
    public void configure(Map<String, ?> configs, boolean isKey) {

    }

    @Override
    public byte[] serialize(String topic, IProtobuf data) {
        return data.encode(); //其实质是调用encode方法
    }

    @Override
    public void close() {

    }
}

9)修改配置文件,添加kafka配置

server:
  port: 8080

spring:
  kafka:
    bootstrap-servers: [your.url]:9092 # 以逗号分隔的地址列表,用于建立与Kafka集群的初始连接(kafka 默认的端口号为9092)
    producer:
      retries: 5 # 发生错误后,消息重发的次数
      batch-size: 2048 #当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小
      buffer-memory: 33554432 # 设置生产者内存缓冲区的大小
      key-serializer: org.apache.kafka.common.serialization.IntegerSerializer # 键的序列化方式
      value-serializer: com.fyy.protobuf.serializer.ProtobufSerializer # 值的序列化方式
      # acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。
      # acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
      # acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
      acks: 1
    consumer:
      auto-commit-interval: 1S # 自动提交的时间间隔,如1S,1M,2H,5D
      # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
      	# latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据
      	# earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
      auto-offset-reset: earliest
      enable-auto-commit: false # 是否自动提交偏移量
      key-deserializer: org.apache.kafka.common.serialization.IntegerDeserializer # 键的反序列化方式
      value-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer # 值的反序列化方式
      group-id: testkafka #分组id
    listener:
      concurrency: 5 # 在侦听器容器中运行的线程数
      ack-mode: manual_immediate #listner负责ack,每调用一次,就立即commit

10)实现消息发送及消息接收

@Controller
@RequestMapping("/kafka/api")
public class TestController {

    private static Logger logger = LoggerFactory.getLogger(TestController.class);
    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * @Author fyy
     * @Description 发送消息
     * @Date 2020/10/28 9:51
     */
    @RequestMapping("/send")
    @ResponseBody
    public String sendMesg(String name){
        User user = new User();
        user.setId(1);
        user.setName(name);
        user.setAge("25");
        kafkaTemplate.send("kafka",1234,user);
        return "发送成功";
    }

    @KafkaListener(topics = "kafka")
    public void ConsumerMsg(ConsumerRecord<Integer,byte[]> record, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic
            , Consumer consumer, Acknowledgment ack){
        int key = record.key();
        User user = new User(record.value());
        logger.info("消费到的key:" + key + ",消费到的消息:" + user.toString());
        logger.info("消费消息:topic:{}",topic);
        ack.acknowledge();
    }

}

源码参考:源码下载连接

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
要在Spring Boot 2.x中配置Kafka 2.2.0,首先需要在项目的pom.xml文件中添加相应的依赖项。在这个例子中,我们将使用Spring Kafka来与Kafka集成: ```xml <!-- Spring Kafka --> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>2.2.0.RELEASE</version> </dependency> ``` 添加了依赖项后,可以在Spring Boot应用程序的配置文件(application.properties或application.yml)中配置Kafka连接和相关属性。下面是一个示例的application.properties文件配置: ```properties # Kafka连接配置 spring.kafka.bootstrap-servers=localhost:9092 # 消费者配置 spring.kafka.consumer.group-id=my-group spring.kafka.consumer.auto-offset-reset=earliest # 生产者配置 spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer ``` 以上配置示例设置了Kafka的连接地址为localhost:9092,并指定了消费者和生产者的相关配置。你可以根据需要进行修改。 接下来,你可以创建Kafka消费者和生产者的相关类。这些类可以使用Spring Kafka提供的注解和模板来简化Kafka操作。下面是一个示例的Kafka消费者类: ```java import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class KafkaConsumer { @KafkaListener(topics = "my-topic", groupId = "my-group") public void consume(String message) { // 处理接收到的消息 System.out.println("Received message: " + message); } } ``` 上面示例中的Kafka消费者使用了`@KafkaListener`注解来监听名为"my-topic"的Kafka主题,并将接收到的消息作为字符串进行处理。 类似地,你可以创建一个Kafka生产者类来发送消息。下面是一个示例的Kafka生产者类: ```java import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Component; @Component public class KafkaProducer { private final KafkaTemplate<String, String> kafkaTemplate; public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate) { this.kafkaTemplate = kafkaTemplate; } public void sendMessage(String topic, String message) { kafkaTemplate.send(topic, message); } } ``` 上面示例中的Kafka生产者使用了`KafkaTemplate`来发送消息。 现在,你已经配置了Kafka并创建了消费者和生产者类。你可以在其他的Spring Boot组件中使用这些类来实现Kafka的消费和生产操作。 请注意,这只是一个简单的示例,实际使用中可能需要更多的配置和自定义。你可以根据具体需求在项目中添加额外的配置和逻辑。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序小达人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值