gRPC-谷歌高性能RPC框架

概要

RPC远程过程访问

gPRC具有标准化、可通用和跨平台的特点

进程间通信,通常是指一个应用实例调用另外一个应用实例所提供的服务,而这两个应用都运行在自己独立的进程中,通过网络彼此交换信息的过程。

契约优先

优点

高效进程间通信

简单且定义良好的服务接口和模式,编译阶段发现问题

属于强类型调用

支持多语言

支持双工通信

缺点

gRPC不适合面向外部的服务

服务定义变更,会出现复杂的开发流程

gRPC生态系统相对较小

Protobuf

Google提供一个具有高效的协议数据交换格式工具库(类似Json)

Protobuf 时间效率和空间效率是JSON的3到5倍

客户端与服务端通信使用 HTTP2 协议

安装

1.安装Protobuf编译器

Protobuf运行环境需安装Protobuf编译器

下载地址:

windows 配置环境变量

  1. 新建系统变量 PROTOBUF_HOME 变量值 是 protobuf 编译器解压目录。
  2. 编辑Path环境变量,添加 %PROTOBUF_HOME%\bin

验证是否配置成功

打开CMD,输入protoc,输出一些列参数说明,则说明安装好了

2.安装IDEA Protobuf插件

打开IDEA,进入插件市场,搜索protobuf,如下图所示,安装即可

gRPC-谷歌高性能RPC框架_第1张图片

使用

服务端开发流程

  1. Maven增加gRPC依赖坐标系

  2. 引入protobuf-maven-plugin插件

  3. 编写proto服务定义文件

  4. 实现服务端业务逻辑

  5. 开发服务端启动器

maven 依赖

<dependency>
    <groupId>io.grpcgroupId>
    <artifactId>grpc-netty-shadedartifactId>
    <version>1.42.0version>
dependency>

<dependency>
    <groupId>io.grpcgroupId>
    <artifactId>grpc-protobufartifactId>
    <version>1.42.0version>
dependency>

<dependency>
    <groupId>io.grpcgroupId>
    <artifactId>grpc-stubartifactId>
    <version>1.42.0version>
dependency>

<dependency> 
    <groupId>org.apache.tomcatgroupId>
    <artifactId>annotations-apiartifactId>
    <version>6.0.53version>
    <scope>providedscope>
dependency>


<plugin>
    <groupId>org.xolstice.maven.pluginsgroupId>
    <artifactId>protobuf-maven-pluginartifactId>
    <version>0.6.1version>
    <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}protocArtifact>
        <pluginId>grpc-javapluginId>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.42.0:exe:${os.detected.classifier}pluginArtifact>
    configuration>
    <executions>
        <execution>
            <goals>
                <goal>compilegoal>
                <goal>compile-customgoal>
            goals>
        execution>
    executions>
plugin>

引入maven 完成后,在代码的main目录下,创建proto文件夹(约定)。

maven 插件会自动扫描,proto 文件夹下的 proto 后缀文件,进行代码生成。

IDEA插件就是对 proto 文件提供语法检查等编辑器支持。

编辑好proto文件后,点击IDEA右侧maven -> 找到需生成的服务 -> Plugins -> protobuf -> protobuf:complie -> protobuf:complie-custom 生成RPC代码

生成的代码在,target 目录对应配置的包下,需要将生成的java代码,移动到src下

服务端代码demo

// 自己建一个 service类,继承 Grpc 下的 实现类
public class TestService extends TestServiceGrpc.TestServiceImplBase {

    @Override
    public void hello(TestProto.StringRequest request, StreamObserver<TestProto.StringResponse> responseObserver) {
        String name = request.getName();
        TestProto.StringResponse response = TestProto.StringResponse.newBuilder().setResult("你好," + name).build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }

}

客户端开发流程

客户端与服务端依赖的jar是相同的

也是main目录下创建 proto 文件夹,然后将proto文件复制到客户端。

然后用maven生成代码,跟服务端一样,如果是同样的语言进行开发,直接从服务端复制过去也可以。

客户端发送请求代码

