SpringBoot集成gRPC

前一篇笔记中我们学习了在Java工程中使用gRPC的通用方式,这篇笔记我们学习如何在SpringBoot工程中集成gRPC。这里注意实际上gRPC官方没有对SpringBoot进行支持,这可能是出于商业竞争上考虑故意不提供SpringBoot支持,但gRPC集成到SpringBoot并不是什么复杂的功能,有很多完善的第三方Starter起步依赖供我们使用,我们也完全可以自己编写类似的起步依赖。

我们这里使用的是net.devhgrpc-server-spring-boot-starter

官方文档:https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/

工程搭建

我们这里创建了3个Maven模块,其中rpc-api模块包含gRPC的IDL定义文件和protoc编译生成的代码,rpc-serverrpc-client分别是RPC调用的服务端和客户端,它们是两个SpringBoot工程。

|_rpc-api
|_rpc-client
|_rpc-service

rpc-api模块中我们直接引入gRPC依赖和编译IDL所需的Maven插件。

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.51.0</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.51.0</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.51.0</version>
</dependency>
<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.7.1</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
            <configuration>
                <protocArtifact>com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}</pluginArtifact>
                <outputDirectory>${basedir}/src/main/java</outputDirectory>
                <clearOutputDirectory>false</clearOutputDirectory>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

对于gRPC客户端和服务端SpringBoot工程,我们可以引入以下依赖:

<dependency>
    <groupId>net.devh</groupId>
    <artifactId>grpc-spring-boot-starter</artifactId>
    <version>2.14.0.RELEASE</version>
</dependency>

上面依赖适用于我们的工程模块既作为客户端也作为服务端的情况,如果我们的SpringBoot工程单独作为客户端或服务端,也可以直接使用grpc-client-spring-boot-starter或是grpc-server-spring-boot-starter

rpc-api模块

rpc-api模块中,我们编写.proto定义文件。

demo_service.proto

syntax = "proto3";

option java_outer_classname = "DemoProtocol";
option java_package = "com.gacfox.demo.api";
option java_multiple_files = false;

message DemoReq {
  string msg = 1;
}

message DemoRsp {
  string msg = 1;
}

service DemoService {
  rpc demo(DemoReq) returns (DemoRsp) {}
}

编写完成后,可以使用mvn compile对其进行编译以供服务端和客户端引用。

rpc-server模块

服务端工程中,我们需要在工程配置文件application.properties配置启动gRPC服务端的主机名和端口。

application.properties

spring.application.name=grpc-server
spring.main.web-application-type=none
grpc.server.address=*
grpc.server.port=9090

DemoService.java中,我们使用了注解@GrpcService标注了该类,这样grpc-spring-boot-starter就可以自动帮我们注册gRPC服务,其他写法和之前普通Java工程中使用gRPC相同。

DemoService.java

package com.gacfox.demo.rpcservice.service;

import com.gacfox.demo.api.DemoProtocol;
import com.gacfox.demo.api.DemoServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@GrpcService
public class DemoService extends DemoServiceGrpc.DemoServiceImplBase {
    @Override
    public void demo(DemoProtocol.DemoReq request, StreamObserver<DemoProtocol.DemoRsp> responseObserver) {
        // ...具体业务处理
        DemoProtocol.DemoRsp demoRsp = DemoProtocol.DemoRsp.newBuilder().setMsg("成功").build();
        responseObserver.onNext(demoRsp);
        responseObserver.onCompleted();
    }
}

如果我们的gRPC服务端没有其它功能,我们可以指定如下配置不启动SpringMVC(Tomcat),避免占用8080端口和消耗额外的资源:

spring.main.web-application-type=none

rpc-client模块

客户端工程中我们需要在工程配置文件中配置服务端地址。

application.properties

spring.application.name=grpc-client
grpc.client.grpc-server.address=static://localhost:9090
grpc.client.grpc-server.negotiation-type=plaintext

其中,static://localhost:9090表示使用静态地址localhost:9090。实际开发中如果使用注册中心等方式,需要参考相关文档进行配置。

DemoController.java

package com.gacfox.demo.rpcclient.controller;

import com.gacfox.demo.api.DemoProtocol;
import com.gacfox.demo.api.DemoServiceGrpc;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class DemoController {
    @GrpcClient("grpc-server")
    private DemoServiceGrpc.DemoServiceBlockingStub demoServiceBlockingStub;

    @GetMapping("/test")
    public String test() {
        DemoProtocol.DemoReq demoReq = DemoProtocol.DemoReq.newBuilder()
                .setMsg("Hello")
                .build();
        DemoProtocol.DemoRsp demoRsp = demoServiceBlockingStub.demo(demoReq);
        System.out.println(demoRsp.getMsg());
        return "success";
    }
}

客户端代码中,我们注入Stub时,使用了@GrpcClient注解,注解需要一个服务名参数,该服务名需要和application.properties中定义的服务名一致,这里我们注入了接口的BlockingStub,后续代码就和之前章节没什么不同了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。