当前位置:   article > 正文

微服务高效通讯方案之SpringBoot + gRpc + Protobuf + Netty_java protobuf 网关

java protobuf 网关

初识

Spring Cloud 微服务一般采用feign调用,实际是http+json方式传输数据, 但这种方案从性能和稳定性来讲并不理想, 有没更好的微服务通讯方案?答案是有的, 如果你需要频繁的传输交互, 对性能要求较高, 且需要和各种平台(golang, c++等)对接, 那么SpringBoot + gRpc + Protobuf + Netty 这种通讯方案则更为合适。

脉络

  • 什么是gRpc
  • gRpc工作机制
  • Spring Boot与GRPC集成使用
  • 拓展:如何在微服务环境中使用

知行

1. 什么是GRPC

gRPC 是Google开源的高性能、通用的RPC框架。客户端与服务端约定接口调用, 可以在各种环境中运行,具有跨语言特性, 适合构建分布式、微服务应用。
在这里插入图片描述
为什么使用gRpc?

  1. 性能优异, 采用http2协议, Proto Buffer作序列化传输, 对比JSON与XML有数倍提升。
  2. 多语言支持,多客户端接入, 支持C++/GO/Ruby等语言。
  3. 支持负载均衡、跟踪、健康检查和认证。

2. GRPC工作机制

Grpc线程模型:

在这里插入图片描述
gRPC 的线程模型遵循 Netty 的线程分工原则,协议层消息的接收和编解码由 Netty 的 I/O(NioEventLoop) 线程负责, 应用层的处理由应用线程负责,防止由于应用处理耗时而阻塞 Netty 的 I/O 线程。BIO线程模型采用了线程池,但是后端的应用处理线程仍然采用同步阻塞的模型,阻塞的时间取决对方I/O处理的速度和网络I/O传输的速度。

客户端调用流程:

在这里插入图片描述

  1. 客户端 Stub 调用 发起 RPC 调用 远程服务。
  2. 获取服务端的地址信息(列表),使用默认的 LoadBalancer 策略,选择一个具体的 gRPC 服务端。
  3. 如果与服务端之间没有可用的连接,则创建 NettyClientTransport 和 NettyClientHandler,建立 HTTP/2 连接。
  4. 对请求使用 PB(Protobuf)序列化,通过 HTTP/2 Stream 发送给 gRPC 服务端。
  5. 服务端接收到响应之后,使用 PB(Protobuf)做反序列化。
  6. 回调 GrpcFuture 的 set(Response) 方法,唤醒阻塞的客户端调用线程,获取 RPC 响应数据。

3. Spring Boot与GRPC集成使用

下面讲解gRpc与Spring Boot的集成使用

3.1 服务设计

在这里插入图片描述

3.1 工程结构

在这里插入图片描述

  • grpc-demo : 父级工程。
  • grpc-client: 客户端工程,负责调用gRPC服务, 提供HTTP服务触发。
  • grpc-server: 股票服务端工程, 提供股票价格接口。
  • grpc-lib: 公用工程,生成为protobuf对象与gRPC Service。
3.2 引入gRpc依赖

在grpc-demo父级工程下面增加依赖:

  <dependencies>
        <!-- spring boot grpc 相关依赖 -->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-spring-boot-starter</artifactId>
            <version>2.4.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>2.4.0.RELEASE</version>
        </dependency>
    </dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里引入的是第三方封装的gRpc组件。