// 类似 jdbc 一样的套路代码
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, serverPort).usePlaintext()
        .build();
try {
    TestServiceGrpc.TestServiceBlockingStub blockingStub = TestServiceGrpc.newBlockingStub(channel);
    Test.StringResponse response = blockingStub.hello(Test.StringRequest.newBuilder().setName("小黑").build());
    System.out.println(response.getResult());
}finally {
    channel.shutdown();
}

Protobuf 文件详解

proto 文件

// 使用proto3语法
syntax = "proto3";
// 生成多个类,设置为否
option java_multiple_files = false;
// 生成java类所在的包
option java_package = "com.songjh.demo";
// 生成外层类类名
option java_outer_classname = "Test";
// 逻辑包名,实际不会加到类上,就是给 proto 文件的逻辑包名
package songjh;
// service服务,用于描述要生成的API接口,类似于接口类
service TestService {
    //rpc 方法名( 参数类型)  returns (返回类型)
    rpc list(TestRequest) returns (TestResponse) {}
    rpc hello(StringRequest) returns (StringResponse){}
}
/*
    消息,类似Java的"实体类"
    名字,对应于生成代码后的类名
    每一个消息都对应生成一个类,根据java_multiple_files设置不同文件数量也不同
 */
message NewsRequest {
    /*
        字段:类型 名称 = 索引值(id)
        每个字段都要定义唯一的索引值,这些数字是用来在消息的二进制格式中识别各个字段的。
        一旦开始使用就不能够再改变,最小的标识号可以从1开始,最大到2^29 - 1, 或 536870911。
        不可以使用 [19000-19999]的标识号, Protobuf协议实现中对这些进行了预留。
        切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。可以1 ,10,,20,  30 这样初始化。
    */
    string date = 1;
}
message TestResponse {
    //repeated说明是一个集合(数组),数组每一个元素都是News对象
    repeated News news = 1; //List getNewsList();
}
//News新闻实体对象
message News{
    int32 id = 1;              // int id = 0;
    string title = 2;          // String title = "";
    int64 createTime = 3; // long createTime = 0l;
}

message StringRequest{
    string name = 1;
}

message StringResponse{
    string result = 1;
}

生成代码

父类 类名 说明
DemoMess 消息类的大类
DemoMess DemoOrBuilder 消息类与构造器接口
DemoMess DemoRequestOrBuilder 消息类与构造器接口
DemoMess DemoResponseOrBuilder 消息类与构造器接口
DemoMess Demo 具体的消息类实现
DemoMess DemoRequest 具体的消息类实现
DemoMess DemoResponse 具体的消息类实现
DemoServiceGrpc gRPC通信类的集合
DemoServiceGrpc XXXDescriptorSupplier gRPC通信用的描述符文件 (元数据)
DemoServiceGrpc DemoServiceBlockingStub 同步通信客户端存根,产生阻塞,支持一元与服务端流数据通信
DemoServiceGrpc DemoServiceFutureStub 异步通信客户端存根,基于Guava Future实现不支持流式处理
DemoServiceGrpc DemoServiceStub 异步通信客户端存根,支持双向流式传输
DemoServiceGrpc NewsServicelmplBase 服务器端的骨架类,继承这个类实现业务逻辑

通信模式

一元RPC通信模式

客户端向服务器发送单个请求并获得单个响应

客户端使用 newBlockingStub 通信即可。

DemoServiceGrpc.newBlockingStub(channel);

服务端流式RPC通信模式

从客户端发起1次请求,会产生从服务器的多次响应

首先proto文件,service 定义中 rpc 方法的 返回值类型 前增加 stream ,如下

service SmsService {
    rpc test(TestRequest) returns (stream TestResponse) {}
}

一般来说客户端传过来的是个 List

服务端代码如下

// 循环遍历客户端传过来的 List
for(String xxx : xxxList) {
    Demo.XXXResponse response = Demo.XXXResponse.newBuilder().setXX().build();
    // 向客户端返回一次响应
    responseObserver.onNext();
}
// 服务器端都处理完了,告诉客户端服务端都完事了
responseObserver.onCompleted();

