跨语言RPC框架:Gprc和protobuf的使用例子分析(包含完整项目源码)

概述

  • Grpc是Google开发的一款高性能、跨语言的RPC框架,使用Google的数据序列化框架protobuf作为IDL(接口定义语言)跨语言的服务接口定义,以及数据序列化。在通信层面基于HTTP2.0实现,这是与大多数RPC框架基于TCP实现不一样的,主要原因是HTTP2.0在性能方面有了比较大的提升。除此之外,Grpc的设计的其中一个目的就是提供移动客户端与服务端的RPC调用,所以基于HTTP协议实现可以应对TCP层面会遇到的内网防火墙问题,因为大部分的防火墙会屏蔽掉除80和443之外的其他端口。
  • 以下使用Java语言实现一个HelloWorld项目来展示Grpc和protobuf的相关使用方法。项目完整源码在个人Github:Github grpc-demo

1. 使用protobuf定义RPC服务接口与相关数据结构

  1. 使用protobuf文件来定义业务数据类型和RPC服务方法并编译。如下在hello_message.proto文件定义数据结构,在rpc_hello_service.proto文件中定义服务方法,实现如下:(关于protobuf的更多语言可以参考官方文档)
  • hello_message.proto:定义了请求数据结构HelloRequest,响应数据结构HelloResponse。

    syntax = "proto3";
    
    package com.yzxie.demo.java.grpc.proto;
    
    // 编译生成的Java文件对应的包和类名
    option java_multiple_files = true;
    option java_package = "com.yzxie.demo.java.grpc.rpc";
    option java_outer_classname = "HelloMessageProto";
    
    // 请求数据结构
    message HelloRequest {
        string userName = 1;
    }
    
    // 响应数据结构
    message HelloResponse {
        string message = 1;
    }
    
  • rpc_hello_service.proto:声明了一个RpcHelloServer服务接口和一个sayHello方法。

    syntax = "proto3";
    
    package com.yzxie.demo.java.grpc.proto;
    
    // 编译生成的Java文件对应的包和类名
    option java_multiple_files = true;
    option java_package = "com.yzxie.demo.java.grpc.rpc";
    option java_outer_classname = "RpcHelloServiceProto";
    
    import "com/yzxie/demo/java/grpc/proto/hello_message.proto";
    
    // RPC接口
    service RpcHelloService {
        // RPC方法声明
        rpc sayHello(HelloRequest) returns (HelloResponse);
    }
    
  1. 接着需要编译为Java对应的类文件,可以使用命令来生成,不过一般会结合maven的编译插件来实现,这样可以在IDEA直接编译,如下为maven的插件配置:

    <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>0.5.0</version>
        <configuration>
            <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
            <pluginId>grpc-java</pluginId>
            <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>compile</goal>
                    <goal>compile-custom</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    
  2. 然后可以在IDEA的右边看到如下所示的编译选项,故直接执行即可编译出对应的Java类文件,最后打包成标准的jar被Grpc服务端和Grpc客户端引用即可。
    在这里插入图片描述

2.实现RPC服务的方法和Grpc服务端

  1. 通常步骤1编译得到的接口文件为RpcHelloServiceGrpc,所以RPC服务方法的实现主要是通过继承RpcHelloServiceGrpc的静态内部类RpcHelloServiceImplBase并重写对应的方法来实现业务逻辑。

    如下实现类RpcHelloServiceImpl继承于RpcHelloServiceGrpc.RpcHelloServiceImplBase并重写sayHello方法:

    public class RpcHelloServiceImpl extends RpcHelloServiceGrpc.RpcHelloServiceImplBase {
    
        @Override
        public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
            HelloResponse response = HelloResponse.newBuilder()
                    .setMessage("Hello, " + request.getUserName())
                    .build();
            // 响应RPC客户端的RPC调用请求
            responseObserver.onNext(response);
            responseObserver.onCompleted();
        }
    }
    
  2. Grpc服务端的核心实现如下:主要是在指定的端口监听Grpc客户端的RPC请求到来,然后进行处理。

    @PostConstruct
    public void init() {
        // 指定监听端口和RPC服务的业务逻辑实现类rpcHelloService
        server = ServerBuilder.forPort(port).addService(rpcHelloService).build();
    }
    
    // 开启在指定端口监听Grpc客户端的RPC方法调用请求
    public void start() {
        try {
            server.start();
            LOG.info("GrpcServer listen on {}", port);
            server.awaitTermination();
        } catch (Exception e) {
            LOG.error("GrpcServer listen on {}", port, e);
        }
    
        // 关闭回调hook
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                LOG.info("GrpcServer shutdown {}", port);
                GrpcServer.this.stop();
            }
        });
    }
    

3.实现Grpc客户端

  1. 首先通过指定Grpc服务端的域名host和监听端口号port来创建Grpc客户端和Grpc服务端通信的channel,然后需要创建该RPC服务对应的服务端点(stub),如下:

    // Grpc客户端channel
    private ManagedChannel channel;
    // RPC服务端点(stub)
    private RpcHelloServiceGrpc.RpcHelloServiceBlockingStub blockingStub;
    
    @PostConstruct
    public void init() {
        // 指定Grpc服务端的域名和端口
        channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build();
        blockingStub = RpcHelloServiceGrpc.newBlockingStub(channel);
    }
    
  2. 然后通过该RPC服务端点来对Grpc服务端发起对该RPC服务端点的RPC方法调用:

    public String getHello(String userName) {
        try {
            HelloRequest request = HelloRequest.newBuilder().setUserName(userName).build();
            // 通过RPC服务端点(stub)发起RPC方法调用
            HelloResponse response = blockingStub.sayHello(request);
            return response.getMessage();
        } catch (StatusRuntimeException e) {
            LOG.error("getHello {}", userName, e);
        }
    
        return "";
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值