TL;DR 本教程帶你用 Spring Boot 快速搭建一個可運行的 gRPC 服務與客户端示例,包含:proto 定義、構建生成 Java stub、服務端實現、客户端調用、配置説明與常見坑和最佳實踐。示例同時給出 Maven 配置(也會提到 Gradle 思路),推薦使用社區成熟的 grpc-spring-boot-starter 簡化集成(也提供手工方式的關鍵點)。


1. 前置條件

  • Java 11+(根據項目需要,也可用 Java 8/17)
  • Maven 或 Gradle 構建工具(以下示例以 Maven 為主)
  • protoc(可通過 Maven/Gradle 插件自動下載,不必須本機已安裝)
  • 熟悉 Spring Boot 基礎項目結構

2. 選擇集成方式(兩條常用路線)

  1. 使用社區 starter(推薦)net.devh:grpc-spring-boot-starter 類庫(或其拆分後的 server/client starter)能把 gRPC 服務以 Spring Bean 形式暴露,支持註解、配置化、攔截器、健康檢查等,省去大量樣板代碼。
  2. 手工集成:獨立使用 io.grpc 原生 API(ServerBuilderManagedChannel)並把它們以 Spring Bean 管理。這種方式更底層但更靈活。

下面以 starter + Maven 的方式給出完整示例,並補充手工集成的關鍵點。


3. 項目結構(示例)

springboot-grpc/
├─ src/
│  ├─ main/
│  │  ├─ java/
│  │  │  └─ com.example.grpc/
│  │  │     ├─ GrpcServerApplication.java
│  │  │     ├─ service/
│  │  │     │  └─ GreetingServiceImpl.java
│  │  │     └─ client/
│  │  │        └─ GrpcClientRunner.java
│  │  └─ proto/
│  │     └─ greeter.proto
├─ pom.xml

4. 定義 proto(src/main/proto/greeter.proto

syntax = "proto3";

package com.example.grpc;

option java_package = "com.example.grpc.proto";
option java_outer_classname = "GreeterProto";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

説明:java_packagejava_outer_classname 可控制生成 Java 包名和類名,便於項目結構組織。


5. Maven 配置(關鍵依賴與插件)

下面的 pom.xml 給出核心依賴和 protobuf / gRPC 生成配置(示例中採用 Maven protobuf 插件自動下載 protoc 和 grpc 插件並生成代碼),同時加入 grpc-spring-boot-starter(示例使用 net.devh 風格,具體版本請根據你環境選擇最新穩定版)。

<!-- 省略常規的 parent 和 properties -->
<project ...>
  <modelVersion>4.0.0</modelVersion>

  <properties>
    <java.version>17</java.version>
    <spring.boot.version>3.1.0</spring.boot.version>
    <grpc.version>1.56.0</grpc.version> <!-- 請按需更新 -->
    <protobuf.version>3.24.0</protobuf.version>
    <protoc-gen-grpc-java.version>1.56.0</protoc-gen-grpc-java.version>
  </properties>

  <dependencies>
    <!-- Spring Boot -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- grpc starter (server + client support) - 用於示例 -->
    <dependency>
      <groupId>net.devh</groupId>
      <artifactId>grpc-spring-boot-starter</artifactId>
      <version>2.13.1.RELEASE</version>
    </dependency>

    <!-- gRPC runtime (如果需要手工方式) -->
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-stub</artifactId>
      <version>${grpc.version}</version>
    </dependency>

    <!-- protobuf runtime -->
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>${protobuf.version}</version>
    </dependency>

    <!-- 可選:反射用於調試 / grpcurl 支持 -->
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-services</artifactId>
      <version>${grpc.version}</version>
    </dependency>
  </dependencies>

  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.7.0</version>
      </extension>
    </extensions>

    <plugins>
      <!-- protobuf plugin: 生成 proto -> java -->
      <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>0.6.1</version>
        <configuration>
          <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
          <pluginId>grpc-java</pluginId>
          <pluginArtifact>io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc-java.version}:exe:${os.detected.classifier}</pluginArtifact>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>compile-custom</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <!-- 其它常規插件(compiler, spring-boot-maven-plugin) -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.10.1</version>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring.boot.version}</version>
      </plugin>
    </plugins>
  </build>
</project>

注:版本號會隨時間更新,上面只是示例。若你使用 Gradle,可使用 com.google.protobuf gradle 插件與 protocprotoc-gen-grpc-java 相似配置來生成代碼。


6. 生成代碼與編譯

在項目根目錄運行:

mvn clean package

protobuf-maven-plugin 會把 src/main/proto 下的 .proto 文件生成到 target/generated-sources,並被編譯進你的項目。生成的類通常包含 GreeterGrpc(服務接口 stub)和消息 POJO 類(例如 HelloRequest, HelloReply)。


7. 服務端實現(使用 starter)

net.devh starter 允許把實現 Bean 用 @GrpcService 暴露。示例實現 GreetingServiceImpl

package com.example.grpc.service;

import com.example.grpc.proto.GreeterGrpc;
import com.example.grpc.proto.GreeterProto;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@GrpcService
public class GreetingServiceImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(GreeterProto.HelloRequest request, StreamObserver<GreeterProto.HelloReply> responseObserver) {
        String name = request.getName();
        String msg = "Hello, " + (name.isEmpty() ? "world" : name) + "!";
        GreeterProto.HelloReply reply = GreeterProto.HelloReply.newBuilder()
                .setMessage(msg)
                .build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }
}

Spring Boot 啓動類:

