在当今的技术世界中,混合技术栈的应用场景越来越普遍。从微服务架构到多语言平台的整合,团队往往需要同时管理多种编程语言。而在这些语言之间进行高效、无缝的通信成为了一个关键的挑战。本文将探讨跨语言接口生成工具如何在混合技术栈中扮演重要角色,并介绍其核心原理与应用场景。
现代软件开发环境中,单一语言难以满足所有需求,导致技术栈多样化。例如:
所以很多时候混合技术栈就会遇到很多问题:
在我个人开发的过程中,经常使用java后端 + vue前端的开发模式,前端通过调用后端的restful接口,来完成数据的增删改查等操作。在整个开发过程中,我需要在springboot的controller中定义接口,然后在vue中通过axios工具类来调用接口。
每次开发一个接口,就需要在新增调用接口。那么,是否工具可以自动生成不同语言之间的接口?
跨语言接口生成工具是为了解决不同语言之间通信复杂性的问题,提供了一种高效且标准化的解决方案。这些工具通过定义语言无关的接口描述文件,自动生成不同语言的代码,从而简化了跨语言通信的实现。
以下是一些流行的跨语言接口生成工具:
工具名称 | 支持语言 | 特点 |
---|---|---|
gRPC | 多种语言 | 基于 HTTP/2,支持流式通信和负载均衡 |
Apache Thrift | 多种语言 | 提供高效的序列化和 RPC 支持 |
OpenAPI | 多种语言 | 专注于 RESTful API 的定义和生成 |
Protocol Buffers (Protobuf) | 多种语言 | 高效的二进制数据序列化格式 |
gRPC是目前比较流行的跨语言接口生成工具,我们通过定义proto描述文件,通过使用不同语言的插件,就可以根据这个描述文件跨语言统一的接口。
首先安装gprc的编译器,可以去github下载安装包。
我使用的mac系统,可以下载osx的安装包,也可以使用brew安装:
brew install protobuf
创建一个简单的 gRPC 协议文件 hello.proto,用于定义 gRPC 服务和消息结构,我们使用协议文件,可以轻松生成不同语言的服务端和客户端代码。
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) 文件中的一种字段定义语法,用于定义消息类型中的字段。
首先安装protoc-gen-grpc-java,protoc-gen-grpc-java 是 gRPC 的 Java 插件,用于根据 Protocol Buffers(.proto 文件)生成 Java 的 gRPC 服务端和客户端代码。
可以使用git仓库进行安装:
git clone https://github.com/grpc/grpc-java.git
cd grpc-java
./gradlew java_pluginExecutable
这里我使用的brew完成了安装:
执行生成命令,就可以根据proto文件,生成java代码。
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中引入依赖:
<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 传输的数据结构。
生成一个抽象类,通常命名为 GreeterImplBase,服务端需要继承这个类并实现里面的方法。
public static abstract class GreeterImplBase
implements io.grpc.BindableService, AsyncService {
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return GreeterGrpc.bindService(this);
}
}
提供不同类型的客户端接口:
提供了静态方法 bindService,用于将服务实现绑定到 gRPC 服务器。
这样,在GreeterGrpc.java中即实现了服务端的 GreeterGrpc.GreeterImplBase 的 sayHello 方法,也构造了客户端的 GreeterGrpc.GreeterBlockingStub 或其他类型的 Stub。
每个 proto 文件中定义的 message 会生成一个对应的 Java 类。例如:
message HelloRequest {
string name = 1;
}
会生成一个请求消息类HelloRequest。
也会生成响应消息类HelloReply。
提供了字段的访问器方法,例如 getName() 获取字段值,setName() 设置字段值。
内部实现了消息对象的序列化和反序列化方法,用于在网络传输时转换为二进制数据和从二进制数据恢复。
如图,会将消息对象序列化为二进制的protobuf格式进行传输,能够压缩传输大小。
先安装python的gRPC的依赖:
pip install protobuf grpcio grpcio-tools
执行命令,根据上面定义的hello.proto文件生成gRPC代码:
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服务端代码并启动。
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作为客户端,调用之前生成的GreeterStub来实现。
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 删除。