SpringBoot开启多端口探究--开启gRPC端口


前情提要

之前咱们聊过SpringBoot下开启多端口有3个思路,并分析了第一种开启独立management端口的实现细节,今天咱们聊聊开启gRPC端口的实现。


一、gRPC的特别之处

基于SpringBoot的微服务,其通信大多基于HTTP+JSON。前者为通信协议,后者为数据内容格式。更进一步说,这HTTP大多也是HTTP/1.1,随着HTTP协议的发展HTTP/2, HTTP/3也已经出来了。此外, 数据格式也未必都是JSON, XML、Protobuf、Thrift, Avro等结构化格式都是可行的。gRPC使用的默认是HTTP/2+Protobuf,其特别之处主要在HTTP/2与HTTP/1.1的区别。

HTTP/1.1HTTP/2
内容编码文本编码二进制编码, 相同信息比文本编码报文更短
网络请求方式在同一链接上,多个请求依次发送和接收在同一连接上,多个情况可以流水线发送

此外, HTTP/2还新增了header压缩和服务端push等特性。由此可见,gRPC依赖的HTTP/2与HTTP/1的有截然不同的要求, 在实际使用中也要专门处理,其中有独立的HTTP/2相关的协议编解码,应用层的消息编解码等。不过,这些也都被gRPC框架给屏蔽了。关于gRPC本身的实现细节在此不做讨论,我们关注在与SpringBoot的集成。

二、粗暴方案

手动创建相关对象,暴露到Spring容器中。

原始的GrpcObservabilityServer

此处为官网原始的GrpcObservabilityServer

public class GcpObservabilityServer {
  private Server server;

  private void start() throws IOException {
    int port = 50051;
    server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
        .addService(new GreeterImpl())
        .build()
        .start();
    logger.info("Server started, listening on " + port);
  }

  private void stop() throws InterruptedException {
    if (server != null) {
      server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
    }
  }

  private void blockUntilShutdown() throws InterruptedException {
    if (server != null) {
      server.awaitTermination();
    }
  }

  /**
   * Main launches the server from the command line.
   */
  public static void main(String[] args) throws IOException, InterruptedException {
    final GcpObservabilityServer server = new GcpObservabilityServer();
    server.start();

    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        System.err.println("*** shutting down gRPC server since JVM is shutting down");
        try {
          server.stop();
        } catch (InterruptedException e) {
          e.printStackTrace(System.err);
        }
        // Shut down observability
        observability.close();
        System.err.println("*** server shut down");
      }
    });

    server.blockUntilShutdown();
  }

  static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
      HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
      responseObserver.onNext(reply);
      responseObserver.onCompleted();
    }
  }
}

集成支持

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

    @Bean
    public Server newGrpcServer() {
        final GcpObservabilityServer server = new GcpObservabilityServer();
        server.start();
        // 最好再实现DisposableBean,当容器关闭时,关闭掉server实例。此处仅为演示
    }
}

方案评价

这个玩法实在过于暴力:

  1. 配置与代码耦合:端口指定完全可以依赖配置文件;
  2. 纯手工组装(容易出错):GreeterImpl如果依赖容器中的其他类,无法自动组装,同样的问题Server对象的组装也是存在的;
  3. 扩展性上来说,每新增一个grpcService都要记得在server端注册;

三、改进方案

基于grpc-spring-boot-starter,Maven依赖声明如下

<dependency>
    <groupId>io.github.lognet</groupId>
    <artifactId>grpc-spring-boot-starter</artifactId>
    <version>4.2.2</version>
</dependency>

基本原理

  1. 通过SpringBoot的AutoConfiguration机制创建Bean GrpcServerRunner;
  2. GrpcServerRunner implements CommandLineRunner接口, 容器初始化完成后执行;
  3. 扫描容器中所有带@GrpcService注解并且类型为BindableService的Bean, 加入到ServerBuilder中;
  4. 结合最终GrpcServerProperties对象, 构造server并启动;
    到这里整个Application就默认开放两个端口, 默认的HTTP/1.1端口和GRPC使用的HTTP/2.0端口。

改造结果

  1. 实际的service增加注解
// 使当前类被容器自动装配,后续就可以由Spring容器注入依赖对象
@Component
// 被GrpcRunner识别,组装GrpcServer用
@GrpcService
public class GreeterImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
      HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
      responseObserver.onNext(reply);
      responseObserver.onCompleted();
    }
  }
  1. 增加必要的配置
grpc:
  port: 8092
  netty-server:
    max-inbound-message-size: 10485760
  1. 保证GreeterImpl会被正确扫描到即可;

  2. 测试用环境说明
    SpringBoot 2.7.2
    Grpc 1.33.0

四、小结

本文介绍了SpringBoot下开启gRPC端口的两种方案,前者仅由容器管理声明周期,后者由框架负责对象的配置,生成和生命周期,开发者可以更多关注自身业务。实际工作中如何处理,还请读者“量体裁衣”,感谢你的阅读。

  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 错误提示中提到了一个未知的标志"--go-grpc",这可能是因为你的命令中使用了错误的标志。正确的命令应该是"protoc --go-grpc_out=. --go-grpc_opt=paths=source_relative user.proto"。请注意,标志"--go-grpc_out"和"--go-grpc_opt"之间应该有一个下划线"_",而不是一个空格。另外,你还需要确保你已经安装了正确的go协议编译器插件。你可以使用以下命令重新安装插件:"$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest"和"$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest"。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [使用protoc编译grpc问题记录(--go_out: protoc-gen-go: plugins are not supported;)](https://blog.csdn.net/m0_57777971/article/details/127864341)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [protoc-gen-go: plugin are not supported;use ‘protoc --go-grpc_out=...‘ to generate gRPC 的问题](https://blog.csdn.net/weixin_42875684/article/details/125652895)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值