Spring Boot 集成 GRPC

时间:2023-01-30 01:17:08

技术:SpringBoot 2.0.5.RELEASE + Grpc 1.15.0    运行环境:JDK 1.8 

概述:SpringBoot框架中集成Grpc服务

详细

一.背景

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

Grpc 由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。

项目定位是电商平台和WMS系统中间的插件服务,wms采用.net语言开发,电商平台采用java开发,所以出现了多语言间的数据交换,开始考虑使用json协议,方便快捷,但是考虑到效率和系统发展的问题,考虑使用RPC框架,斟酌之后敲定为GRPC。

但是,我们拓展服务使用的SpringBoot,Grpc和SpringBoot集成的文章不是很多,大部分都是采用github上*-springboot.grpc.starter的项目,看了一下,并不是很感兴趣,所以自己根据官网的demo,尝试与SpringBoot集成,所以在这和大家分享下

二. 准备工作

开发环境

Spring Boot 2.0.5.RELEASE
Grpc 1.15.0
Inteliji Idea 2018.3

项目截图

Spring Boot 集成 GRPC

三.实现过程

1. 配置SpringBoot项目
  • Pom文件增加
<parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.5.RELEASE</version>
  </parent>

  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
      </dependency>
  </dependencies>
  • 编写启动类
public static void main(String[] args) {
       // 启动SpringBoot web
      SpringApplication.run(Launcher.class, args);
}
2. 增加GRPC支持和GRPC代码生成工具,集成GrpcServer端
  • 增加POM文件
<dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.15.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.15.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.15.0</version>
        </dependency>
  • 增加maven工具
<build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.15.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 编写Server端实现类
public class HelloWorldService extends HelloWorldServiceGrpc.HelloWorldServiceImplBase {

    @Override
    public void welcome(HelloWorld.NameRequest request, StreamObserver<HelloWorld.EchoResponse> responseObserver) {
        HelloWorld.EchoResponseOrBuilder echoResponseOrBuilder = HelloWorld.EchoResponse.newBuilder();
        ((HelloWorld.EchoResponse.Builder) echoResponseOrBuilder).setMergename("welcome " + request.getName());
        responseObserver.onNext(((HelloWorld.EchoResponse.Builder) echoResponseOrBuilder).build());
        responseObserver.onCompleted();
    }
}
  • 增加Grpc启动器
@Component("grpcLauncher")
public class GrpcLauncher {

    private Logger logger = LoggerFactory.getLogger(Launcher.class);


    /**
     * 定义Grpc Server
     */
    private Server server;


    @Value("${grpc.server.port}")
    private Integer grpcServerPort;
    /**
     * GRPC 服务启动方法
     * @param grpcServiceBeanMap
     */
    public void grpcStart(Map<String, Object> grpcServiceBeanMap) {
        try{
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            ServerBuilder serverBuilder = ServerBuilder.forPort(grpcServerPort).useTransportSecurity(ssc.certificate(), ssc.privateKey());
            for (Object bean : grpcServiceBeanMap.values()){
                serverBuilder.addService((BindableService) bean);
                logger.info(bean.getClass().getSimpleName() + " is regist in Spring Boot");
            }
            server = serverBuilder.build().start();
            logger.info("grpc server is started at " +  grpcServerPort);
            server.awaitTermination();
            Runtime.getRuntime().addShutdownHook(new Thread(()->{grpcStop();}));
        } catch (IOException e){
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * GRPC 服务Stop方法
     */
    private void grpcStop(){
        if (server != null){
            server.shutdownNow();
        }
    }
}
  • 添加自定义注解用于扫描Grpc服务
/**
 * 自定义注解,用于获取Spring扫描到的类
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface GrpcService {
}
  • 在SpringBoot中增加Grpc启动器,并将Spring管理的类,添加到Grpc服务中
/**
 * Spring Boot 启动器
 */
@SpringBootApplication
public class Launcher {

    public static void main(String[] args) {
        // 启动SpringBoot web
        ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Launcher.class, args);
        Map<String, Object> grpcServiceBeanMap =  configurableApplicationContext.getBeansWithAnnotation(GrpcService.class);
        GrpcLauncher grpcLauncher = configurableApplicationContext.getBean("grpcLauncher",GrpcLauncher.class);
        grpcLauncher.grpcStart(grpcServiceBeanMap);
    }
}

至此Server端,集成完毕

Spring Boot 集成 GRPC

3. 增加Client端
  • 获取Chanel
@Component
public class GrpcClientMananer {
    @Value("${grpc.client.host}")
    private String host;
    @Value("${grpc.client.port}")
    private Integer port;

    public ManagedChannel getChannel(){
        NettyChannelBuilder ncb = NettyChannelBuilder.forAddress(host, port).sslContext(SslContextBuilder.forClient()
                        .sslProvider(OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK)
                        .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                        .trustManager(InsecureTrustManagerFactory.INSTANCE)
                        .applicationProtocolConfig(
                                new ApplicationProtocolConfig(
                                        ApplicationProtocolConfig.Protocol.ALPN,
                                        ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
                                        ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
                                        ApplicationProtocolNames.HTTP_2,
                                        ApplicationProtocolNames.HTTP_1_1))
                        .build());
        return ncb.build();                
    }
}
  • 调用方式
@Component
public class HelloWorldClient {

    @Autowired
    private GrpcClientMananer grpcClientMananer;

    public void call(){
        ManagedChannel channel = grpcClientMananer.getChannel();
        HelloWorld.NameRequestOrBuilder nameRequestOrBuilder = HelloWorld.NameRequest.newBuilder();
        ((HelloWorld.NameRequest.Builder) nameRequestOrBuilder).setName("Geek");
        HelloWorldServiceGrpc.HelloWorldServiceBlockingStub stub = HelloWorldServiceGrpc.newBlockingStub(channel);
        HelloWorld.EchoResponse echoResponse = stub.welcome(((HelloWorld.NameRequest.Builder) nameRequestOrBuilder).build());
        System.out.println(echoResponse.getMergename());
    }

}

`