【Web系列二十五】前后端使用proto+grpc实现数据传输

目录

写在前面

简介

定义

编码

使用过程

环境配置

后端

开发环境

pom配置

前端

开发环境

安装依赖

建立通信

定义Proto结构

编译proto文件

后端

前端

使用proto

后端

前端

参考资料


简介

定义

        protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。通信时所传递的信息是通过Protobuf定义的message数据结构进行打包,然后编译成二进制的码流再进行传输或者存储。

        Protocol buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

        Protobuf 使用的时候必须写一个 IDL(Interface description language)文件,在里面定义好数据结构,只有预先定义了的数据结构,才能被序列化和反序列化。其中,序列化是将对象转换二进制数据,反序列化是将二进制数据转换成对象。

编码

        ProtoBuf 编码格式类似于TLV 格式(Tag | Length | Value),Tag为字段唯一标识,Length为Value域的长度,Value为数据本身。其中,Tag由field_number和wire_type两个部分组成,field_number是message定义字段时指定的字段编号,wire_type是ProtoBuf 编码类型,根据这个类型选择不同的 Value 编码方案。wire_type最多可表达8种编码类型(因为是3bit的),目前已经定义了六种(Varint,64-bit,Length-delimited,Start group,End group,32-bit,其中Start group和End group已经弃用)。

        在ProtoBuf 中,Length是可选的,即有些类型的数据结构编码后格式是(Tag | Value),如Varint,64-bit和32-bit。为确定这种格式的数据边界引入了Varint编码。

使用过程

  • 第一步,创建 .proto 文件,定义数据结构。
  • 第二步,protoc 编译 .proto 文件生成读写接口
  • 第三步,调用接口实现序列化、反序列化以及读写

环境配置

后端

开发环境

        spring boot + maven

pom配置

        在Pom文件中添加以下配置。

<?xml version="1.0" encoding="UTF-8"?>
        </project ...>
    ...
    <properties>
        ...
        <grpc.version>1.6.1</grpc.version>
        <protobuf.version>3.3.0</protobuf.version>
        <os.detected.classifer>windows-x86_64</os.detected.classifer>
        ...
    </properties>
    ...
    <build>
        <plugins>
            <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.vlassifer}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifer}</pluginArtifact>
                    <protoSourceRoot>src/main/resources/proto</protoSourceRoot>
                    <outputDorectory>src/main/jsvs</outputDorectory>
                    <clearOutputDirectory>false/clearOutputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            <plugin>
        </plugins>
    </build>
    ...
</project>

前端

开发环境

        vue

安装依赖

npm install pbjs
npm install protobufjs

        在package.json中添加一下命令,其中xxx对应每个proto文件的名称,如果有多个文件,需要分多次执行

{
    "scripts": {
        "proto": "pbjs -t json-module -w commonjs -o --ts src/utils/proto/xxx.ts src/utils/proto/xxx.proto"
    }
}

建立通信

定义Proto结构

        student结构

syntax = "proto3";

package com.test.proto;

option java_multiple_diles = false;
option java_package = "com.test.protobuf";
option java_outer_classname = "StudentVO";

message Student {
    int32 number = 1;
    string name = 2;
}

         继承student的class结构

syntax = "proto3";

import "student.proto";

pacakge com.test.proto;

option java_mutiple_files = false;
option java_package = "com.test.protobuf";
option java_outer_classname = "ClassVO";

message Class {
    enum Grade {
        One = 0;
        Two = 1;
        Three = 2;
    };
    
    string name = 1;
    Grade grade = 2;
    com.test.proto.Student student = 3;
}

         rpc服务

syntax = "proto3";

import "class.proto";

pacakge com.test.proto;

option java_mutiple_files = false;
option java_package = "com.test.protobuf";
option java_outer_classname = "ClassService";

service ClassService {
    rpc SendClassMessage(Class) return (Result) {}
}

message Result {
    enum Statu {
        SUCCESS = 0;
        FAIL = 1;
    }
    
    State result = 1;
}

        要在.proto文件中添加注释,请使用C/ C++风格的//和/ /语法。

  1. syntax:指定Proto语法,默认使用的是proto2;
  2. import:用于导入包,类似python;
  3. package:定义包的名称
  4. option:用于java。指定输出的是java包名称以及类的名称
  5. message:数据结构
  6. enum:枚举类型
  7. repeated: 表示列表
  8. service:rpc服务

编译proto文件

        编译前要确保前后端使用的是同样的proto文件

后端

         添加了依赖之后可以发现Maven中会有一个Protobuf的插件,点击插件中的compile就可以编译生成java格式的文件了

