前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >gRPC | 抛开HTTP,不同语言之间如何实现接口调用

gRPC | 抛开HTTP,不同语言之间如何实现接口调用

原创
作者头像
叫我阿柒啊
发布2025-01-03 17:48:19
发布2025-01-03 17:48:19
20200
代码可运行
举报
运行总次数:0
代码可运行

前言

在当今的技术世界中,混合技术栈的应用场景越来越普遍。从微服务架构到多语言平台的整合,团队往往需要同时管理多种编程语言。而在这些语言之间进行高效、无缝的通信成为了一个关键的挑战。本文将探讨跨语言接口生成工具如何在混合技术栈中扮演重要角色,并介绍其核心原理与应用场景。

混合技术栈的现状

现代软件开发环境中,单一语言难以满足所有需求,导致技术栈多样化。例如:

  • 前端开发:使用 JavaScript/TypeScript 的框架(如 React、Vue)构建用户界面。
  • 后端服务:采用 Java、Python、Go、Rust 等语言开发微服务。
  • 数据处理:Python 的丰富生态适合数据科学,而 Java 则更适合大规模分布式处理。
  • 移动开发:使用 Swift、Kotlin 开发原生应用。

所以很多时候混合技术栈就会遇到很多问题:

  • 通信复杂性:不同语言的服务之间需要可靠的通信协议。
  • 数据序列化:如何在高效传输的同时保证数据格式的一致性。
  • 开发效率:手动定义和维护跨语言的接口容易出错,且增加了开发和维护成本。

在我个人开发的过程中,经常使用java后端 + vue前端的开发模式,前端通过调用后端的restful接口,来完成数据的增删改查等操作。在整个开发过程中,我需要在springboot的controller中定义接口,然后在vue中通过axios工具类来调用接口。

每次开发一个接口,就需要在新增调用接口。那么,是否工具可以自动生成不同语言之间的接口?

跨语言接口生成工具

跨语言接口生成工具是为了解决不同语言之间通信复杂性的问题,提供了一种高效且标准化的解决方案。这些工具通过定义语言无关的接口描述文件,自动生成不同语言的代码,从而简化了跨语言通信的实现。

核心功能

  • 接口描述:提供一种统一的接口定义语言(如 IDL)。
  • 代码生成:根据接口定义自动生成客户端和服务端代码。
  • 数据序列化:支持高效的数据序列化和反序列化。

常见工具

以下是一些流行的跨语言接口生成工具:

工具名称

支持语言

特点

gRPC

多种语言

基于 HTTP/2,支持流式通信和负载均衡

Apache Thrift

多种语言

提供高效的序列化和 RPC 支持

OpenAPI

多种语言

专注于 RESTful API 的定义和生成

Protocol Buffers (Protobuf)

多种语言

高效的二进制数据序列化格式

gRPC

gRPC是目前比较流行的跨语言接口生成工具,我们通过定义proto描述文件,通过使用不同语言的插件,就可以根据这个描述文件跨语言统一的接口。

安装gprc编译器

首先安装gprc的编译器,可以去github下载安装包。

我使用的mac系统,可以下载osx的安装包,也可以使用brew安装:

代码语言:bash
复制
brew install protobuf

gRPC协议文件定义

创建一个简单的 gRPC 协议文件 hello.proto,用于定义 gRPC 服务和消息结构,我们使用协议文件,可以轻松生成不同语言的服务端和客户端代码。

代码语言:proto
复制
syntax = "proto3";

package helloworld;

// 定义服务
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// 定义消息
message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

message 是一种数据结构,用于定义消息格式。它可以被看作是一个复合数据类型,类似于编程语言中的类或结构体,用于在客户端和服务端之间传递数据。

HelloRequest表示客户端向服务端发送的请求数据,包含客户端希望传递给服务端的信息。HelloReply表示服务端向客户端返回的响应数据,包含服务端处理请求后返回的信息。

每个message是由字段组成的。例如string name = 1; 是 Protocol Buffers (.proto) 文件中的一种字段定义语法,用于定义消息类型中的字段。

  1. string:表示字段的数据类型是字符串(string)
  2. name:表示字段的名字,通常用于标识这个字段的含义
  3. = 1:表示这个字段的 标识号(tag number),具有唯一性,用于在序列化和反序列化时识别字段

Java接口生成

首先安装protoc-gen-grpc-java,protoc-gen-grpc-java 是 gRPC 的 Java 插件,用于根据 Protocol Buffers(.proto 文件)生成 Java 的 gRPC 服务端和客户端代码。

可以使用git仓库进行安装:

代码语言:bash
复制
git clone https://github.com/grpc/grpc-java.git
cd grpc-java
./gradlew java_pluginExecutable

这里我使用的brew完成了安装:

代码生成

执行生成命令,就可以根据proto文件,生成java代码。

代码语言:shell
复制
protoc --java_out=./java_out --grpc-java_out=./java_out --plugin=protoc-gen-grpc-java=$(which protoc-gen-grpc-java) -I. hello.proto

在java_out目录下生成了两个java文件:

将java文件复制到java项目中,并在pom中引入依赖:

代码语言:xml
复制
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty</artifactId>
    <version>1.48.0</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.48.0</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.48.0</version>
