1. 简介
gRPC 是 Google 开发的开源 RPC(远程过程调用)框架,专为高性能、低延迟的跨服务通信设计,适用于任意环境,包括跨数据中心场景。它天然支持微服务架构,具备可插拔的负载均衡、链路追踪、健康检查和认证机制,是构建分布式系统的理想选择。
在 gRPC 中,客户端通过生成的桩代码(stub)调用远程服务器上的方法,整个过程对开发者透明,屏蔽了网络通信的复杂性。
本文将深入讲解 gRPC 的核心组件,并使用 Kotlin 实现一个完整的 gRPC 服务端与客户端。
✅ 提示:如果你已经熟悉 gRPC 基本概念,可以直接跳到实现部分。但如果你踩过“序列化性能差”或“接口耦合紧”的坑,gRPC 值得你重新审视。
2. 核心组件
gRPC 的起点是 服务定义(service definition) —— 本质上是一个接口,声明了可用的方法、入参和返回类型。
基于这个定义:
- 客户端使用 桩代码(stub) 发起远程调用
- 服务端实现该接口,并启动 gRPC 服务监听请求
⚠️ 关键点:gRPC 默认使用 Protobuf 作为接口描述语言(IDL)和数据序列化格式。它比 JSON 更小、更快,且天生支持多语言。
当然,你也可以替换序列化方式,比如使用 Gson,但这会失去 Protobuf 的强类型和高效优势,一般不推荐。
3. 项目依赖
以下是 Kotlin 项目中使用 gRPC 所需的核心 Maven 依赖:
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.46.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.53.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.46.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-kotlin-stub</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-kotlin</artifactId>
<version>3.18.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.18.1</version>
</dependency>
✅ 注意:
grpc-kotlin-stub
提供了协程支持,让 Kotlin 代码更简洁protobuf-kotlin
和protobuf-java
都需要,因为生成代码依赖 Java 插件
4. Proto 文件定义
我们使用 .proto
文件定义服务契约。以下是一个经典的 “Hello World” 示例:
syntax = "proto3";
package com.baeldung.grpc.helloworld;
option java_multiple_files = true;
service HelloService {
rpc hello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
如何生成桩代码?
你可以手动运行 protoc
编译器:
protoc --plugin=protoc-gen-grpc-java=build/exe/java_plugin/protoc-gen-grpc-java \
--grpc-java_out="$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" "$PROTO_FILE"
❌ 但更推荐的做法是:将这一步集成到构建流程中,避免手动操作出错。
5. Maven 插件自动编译
使用 protobuf-maven-plugin
可以在 mvn compile
时自动生成代码,提升开发效率。
配置如下:
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
<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>
<protocPlugins>
<protocPlugin>
<id>grpc-kotlin</id>
<groupId>io.grpc</groupId>
<artifactId>protoc-gen-grpc-kotlin</artifactId>
<version>${grpc.kotlin.version}</version>
<classifier>jdk7</classifier>
<mainClass>io.grpc.kotlin.generator.GeneratorRunner</mainClass>
</protocPlugin>
</protocPlugins>
</configuration>
</execution>
</executions>
</plugin>
✅ 推荐做法:把 proto 文件放在
src/main/proto
,插件会自动扫描并生成 Kotlin 桩代码。
6. 服务端实现
继承 HelloServiceCoroutineImplBase
并重写方法即可实现服务逻辑。得益于 Kotlin 协程,代码非常简洁:
class HelloService : HelloServiceGrpcKt.HelloServiceCoroutineImplBase() {
override suspend fun hello(request: HelloRequest): HelloReply {
return HelloReply.newBuilder()
.setMessage("Hello, ${request.name}")
.build()
}
}
启动 gRPC 服务:
fun helloServer() {
val helloService = HelloService()
val server = ServerBuilder
.forPort(15001)
.addService(helloService)
.build()
Runtime.getRuntime().addShutdownHook(Thread {
server.shutdown()
server.awaitTermination()
})
server.start()
server.awaitTermination()
}
fun main(args: Array<String>) {
helloServer()
}
✅ 要点:
- 使用
suspend
函数天然支持异步非阻塞 - 添加 JVM 关闭钩子确保优雅停机
7. 客户端实现
建立连接
通过 ManagedChannel
连接指定主机和端口:
val channel = ManagedChannelBuilder.forAddress("localhost", 15001)
.usePlaintext()
.build()
⚠️ 生产环境务必使用 TLS 加密(去掉
usePlaintext()
)
创建桩代码并调用
gRPC 支持多种调用模式,按需选择:
同步阻塞调用(Blocking Stub)
最简单,适合简单场景:
val stub = HelloServiceGrpc.newBlockingStub(channel)
val response = stub.hello(HelloRequest.newBuilder().setName("Baeldung").build())
异步 Future 调用(Future Stub)
获取 ListenableFuture
,支持超时和取消:
val futureStub = HelloServiceGrpc.newFutureStub(channel)
val responseFuture = futureStub.hello(HelloRequest.newBuilder().setName("Baeldung").build())
// 可设置超时
val response = responseFuture.get(5, TimeUnit.SECONDS)
流式响应(Async Stub + Callback)
适用于流式数据或完全异步处理:
HelloServiceGrpc.newStub(channel).hello(
HelloRequest.newBuilder().setName("Baeldung").build(), object: StreamObserver<HelloReply> {
override fun onNext(response: HelloReply?) {
println("Received: ${response?.message}")
}
override fun onError(throwable: Throwable?) {
println("Error: ${throwable?.message}")
}
override fun onCompleted() {
println("Call completed")
}
}
)
✅ 建议:在 Kotlin 中优先使用协程封装异步调用,代码更清晰。
8. 总结
本文介绍了如何在 Kotlin 项目中使用 gRPC 构建高性能微服务通信。
核心要点回顾:
- ✅ 使用 Protobuf 定义服务契约,保证高效与跨语言兼容
- ✅ 通过 Maven 插件自动化生成桩代码,避免手动维护
- ✅ 利用 Kotlin 协程简化异步逻辑,提升代码可读性
- ✅ 客户端支持阻塞、Future、流式等多种调用模式,灵活适配不同场景
完整源码已托管至 GitHub:
👉 https://github.com/Baeldung/kotlin-tutorials/tree/master/kotlin-libraries-2