package com.example.grpc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GrpcServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(GrpcServerApplication.class, args);
    }
}

配置(application.ymlapplication.properties):

grpc:
  server:
    port: 9090   # gRPC 服務監聽端口
    enable-reflection: true  # 開啓服務反射,方便 grpcurl 等工具調試

8. 客户端調用(兩種方式)

方式 A — 使用 starter 的 @GrpcClient(更 Spring 化)

package com.example.grpc.client;

import com.example.grpc.proto.GreeterGrpc;
import com.example.grpc.proto.GreeterProto;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class GrpcClientRunner implements CommandLineRunner {

    @GrpcClient("localGrpc")
    private GreeterGrpc.GreeterBlockingStub greeterBlockingStub;

    @Override
    public void run(String... args) throws Exception {
        GreeterProto.HelloRequest req = GreeterProto.HelloRequest.newBuilder().setName("Mir").build();
        GreeterProto.HelloReply resp = greeterBlockingStub.sayHello(req);
        System.out.println("gRPC response: " + resp.getMessage());
    }
}

並在 application.yml 裏配置客户端目標:

grpc:
  client:
    localGrpc:
      address: static://localhost:9090

方式 B — 原生 gRPC ManagedChannel

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090)
    .usePlaintext()
    .build();
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
HelloReply reply = stub.sayHello(HelloRequest.newBuilder().setName("Mir").build());
channel.shutdown();

9. 調試與測試

  • 使用 grpcurlgrpcurl -plaintext localhost:9090 list / grpcurl -plaintext -d '{"name":"Mir"}' localhost:9090 com.example.grpc.Greeter/SayHello(如果啓用反射則更方便)
  • 用 Java 客户端或集成測試直接調用(如上 GrpcClientRunner
  • 開啓日誌 io.grpc 包的日誌級別便於排查

10. TLS、認證與攔截器

  • TLS:在生產環境強烈建議啓用 TLS(ServerBuilder.useTransportSecurity(certChainFile, privateKeyFile) 或 starter 提供的配置選項)。
  • 認證:可以在 gRPC 層添加服務器/客户端攔截器(ServerInterceptor/ClientInterceptor)做 token 驗證、限流、審計等。starter 支持以 Spring 管理的 interceptor 注入。
  • 攔截器示例(ServerInterceptor):
public class AuthInterceptor implements ServerInterceptor {
  @Override
  public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
      ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {

    String token = headers.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER));
    if (!valid(token)) {
      call.close(Status.UNAUTHENTICATED.withDescription("Invalid token"), new Metadata());
      return new ServerCall.Listener<ReqT>() {};
    }
    return next.startCall(call, headers);
  }
}

11. 常見問題與解決方案(常見坑)

  • 生成類找不到 / package 混亂:檢查 proto 中的 option java_package 與構建插件輸出路徑,使用 mvn clean 後重編譯。
  • 端口衝突 / 服務未啓動:確認 gRPC 端口配置(與 Spring MVC 端口不同),及是否有防火牆阻止。
  • 客户端連接報 UNAVAILABLE:通常是服務未啓動或網絡問題;檢查服務端日誌、端口監聽、usePlaintext()(測試環境)或 TLS 設置。
  • 版本不兼容:gRPC、protobuf、protoc 的版本需配套(如果使用插件自動下載通常安全)。
  • Spring Boot 熱重載(devtools)與生成代碼:若使用 IDE 熱重載,確保 generated-sources 被 IDE 識別,或在更改 proto 後重新執行構建任務。

12. 最佳實踐

  • 使用 proto 文件作為 API 的唯一事實源(source of truth),並把它放到單獨模塊以便共享(service / client 多項目共享)。
  • 對外接口採用語義化版本管理(proto package + major version 例如 com.example.grpc.v1),避免破壞兼容。
  • 在生產開啓 TLS,並設計好認證/鑑權方案(mTLS 或 token + interceptors)。
  • 使用健康檢查與監控(將 gRPC 服務狀態暴露給 Prometheus / k8s readiness probe)。
  • 在負載較高場景配置合適的線程池、流控與 keepalive 策略。
  • 使用攔截器統一實現日誌、鑑權與限流策略,方便統一管理。

13. 進階主題(推薦後續擴展)

  • 雙向流(bi-directional streaming)與流控(flow control)策略
  • gRPC + HTTP/JSON 網關(grpc-gateway)供瀏覽器/外部系統訪問
  • 在 Kubernetes 中使用 gRPC 負載均衡(DNS、Envoy/L4)和健康探測
  • 使用 protobuf 的 oneofmap 與性能優化(避免過大消息)

14. 完整示例回顧(快速複製)

  • src/main/proto/greeter.proto(見第4節)
  • Maven pom.xml(見第5節)
  • 服務實現 GreetingServiceImpl(見第7節)
  • 客户端 GrpcClientRunner(見第8節 A)
  • 配置 application.yml
server:
  port: 8080

grpc:
  server:
    port: 9090
    enable-reflection: true

  client:
    localGrpc:
      address: static://localhost:9090

運行 mvn spring-boot:run,觀察控制枱;或 mvn clean package 生成 jar 後運行。


15. 小結

通過本文你應能搭建一個從 proto 到可運行的 Spring Boot + gRPC 的完整流程:定義 proto → 通過插件生成 Java stub → 使用 grpc-spring-boot-starter 把服務以 Spring Bean 暴露 → 使用 @GrpcClient 或原生 ManagedChannel 調用。starter 大幅減少樣板代碼,推薦在短時間內快速搭建服務;生產環境注意啓用 TLS、攔截器與監控。