• 简单的GRPCdemo解析


    GRPC调用示例

    调用示例如下图所示

    为了保证调用方和服务提供方能够正常通信,我们需要先约定一个通信过程中的契约

    Protocol Buffer 代码如下所示

    syntax = "proto3";
    
    
    option java_multiple_files = true;
    option java_package = "io.grpc.hello";
    option java_outer_classname = "HelloProto";
    option objc_class_prefix = "HLW";
    
    
    package hello;
    
    
    service HelloService{
    rpc Say(HelloRequest) returns (HelloReply) {}
    }
    
    
    message HelloRequest {
    string name = 1;
    }
    
    
    message HelloReply {
    string message = 1;
    }

    我们可以利用 Protocol Buffer 的编译器 protoc,再配合 gRPC Java 插件(protoc-gen-grpc-java),通过命令行 protoc3 加上 plugin 和 proto 目录地址参数,我们就可以生成消息对象和 gRPC 通信所需要的基础代码

     

    发送原理

    我们就可以基于生成的代码写下调用端代码

    package io.grpc.hello;
    
    
    import io.grpc.ManagedChannel;
    import io.grpc.ManagedChannelBuilder;
    import io.grpc.StatusRuntimeException;
    
    
    
    
    import java.util.concurrent.TimeUnit;
    
    
    public class HelloWorldClient {
    
    
        private final ManagedChannel channel;
        private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub;
        /**
        * 构建Channel连接
        **/
        public HelloWorldClient(String host, int port) {
            this(ManagedChannelBuilder.forAddress(host, port)
                    .usePlaintext()
                    .build());
        }
    
    
        /**
        * 构建Stub用于发请求
        **/
        HelloWorldClient(ManagedChannel channel) {
            this.channel = channel;
            blockingStub = HelloServiceGrpc.newBlockingStub(channel);
        }
        
        /**
        * 调用完手动关闭
        **/
        public void shutdown() throws InterruptedException {
            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
        }
    
    
     
        /**
        * 发送rpc请求
        **/
        public void say(String name) {
            // 构建入参对象
            HelloRequest request = HelloRequest.newBuilder().setName(name).build();
            HelloReply response;
            try {
                // 发送请求
                response = blockingStub.say(request);
            } catch (StatusRuntimeException e) {
                return;
            }
            System.out.println(response);
        }
    
    
        public static void main(String[] args) throws Exception {
                HelloWorldClient client = new HelloWorldClient("127.0.0.1", 50051);
                try {
                    client.say("world");
                } finally {
                    client.shutdown();
                }
        }
    }

    首先用 host 和 port 生成 channel 连接;

    然后用前面生成的 HelloService gRPC 创建 Stub 类;

    最后我们可以用生成的这个 Stub 调用 say 方法发起真正的 RPC 调用,后续其它的 RPC 通信细节就对我们使用者透明了。

     

    接收原理

    static class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
    
    
      @Override
      public void say(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
        HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
      }
    }

    上面 HelloServiceImpl 类是按照 gRPC 使用方式实现了 HelloService 接口逻辑,但是对于调用者来说并不能把它调用过来,因为我们没有把这个接口对外暴露,在 gRPC 里面我们是采用 Build 模式对底层服务进行绑定,具体代码如下:

    package io.grpc.hello;
    
    
    import io.grpc.Server;
    import io.grpc.ServerBuilder;
    import io.grpc.stub.StreamObserver;
    
    
    import java.io.IOException;
    
    
    
    
    public class HelloWorldServer {
    
    
      private Server server;
    
    
      /**
      * 对外暴露服务
      **/
      private void start() throws IOException {
        int port = 50051;
        server = ServerBuilder.forPort(port)
            .addService(new HelloServiceImpl())
            .build()
            .start();
        Runtime.getRuntime().addShutdownHook(new Thread() {
          @Override
          public void run() {
            HelloWorldServer.this.stop();
          }
        });
      }
    
    
      /**
      * 关闭端口
      **/
      private void stop() {
        if (server != null) {
          server.shutdown();
        }
      }
    
    
      /**
      * 优雅关闭
      **/
      private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
          server.awaitTermination();
        }
      }
    
    
    
    
      public static void main(String[] args) throws IOException, InterruptedException {
        final HelloWorldServer server = new HelloWorldServer();
        server.start();
        server.blockUntilShutdown();
      }
      
    }

    服务对外暴露的目的是让过来的请求在被还原成信息后,能找到对应接口的实现。在这之前,我们需要先保证能正常接收请求,通俗地讲就是要先开启一个 TCP 端口,让调用方可以建立连接,并把二进制数据发送到这个连接通道里面,这里依然只展示最重要的部分。

    Handler 里面会绑定一个 FrameListener,gRPC 会在这个 Listener 里面处理收到数据请求的 Header 和 Body,并且也会处理 Ping、RST 命令等,具体流程如下图所示:

     

    总结

    总的来说,其实我们可以简单地认为 gRPC 就是采用 HTTP/2 协议,并且默认采用 PB 序列化方式的一种 RPC,它充分利用了 HTTP/2 的多路复用特性,使得我们可以在同一条链路上双向发送不同的 Stream 数据,以解决 HTTP/1.X 存在的性能问题。

  • 相关阅读:
    Java 中文 乱码问题
    JQuery 操作 radio 被坑一例
    标准I/O库之打开和关闭流
    标准I/O库之缓冲
    标准I/O库之标准输入、标准输出和标准出错
    标准I/O库之流和FILE对象
    文件和目录之文件访问权限位小结
    文件和目录之设备特殊文件
    文件和目录之chdir、fchdir和getcwd函数
    文件和目录之读目录
  • 原文地址:https://www.cnblogs.com/ak918xp/p/16379287.html
Copyright © 2020-2023  润新知