• 后端学习 -gRPC


    参考官方文档

    1 什么是 RPC

    • RPC 执行流程:调用方和被调用方

    1. Client 调用以本地调用方式调用服务;
    2. Client Stub 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(桩代码是未实现的接口,此处使用动态代理);
    3. Client Stub 找到服务地址,并将消息发送到服务端;
    4. Server Stub 收到消息后进行解码;
    5. Server Stub 根据解码结果调用本地的服务;
    6. 本地服务执行并将结果返回给 Server Stub;
    7. Server Stub 将返回结果打包成消息并发送至消费方;
    8. Client Stub 接收到消息,并进行解码;
    9. Client 得到最终结果
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • Stub (桩代码)指满足形式要求但没有实现实际功能的占位或代理代码
    • RPC 中的桩代码负责远程请求或响应
    • RPC 和 HTTP 的关系

      1. 基于 HTTP 的远程调用方案(包含了接口规范、序列化反序列化等)和 RPC 是并列的
      2. 单从 HTTP 协议来看,RPC 可以使用 HTTP 作为通信协议
        在这里插入图片描述
    • 基于 HTTP 的远程调用方案和 RPC 的相同点、不同点

      • 相同点
        1. 都可以实现远程调用(上图的顶端节点)
      • 不同点
        1. 基于 HTTP RESTful 的远程调用可读性更好
        2. RPC 可以针对场景选用不同的框架(如 Dubbo、gRPC 等),不同的通信协议(HTTP、TCP、UDP),以及不同的序列化方式(JSON、PB 等),效率更高

    2 ProtocolBuffer

    • PB 是一种语言无关、平台无关、可扩展的序列化结构数据的接口描述语言(Interface Description Language, IDL),可用于通信协议、数据存储等
    • 使用 service 定义服务,使用 message 定义服务使用的类
      • service 定义的每个 rpc 方法,根据输入和返回是否为 stream 修饰,分为四种情况
    • 基本语法
    syntax = "proto3"  // 语法版本
    
    option java_package = "com.hit.demo";  // 生成桩代码的包,位于target的子目录
    ...
    
    // 定义服务
    service PhoneLookup {
    	rpc QueryPhoneByAddress (Request) returns (Response) {}
    }
    
    message Requset {
    	required string id = 1;  // required:必须传入1个参数
    	optional string name = 2;  // optional:可以传入0或1个参数,如果不传入则为默认0值
    	repeated string address = 3;  // repeated:可以传入0或多个参数
    }
    
    message Response {
    	string id = 1;
    	ResultCode result = 2 [default = SUCCESS];  // 声明默认值
    	repeated int64 number = 3;
    	repeated bool active = 4;
    	
    	enum ResultCode {
    		SUCCESS = 0;
    		FAIL = 1;
    		UNKNOWN = 3;
    	}
    }
    
    • 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

    3 Demo: HelloService

    1. 创建 Maven 工程,导入 gRPC 依赖,配置 PB 插件

       
       <project xmlns="http://maven.apache.org/POM/4.0.0"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       
           <properties>
               <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
               <grpc.version>1.51.1grpc.version>
               <protobuf.version>3.21.7protobuf.version>
               <protoc.version>3.21.7protoc.version>
               
               <maven.compiler.source>1.7maven.compiler.source>
               <maven.compiler.target>1.7maven.compiler.target>
           properties>
       
           <dependencyManagement>
               <dependencies>
                   <dependency>
                       <groupId>io.grpcgroupId>
                       <artifactId>grpc-bomartifactId>
                       <version>${grpc.version}version>
                       <type>pomtype>
                       <scope>importscope>
                   dependency>
               dependencies>
           dependencyManagement>
       
           <dependencies>
               <dependency>
                   <groupId>io.grpcgroupId>
                   <artifactId>grpc-netty-shadedartifactId>
               dependency>
               <dependency>
                   <groupId>io.grpcgroupId>
                   <artifactId>grpc-protobufartifactId>
               dependency>
               <dependency>
                   <groupId>io.grpcgroupId>
                   <artifactId>grpc-stubartifactId>
               dependency>
               <dependency>
                   <groupId>com.google.protobufgroupId>
                   <artifactId>protobuf-java-utilartifactId>
                   <version>${protobuf.version}version>
               dependency>
               <dependency>
                   <groupId>com.google.code.gsongroupId>
                   <artifactId>gsonartifactId>
                   <version>2.9.0version> 
               dependency>
               <dependency>
                   <groupId>org.apache.tomcatgroupId>
                   <artifactId>annotations-apiartifactId>
                   <version>6.0.53version>
                   <scope>providedscope> 
               dependency>
               <dependency>
                   <groupId>io.grpcgroupId>
                   <artifactId>grpc-testingartifactId>
                   <scope>testscope>
               dependency>
               <dependency>
                   <groupId>junitgroupId>
                   <artifactId>junitartifactId>
                   <version>4.12version>
                   <scope>testscope>
               dependency>
               <dependency>
                   <groupId>org.mockitogroupId>
                   <artifactId>mockito-coreartifactId>
                   <version>3.4.0version>
                   <scope>testscope>
               dependency>
           dependencies>
       
           <build>
               <extensions>
                   <extension>
                       <groupId>kr.motd.mavengroupId>
                       <artifactId>os-maven-pluginartifactId>
                       <version>1.6.2version>
                   extension>
               extensions>
               <plugins>
                   <plugin>
                       <groupId>org.xolstice.maven.pluginsgroupId>
                       <artifactId>protobuf-maven-pluginartifactId>
                       <version>0.6.1version>
                       <configuration>
                           <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}protocArtifact>
                           <pluginId>grpc-javapluginId>
                           <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}pluginArtifact>
                       configuration>
                       <executions>
                           <execution>
                               <goals>
                                   <goal>compilegoal>
                                   <goal>compile-customgoal>
                               goals>
                           execution>
                       executions>
                   plugin>
               plugins>
           build>
       project>
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    2. 定义 PB

       syntax="proto3";
       
       option java_package = "com.xuecheng.fastdfs.target.stub";  // 指定桩代码在target目录下的包
       option java_generic_services = true;
       
       service HelloService {
         rpc sayHello(HelloRequest) returns (HelloResponse) {}
       }
       
       message HelloRequest {
         int32 requestId = 1;
         string name = 2;
       }
       
       message HelloResponse {
         int32 responseId = 1;
         bool responseState = 2;
         string greeting = 3;
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. 执行 Maven lifecycle compile 编译工程

    • 根据插件的配置会自动执行 protobuf compile(生成message实体类)protobuf compile-customer(生成service的实现基类)

    在这里插入图片描述

    4. 生成桩代码,将其复制到源代码目录下

    • PB 配置的 option java_package="com.xuecheng.fastdfs.target.stub" 指定了生成的桩代码在编译结果 target 目录下的位置(蓝框所在的包),不要和复制的目标位置(红框)处于相同的包
    • 如果相同,即设置 option java_package="com.xuecheng.fastdfs.stub" ,会在后续执行 maven compile 时出现类重复的异常(因为 HelloService.proto 和源代码都想向同一个包下写入同名 class 文件)

    在这里插入图片描述

    5. 实现服务器和客户端逻辑、启动类

    • 服务器的逻辑和启动类
    package com.xuecheng.fastdfs.demo;
    
    import com.xuecheng.fastdfs.stub.HelloServiceGrpc;
    import com.xuecheng.fastdfs.stub.HelloServiceOuterClass;
    import io.grpc.Server;
    import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
    import io.grpc.stub.StreamObserver;
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    
    public class MyHelloServer extends HelloServiceGrpc.HelloServiceImplBase {
    
        @Override
        public void sayHello(HelloServiceOuterClass.HelloRequest request, StreamObserver<HelloServiceOuterClass.HelloResponse> responseObserver) {
            int requestId = request.getRequestId();
            String requestName = request.getName();
            HelloServiceOuterClass.HelloResponse response = HelloServiceOuterClass.HelloResponse.newBuilder()
                    .setResponseId(requestId)
                    .setResponseState(true)
                    .setGreeting("Hello " + requestName + "! This greeting is from server!")
                    .build();
            responseObserver.onNext(response);
            responseObserver.onCompleted();
        }
    
        public static void main(String[] args) {
            // 设置服务端口号
            int port = 8899;
            Server server = NettyServerBuilder.forPort(port)
                    .addService(new MyHelloServer())
                    .build();
            try {
                // 启动服务
                server.start();
                System.out.println("gRPC server started, on port: " + port);
                // 设置60秒后关闭
                server.awaitTermination(60, TimeUnit.SECONDS);
                System.out.println("gRPC server terminated.");
            } catch (IOException e) {
                System.out.println("Start server exception: " + e.getMessage());
                e.printStackTrace();
            } catch (InterruptedException e) {
                System.out.println("Server interrupted: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 客户端的逻辑和启动类
    package com.xuecheng.fastdfs.demo;
    
    import com.xuecheng.fastdfs.stub.HelloServiceGrpc;
    import com.xuecheng.fastdfs.stub.HelloServiceOuterClass;
    import io.grpc.ManagedChannel;
    import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
    import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
    
    public class MyHelloClient {
        public static void main(String[] args) {
            // 指定服务的ip和端口号
            String host = "192.168.0.105";
            int port = 8899;
            // 创建客户端
            ManagedChannel channel = NettyChannelBuilder.forAddress(host, port)
                    .negotiationType(NegotiationType.PLAINTEXT)
                    .build();
            HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);
            // 构造请求
            HelloServiceOuterClass.HelloRequest request = HelloServiceOuterClass.HelloRequest.newBuilder()
                    .setRequestId(1)
                    .setName("zy")
                    .build();
            // 请求服务
            HelloServiceOuterClass.HelloResponse response = blockingStub.sayHello(request);
            // 解析结果
            System.out.println("Greeting: " + response.getGreeting());
        }
    }
    
    • 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
    • 29

    6. 启动服务,查看运行结果

    在这里插入图片描述

  • 相关阅读:
    KY191 矩阵幂(用Java实现)
    Day2:写前端项目(html+css+js)
    flutter 文字拼接
    【数据结构】归并排序
    [Daimayuan] 最喜欢数学了(高数除外)(C++,逻辑推理)
    Java处理Excel:从POI到SPL
    L2-041 插松枝
    15.Xaml StackPanel控件 布局控件
    云原生虚拟网络 tun/tap & veth-pair
    开发者们,Docker到底是什么,能干什么?
  • 原文地址:https://blog.csdn.net/weixin_43785688/article/details/128100768