一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Spring Boot 4.0 与 gRPC 极简集成:4步搞定 比REST还丝滑

时间:2026-07-17 08:42:03 编辑:袖梨 来源:一聚教程网

01 引言

gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 + Protocol Buffers,序列化效率和传输速度都比传统 REST + JSON 高出一截,在微服务内部通信、移动端调用、跨语言服务等场景下非常吃香。

但传统 gRPC 的 Java 集成姿势比较原始:手写 ServerBuilder、手写 ManagedChannel、手写拦截器链,样板代码一堆。Spring gRPC(1.0.x,2025 年正式发布)的目标就是把这些脏活全包了,让你像写普通 Spring Boot 应用一样写 gRPC。

02 环境准备

Spring gRPC对环境还是有要求的:

  • 最低Java17
  • Spring Framework 7.0.1
  • Spring gRPC 1.0.x使用Spring Boot要求4.0.x

03 Spring gRPC Server

3.1 Maven依赖

 复制代码<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-grpc-server</artifactId>
</dependency>

使用Spring的脚手架,还引入了protobuf-maven-plugin编译插件。

Protobuf 编译插件,让 Maven/Gradle 自动把 .proto 文件编译成 Java 类。

3.2 定义 proto 文件

src/main/proto/hello.proto 创建服务定义(注意把 java_package 换成你自己的包名)。

使用Spring脚手架自动生成proto文件包。

 复制代码syntax = "proto3";option java_multiple_files = true;
option java_package = "com.simonking.ws.boot.grpc.server.proto";
option java_outer_classname = "UserProto";package user;// 用户信息服务 - 展示多参数 gRPC 方法
service UserService {
  // 多参数创建用户
  rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
  // 多参数查询用户
  rpc GetUser (GetUserRequest) returns (GetUserResponse);
}// ============ 通用消息 ============// 用户信息
message UserInfo {
  int64 id = 1;
  string username = 2;
  string email = 3;
  int32 age = 4;
  string phone = 5;
  string created_at = 6;
  string updated_at = 7;
}// ============ 创建用户 (多参数) ============message CreateUserRequest {
  string username = 1;
  string email = 2;
  int32 age = 3;
  string phone = 4;
  string password = 5;
  string nickname = 6;
}message CreateUserResponse {
  bool success = 1;
  string message = 2;
  int64 user_id = 3;
}// ============ 查询用户 ============message GetUserRequest {
  int64 user_id = 1;
}message GetUserResponse {
  bool success = 1;
  string message = 2;
  UserInfo user = 3;
}

在这里定义方法,参数以及响应。

字段说明

  • java_package:生成的 Java 类放在哪个包
  • java_multiple_files = true:每个 message 一个独立 .java 文件(推荐)
  • java_outer_classname:容器类名(一般用不到,因为开了 multiple_files)
  • 字段后面的 = 1 是 protobuf 的字段编号,不能改也不能重用,是序列化兼容性的关键

3.3 编译生成stub

执行:

 复制代码./mvnw clean compile

编译完成之后,会在target/generated-sources/protobuf 生成对应的实体和服务:

IntelliJ 用户注意:右键 target/generated-sources 目录 → Mark Directory asGenerated Sources Root,否则 IDE 识别不到。

3.4 实现方法