</dependency>

其中GreeterGrpc是 gRPC 服务的核心文件,用于实现服务和客户端的逻辑。这是从 .proto 文件中定义的服务自动生成的代码。Hello.java 是从 .proto 文件中定义的消息(Message)生成的类文件,对应 gRPC 传输的数据结构。

GreeterGrpc

服务接口定义

生成一个抽象类,通常命名为 GreeterImplBase,服务端需要继承这个类并实现里面的方法。

代码语言:java
复制
public static abstract class GreeterImplBase
    implements io.grpc.BindableService, AsyncService {

  @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
    return GreeterGrpc.bindService(this);
  }
}
客户端 Stub 定义

提供不同类型的客户端接口:

  1. BlockingStub: 同步调用的客户端接口。
  2. FutureStub: 异步调用的客户端接口。
  3. Stub: 异步流式调用的客户端接口。
gRPC 通信绑定

提供了静态方法 bindService,用于将服务实现绑定到 gRPC 服务器。

这样,在GreeterGrpc.java中即实现了服务端的 GreeterGrpc.GreeterImplBase 的 sayHello 方法,也构造了客户端的 GreeterGrpc.GreeterBlockingStub 或其他类型的 Stub。

Hello.java

消息类

每个 proto 文件中定义的 message 会生成一个对应的 Java 类。例如:

代码语言:proto
复制
message HelloRequest {
    string name = 1;
}

会生成一个请求消息类HelloRequest。

也会生成响应消息类HelloReply。

消息字段

提供了字段的访问器方法,例如 getName() 获取字段值,setName() 设置字段值。

序列化和反序列化

内部实现了消息对象的序列化和反序列化方法,用于在网络传输时转换为二进制数据和从二进制数据恢复。

如图,会将消息对象序列化为二进制的protobuf格式进行传输,能够压缩传输大小。

Python接口代码生成

先安装python的gRPC的依赖:

代码语言:bash
复制
pip install protobuf grpcio grpcio-tools

执行命令,根据上面定义的hello.proto文件生成gRPC代码:

代码语言:bash
复制
python3 -m grpc_tools.protoc \
    -I. \
    --python_out=./python_out \
    --grpc_python_out=./python_out \
    hello.proto

在python_out目录下生成两个python文件:

和java生成的代码功能相同。hello_pb2.py定义消息类型和序列化逻辑,hello_pb2_grpc.py:定义 gRPC 服务端和客户端的实现。

服务调用

根据proto文件可以生成每种语言的服务端和客户端代码逻辑,也就意味着每种语言的角色都有客户端和服务端两种,我们编写代码调用proto生成的代码,来实现不同语言之间服务端和客户端的交互。

Java服务端

这里就以Java作为服务端,我们编写java服务端代码并启动。

代码语言:java
复制
public class GreeterServer {
    public static void main(String[] args) throws Exception {
        Server server = ServerBuilder
                .forPort(50051)
                .addService(new GreeterImpl())
                .build()
                .start();
        System.out.println("Server started at port 50051");
        server.awaitTermination();
    }

    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(Hello.HelloRequest request, StreamObserver<Hello.HelloReply> responseObserver) {
            System.out.println("接收到客户端的数据:" + request.getName());
            Hello.HelloReply reply = Hello.HelloReply.newBuilder()
                    .setMessage("Hello, " + request.getName())
                    .build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

可以看到GreeterImplBase就是我们之前生成gRPC生成的代码,这里监听50051端口。

Python客户端

然后python作为客户端,调用之前生成的GreeterStub来实现。

代码语言:python
代码运行次数:0
复制
import grpc
from proto import hello_pb2, hello_pb2_grpc

def run():
    # Connect to the Java server
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = hello_pb2_grpc.GreeterStub(channel)
        # Send a request
        response = stub.SayHello(hello_pb2.HelloRequest(name="hello aqi!"))
        print("Received from server: " + response.message)

if __name__ == '__main__':
    run()

然后启动python客户端,发送 hello aqi,我们可以看到Java服务端接收到了Python客户端的请求数据:

同时Python客户端也收到了JavaScript服务端的响应数据。

结语

相对于传统的HTTP调用模式,gRPC高效序列化能力特别适合大规模数据的共享和传输,用 Protocol Buffers 进行数据序列化,减少传输时间和带宽占用。同时统一的接口定义(proto描述文件)避免了语言间的数据不一致问题。

在实际应用中,团队需要根据具体需求选择合适的工具,并遵循最佳实践来确保工具的高效使用。未来,随着技术的不断发展,跨语言接口生成工具必将在更多领域发挥重要作用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 混合技术栈的现状
  • 跨语言接口生成工具
    • 核心功能
    • 常见工具
  • gRPC
  • 安装gprc编译器
  • gRPC协议文件定义
  • Java接口生成
    • 代码生成
    • GreeterGrpc
      • 服务接口定义
      • 客户端 Stub 定义
      • gRPC 通信绑定
    • Hello.java
      • 消息类
      • 消息字段
      • 序列化和反序列化
  • Python接口代码生成
  • 服务调用
    • Java服务端
    • Python客户端
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档