前端

        修改package.json中的文件名后,执行npm run proto即可

使用proto

        前面已经使用编译工具生成了前后端不同语言的proto类,类中定义了proto的结构以及decode\encode方法,因此使用起来很简单,只要建立普通的前后端通信,如http\websocket\rpc等,然后使用proto类作为信息的载体即可。

        以Student为例,前后端通信接口可以类似如下方式建立(代码不完整,删减了部分信息)

后端

        StudentController.java

@Api(tags = "学生信息接口")
@RestController
@RequestMapping("student")
@CrossOrigin
public class StudentController {

    @Autowired
    private StudentService studentService;
    
    @ApiOperation("根据班级信息获取学生信息")
    @PostMapping(value="queryStudentData", produces="application/x-protobuf")
    public StudentVO.Student queryStudentData(
        @ApiParam(name = "number", value = "学号")
        @RequestParam("number") Integer number
    ) throws IOException {
        return studentService.queryStudentData(number);
    }
}

        StudentService.java

public interface StudentService {
    StudentVO.Student queryStudentData(Integer number);
}

        StudentServiceImpl.java

public StudentVO.Student queryStudentData(Integer number) {
    StudentVO.Student.Builder student = StudentVO.Student.newBuilder();
    student.setNumber(number);
    student.setName("student1");
    return student.build();
}

前端

        studentServer.ts

export async function queryStudentData (name: number) {
    return axios.create(
            {
                timeout: 5 * 60 * 1000,
                baseURL: 'http://' + import.meta.env.IP + ':' + import.meta.env.PORT
            }
        ).post(
            '/student/queryStudentData?number=' + number   
        )
}

        student.ts

import { queryStudentData } from '@/api/http/studentServer.ts'

function getStudent(num: number) {
    let student
    await queryStudentData(1).then((res: any) => {
        let uint8 = new Uint8Array(res.data)
        student = enc.decode(uint8)
    })
    return student
}

参考资料

ProtoBuf介绍(内含官网汉化文档) - 知乎 (zhihu.com)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java + gRPC + grpc-gateway 的实践主要分为以下几个步骤: 1. 定义 proto 文件 在 proto 文件中定义需要调用的服务以及方法,同时指定请求和响应的数据类型。例如: ``` syntax = "proto3"; package example; service ExampleService { rpc ExampleMethod (ExampleRequest) returns (ExampleResponse) {} } message ExampleRequest { string example_field = 1; } message ExampleResponse { string example_field = 1; } ``` 2. 使用 protoc 编译 proto 文件 使用 protoc 编译 proto 文件,生成 Java 代码。例如: ``` protoc --java_out=./src/main/java ./example.proto ``` 3. 实现 gRPC 服务 在 Java 代码中实现定义的 gRPC 服务,例如: ``` public class ExampleServiceImpl extends ExampleServiceGrpc.ExampleServiceImplBase { @Override public void exampleMethod(ExampleRequest request, StreamObserver<ExampleResponse> responseObserver) { // 实现具体逻辑 ExampleResponse response = ExampleResponse.newBuilder().setExampleField("example").build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } ``` 4. 启动 gRPC 服务器 使用 gRPC 提供的 ServerBuilder 构建 gRPC 服务器,并启动服务器。例如: ``` Server server = ServerBuilder.forPort(8080).addService(new ExampleServiceImpl()).build(); server.start(); ``` 5. 集成 grpc-gateway 使用 grpc-gateway 可以将 gRPC 服务转换为 HTTP/JSON API。在 proto 文件中添加以下内容: ``` import "google/api/annotations.proto"; service ExampleService { rpc ExampleMethod (ExampleRequest) returns (ExampleResponse) { option (google.api.http) = { post: "/example" body: "*" }; } } ``` 在 Java 代码中添加以下内容: ``` Server httpServer = ServerBuilder.forPort(8081).addService(new ExampleServiceImpl()).build(); httpServer.start(); String grpcServerUrl = "localhost:8080"; String httpServerUrl = "localhost:8081"; ProxyServerConfig proxyConfig = new ProxyServerConfig(grpcServerUrl, httpServerUrl, "/example"); HttpProxyServer httpProxyServer = new HttpProxyServer(proxyConfig); httpProxyServer.start(); ``` 6. 测试 使用 HTTP/JSON API 调用 gRPC 服务,例如: ``` POST http://localhost:8081/example Content-Type: application/json { "example_field": "example" } ``` 以上就是 Java + gRPC + grpc-gateway 的实践步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值