继承XxxGrpc.XxxImplBase即可。

 复制代码@GrpcService
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");    // 内存存储模拟数据库
    private final ConcurrentHashMap<Long, UserInfo> userStore = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);    // ===================== 1. 创建用户 =====================    @Override
    public void createUser(CreateUserRequest request,
                           StreamObserver<CreateUserResponse> responseObserver) {
        long userId = idGenerator.getAndIncrement();
        String now = LocalDateTime.now().format(DTF);        UserInfo user = UserInfo.newBuilder()
                .setId(userId)
                .setUsername(request.getUsername())
                .setEmail(request.getEmail())
                .setAge(request.getAge())
                .setPhone(request.getPhone())
                .setCreatedAt(now)
                .setUpdatedAt(now)
                .build();        userStore.put(userId, user);        CreateUserResponse response = CreateUserResponse.newBuilder()
                .setSuccess(true)
                .setMessage("用户创建成功")
                .setUserId(userId)
                .build();        responseObserver.onNext(response);
        responseObserver.onCompleted();
        log.info("===== createUser 响应: userId={} =====", userId);
    }    // ===================== 2. 查询用户 =====================    @Override
    public void getUser(GetUserRequest request,
                        StreamObserver<GetUserResponse> responseObserver) {
        log.info("===== getUser: userId={} =====", request.getUserId());        UserInfo user = userStore.get(request.getUserId());
        if (user == null) {
            GetUserResponse response = GetUserResponse.newBuilder()
                    .setSuccess(false)
                    .setMessage("用户不存在: " + request.getUserId())
                    .build();
            responseObserver.onNext(response);
            responseObserver.onCompleted();
            return;
        }        GetUserResponse response = GetUserResponse.newBuilder()
                .setSuccess(true)
                .setMessage("查询成功")
                .setUser(user)
                .build();        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

注意实现类上的注解:org.springframework.grpc.server.service.GrpcService

3.5 启动类

启动类不需要额外的编写,但是启动类帮我们做了三件事:

  • 启动 Netty-based gRPC Server
  • 监听 spring.grpc.server.port(默认 9090
  • 把所有 BindableService Bean 注册上去

04 Spring gRPC Client

客户端的编写和服务端差不多。

4.1 Maven依赖

 复制代码<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-grpc-client</artifactId>
</dependency><build>
    <plugins>
        <plugin>
            <groupId>io.github.ascopes</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

3.2 定义 proto 文件

同服务端

3.3 编译生成stub

同服务端

3.4 配置客户端

由于gRPC基于Netty,所以需要配置通道信息。

 复制代码@Configuration
public class GrpcClientConfig {    @Value("${grpc.client.host:localhost}")
    private String host;    @Value("${grpc.client.port:9090}")
    private int port;    @Bean(destroyMethod = "shutdown")
    public ManagedChannel grpcChannel() {
        return ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext()
                .build();
    }    @Bean
    public UserServiceGrpc.UserServiceBlockingStub blockingStub(ManagedChannel channel) {
        return UserServiceGrpc.newBlockingStub(channel);
    }
}

3.5 调用方法

 复制代码@RestController
@RequestMapping("/user")
public class UserController {    private static final Logger log = LoggerFactory.getLogger(UserController.class);    @Autowired
    private UserServiceBlockingStub blockingStub;    // ===================== 1. 创建用户 =====================    @PostMapping("/create")
    public Map<String, Object> createUser() {
        CreateUserRequest request = CreateUserRequest.newBuilder()
                .setUsername("test_user")
                .setEmail("[email protected]")
                .setAge(25)
                .setPhone("13800138000")
                .setPassword("123456")
                .setNickname("测试用户")
                .build();        CreateUserResponse resp = blockingStub.createUser(request);
        log.info("createUser -> userId={}", resp.getUserId());
        return Map.of("success", resp.getSuccess(), "message", resp.getMessage(), "userId", resp.getUserId());
    }    // ===================== 2. 查询用户 =====================    @GetMapping("/{userId}")
    public Map<String, Object> getUser(@PathVariable long userId) {
        GetUserRequest request = GetUserRequest.newBuilder()
                .setUserId(userId)
                .build();
        GetUserResponse resp = blockingStub.getUser(request);        Map<String, Object> result = new LinkedHashMap<>();
        result.put("success", resp.getSuccess());
        result.put("message", resp.getMessage());
        if (resp.hasUser()) {
            result.put("user", toUserMap(resp.getUser()));
        }
        return result;
    }    // ===================== 工具方法 =====================    private Map<String, Object> toUserMap(UserInfo user) {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("id", user.getId());
        map.put("username", user.getUsername());
        map.put("email", user.getEmail());
        map.put("age", user.getAge());
        map.put("phone", user.getPhone());
        map.put("createdAt", user.getCreatedAt());
        map.put("updatedAt", user.getUpdatedAt());
        return map;
    }
}

3.6 测试

创建用户

查询用户

热门栏目