客户端还是用newBlockingStub。

流式的返回值是 Iterator 。

客户端流式RPC通信模式

从客户端发起多次请求,产生从服务器的1次响应

例如导入多条数据,最后返回一次结果。

客户端流式RPC,服务端必须使用异步处理

首先proto文件客户端需要增加stream,如下

service TestService {
    rpc createString(stream TestRequest) returns (TestResponse) {}
}

服务端代码,必须用异步处理

public StreamObserver<TestProto.TestRequest> test(StreamObserver<TestProto.TestResponse> responseObserver) {
    /**
     * 异步通信基于responseObserver事件回调
     */
    return new StreamObserver<TestProto.TestRequest>() {
        @Override
        //接收到客户端发来的单个请求时,触发onNext
        public void onNext(TestProto.TestRequest request) {
            // 处理业务请求
        }

        @Override
        public void onError(Throwable throwable) {
            throwable.printStackTrace();
        }

        //客户端传输完毕时,完成消息统计
        @Override
        public void onCompleted() {
            // 服务端返回一次消息结果
            responseObserver.onNext()
            responseObserver.onCompleted();
        }
    };

}

双向流式RPC通信模式

双向即是 客户端和服务端都是多对多,其实就是上述 客户端和服务端流式RPC通信代码结合。

proto文件客户端和服务端都需要增加stream

service SmsService {
    rpc test(stream testRequest) returns (stream testResponse) {}
}

SpringBoot 集成 gRPC

maven依赖,插件依赖跟之前一样没有变化

<dependency>
    <groupId>net.devhgroupId>
    <artifactId>grpc-server-spring-boot-starterartifactId>
    <version>2.13.0.RELEASEversion>
dependency>

<plugin>
    <groupId>org.xolstice.maven.pluginsgroupId>
    <artifactId>protobuf-maven-pluginartifactId>
    <version>0.6.1version>
    <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}protocArtifact>
        <pluginId>grpc-javapluginId>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.42.0:exe:${os.detected.classifier}pluginArtifact>
    configuration>
    <executions>
        <execution>
            <goals>
                <goal>compilegoal>
                <goal>compile-customgoal>
            goals>
        execution>
    executions>
plugin>

依然是在src目录下创建 proto 文件夹,然后在该文件夹下创建proto文件。

在创建 service 类上,增加 @GrpcService 注解。

yml配置

# gRPC 服务端设置,服务端开启的端口
grpc:
    server:
        port: 9090

# gRPC 客户端配置
grpc:
    client:
        grpc-server: #自定义一个gRPC服务名
            address: 'static://127.0.0.1:9090'  # static 表示静态ip直接访问
            negotiationType: plaintext  # 传输类型设置成文本

客户端使用时,可以直接依赖注入,通过@GrpcClient 注解。

@GrpcClient("grpc-server")
private DemoServiceGrpc.DemoServiceBlockingStub newsStub;

Eureka 集成 gRPC

eureka server 没变化,也不用额外引入maven 或单独配置。

gRPC 的 server,也作为 eureka client ,注册到eureka 上。

gRPC 的客户端和服务端 需引入maven 配置如下

<dependency>
    <groupId>net.devhgroupId>
    <artifactId>grpc-server-spring-boot-starterartifactId>
    <version>2.13.0.RELEASEversion>
dependency>

<dependency>
    <groupId>com.google.guavagroupId>
    <artifactId>guavaartifactId>
    <version>31.0-jreversion>
dependency>

server 端代码编写,跟springboot 编写一样,就是配置不一样

grpc:
   server:
       port: 0  # 设置为0 表示使用给一个未被占用的随机端口,会被上传eureka,所以不用配

客户端配置

grpc:
    client:
        g-server-service:  # 服务端名称,spring.application.name
            negotiationType: plaintext  #代表文本传输

客户端代码

// 这个名字要保持一致,调用哪个服务的方法
@GrpcClient("g-server-service")
private DemoServiceGrpc.DemoServiceBlockingStub newsStub;

你可能感兴趣的:(rpc,java,网络协议)