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-kotlinprotobuf-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


原始标题:gRPC and Kotlin