3.3 定义Protobuf结构体
  1. 在grpci-lib工程下, 创建StockService.proto文件:
    syntax = "proto3";
    
    option java_multiple_files = true;
    option java_package = "com.mirson.grpc.lib";
    option java_outer_classname = "StockServiceProto";
    
    // The stock service definition.
    service StockService {
        // get stock price by name
        rpc GetStockPrice (StockServiceRequest) returns (StockServiceReply) {
        }
    }
    
    // The request message
    message StockServiceRequest {
        string name = 1;
    }
    
    // The response message
    message StockServiceReply {
        string message = 1;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 定义一个接口GetStockPrice , 用于查询股票价格。
  • 定义请求结构体StockServiceRequest, 里面定义请求参数。
  • 定义返回结构体StockServiceReply, 里面定义返回数据属性。
  1. 增加POM依赖:
    <dependencies>
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>3.8.0</version>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
                <version>1.21.0</version>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
                <version>1.21.0</version>
                <exclusions>
                    <exclusion>
                        <artifactId>protobuf-java</artifactId>
                        <groupId>com.google.protobuf</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
                <version>1.21.0</version>
            </dependency>
        </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  2. 编译Proto文件, 生成java代码
  • 下载Protoc工具, 地址: https://github.com/protocolbuffers/protobuf/releases/download/v3.8.0/protoc-3.8.0-win64.zip
  • 下载gRpc插件, 地址: http://central.maven.org/maven2/io/grpc/protoc-gen-grpc-java/1.10.0/protoc-gen-grpc-java-1.10.0-windows-x86_64.exe
  • 执行Proto编译命令:
    指定gRpc插件和proto文件路径:
    protoc --plugin=protoc-gen-grpc-java=d:/TestCode/protoc-grpc.exe --java_out=./ --grpc-java_out=./ StockService.proto
    
    • 1
3.4 创建gRpc服务

接下来, 我们要提供gRpc服务, 在grpc-server服务工程, 创建GrpcStockService实现类:

@GrpcService
public class GrpcStockService extends StockServiceGrpc.StockServiceImplBase {

    @Override
    public void getStockPrice(StockServiceRequest request, StreamObserver<StockServiceReply> responseObserver) {
        String msg = "股票名称:" + request.getName() + ", 股票价格:" + (new Random().nextInt(100-20)+20);
        StockServiceReply reply =StockServiceReply.newBuilder().setMessage(msg).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

重写在定义的gRpc接口, getStockPrice获取股票价格。这里做测试, 我们随机生成股票价格。
创建GrpcServiceStartup启动类:

@SpringBootApplication
@ComponentScan(basePackages = {"com.mirson"})
public class GrpcServerStartup {
 public static void main(String[] args) {
     SpringApplication.run(GrpcServerStartup.class, args);
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

工程配置信息:

spring:
  application:
    name: grpc-server
grpc:
  server:
    port: 9999

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

定义服务名称和端口。

3.5 创建gRpc客户端

有了gRpc服务, 客户端如何调用? 在grpc-client工程中, 创建GrpcClientService类:

@Service
public class GrpcClientService {

    @GrpcClient("grpc-server")
    private StockServiceGrpc.StockServiceBlockingStub stockServiceStub;

    public String getStockPrice(final String name) {
        try {
            final StockServiceReply response = stockServiceStub.getStockPrice(StockServiceRequest.newBuilder().setName(name).build());
            return response.getMessage();
        } catch (final StatusRuntimeException e) {
            return "error!";
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

看到这里, 应该可以明白, 我们引入了生成的StockServiceGrpc, 采用StockServiceBlockingStub阻塞式调用。
通过GrpcClient注解, 指定了具体的grpc服务信息, 这里名称要与配置文件中定义的名称一致。

再创建启动类:

@SpringBootApplication
@RestController
@ComponentScan(basePackages = {"com.mirson"})
public class GrpcClientApplication {

    @Autowired
    private GrpcClientService grpcClientService;

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

    @RequestMapping("/")
    public String getStockPrice(@RequestParam(defaultValue = "中国平安") String name) {
        return grpcClientService.getStockPrice(name);
    }
}  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

为了便于测试, 我们通过Spring Boot 提供了一个web接口。

接下来, 就是客户端的工程配置文件, 重点是gRpc的配置信息:

server:
  port: 9000
spring:
  application:
    name: grpc-client

grpc:
  client:
    grpc-server:
      address: 'static://127.0.0.1:9999'
      enableKeepAlive: true
      keepAliveWithoutCalls: true
      negotiationType: plaintext
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

定义了一个名为grpc-server的远程服务,采用静态方式调用, 指定了IP和PORT, 当然, 也可以采用服务名称方式调用, 这个要结合服务注册发现组件来使用, 后面我们会讲如何与Nacos集成, 在微服务中使用。

3.6 启动验证
  • 启动服务端
  • 启动客户端
  • 访问接口地址: http://127.0.0.1:9000/?name=%E5%B7%A5%E5%95%86%E9%93%B6%E8%A1%8C
    在这里插入图片描述
    可以看到, 能够成功访问。

4. 拓展:如何在微服务环境中使用

4.1 说明

Spring Boot集成gRpc可以正常调用, 但是这种调用方式是静态指定了IP和端口, 如果发生变化,或者要支持负载, 该如何处理? 这里我们以Spring Cloud Alibaba环境来讲解下如何使用。
Spring Cloud Alibaba 采用的是Nacos作为服务注册发现, 这里的gRpc服务是可以支持的, 但是不支持最新版本, 我们要做些修改。

4.2 自定义gRpc启动器

为了便于微服务环境中的集成使用, 我们可以封装为自定义的启动器。

  • 创建cloud-starter-grpc工程
    增加依赖:
       <dependencies>
           <!-- spring boot grpc 相关依赖 -->
           <dependency>
               <groupId>net.devh</groupId>
               <artifactId>grpc-server-spring-boot-starter</artifactId>
               <version>${grpc.version}</version>
           </dependency>
           <dependency>
               <groupId>net.devh</groupId>
               <artifactId>grpc-client-spring-boot-starter</artifactId>
               <version>${grpc.version}</version>
           </dependency>
           <!-- Nacos服务注册发现依赖 -->
           <dependency>
               <groupId>com.alibaba.cloud</groupId>
               <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
           </dependency>
       </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 创建配置类:
    GrpcNacosConfiguration类:
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({NacosNamingService.class})
public class GrpcNacosConfiguration {

    @Autowired(required = false)
    private NacosRegistration nacosRegistration;

    @Autowired
    private GrpcServerProperties grpcProperties;

    /**
     * 自定义将gRpc服务信息注册至Nacos, 解决gRpc组件不支持最新Cloud Alibaba 问题
     */
    @PostConstruct
    public void init() {
        if (this.nacosRegistration == null) {
            return;
        }
        final int port = this.grpcProperties.getPort();
        if (port != -1) {
            this.nacosRegistration.getMetadata().put(GrpcUtils.CLOUD_DISCOVERY_METADATA_PORT, Integer.toString(port));
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

将gRpc服务信息注册至Nacos, 解决gRpc组件不支持最新版Cloud Alibaba 问题。这样gRpc内部依赖的低版本Nacos就不用理会,直接采用我们工程当中引入的最新版的Nacos组件。

4.3 微服务调用配置

引入封装的cloud-starter-grpc工程, 会自动加入gRpc相关依赖, 接下来在工程中如何配置呢?
只需修改adress, 将ip改为服务注册的名称即可:

# grpc 配置
grpc:
  server:
    port: 19989
grpc:
  client:
    grpc-server:
      # address: 'static://127.0.0.1:9999'
      address: 'discovery:///grpc-server'
      enableKeepAlive: true
      keepAliveWithoutCalls: true
      negotiationType: plaintext
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
4.4 如何对外提供服务

基于内部微服务, 可以通过服务注册发现工具, 比如Nacos, consul等直接调用, 如何对外访问呢? 可以通过Cloud Gateway网关作一层转发, 或者自己编写一个Proxy代理服务, 通过Netty对外提供访问, Spring Boot 如何与Netty进行集成, 可以参考我写的另一篇博文: [Spring Boot + Netty+WebSocket+Protobuf 集成通讯配置]

总结

  • SpringBoot +Netty+Protobuf+gRpc 在微服务中是一种高效的通讯方案,相比feign能够减少连接和数据传输的开销, 适用在能要求较高的场景。
  • Spring Cloud Alibaba 版本更新比较快, 如果大家采用最新的Cloud版本, 要注意gRpc组件的兼容性, 如果出现问题, 可以按照上面所讲的, 针对性的做些调整修改。

本文由mirson创作分享,如需进一步交流,请加QQ群:19310171或访问www.softart.cn

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/128701
推荐阅读
相关标签
  

闽ICP备14008679号