gRpc入门

279 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、概述

官网:grpc.io/docs/

gRpc 是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。

gRPC基于HTTP/2标准设计,带来诸如双向流、流控、头部压缩、单TCP连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

gRpc默认使用的是Protocol Buffers,是Google开源的一种接口描述语言(IDL

目前官方支持的语言以及平台如下:

LanguageOSCompilers / SDK
C/C++Linux, MacGCC 6.3+, Clang 6+
C/C++Windows 10+Visual Studio 2017+
C#Linux, Mac.NET Core, Mono 4+
C#Windows 10+.NET Core, NET 4.5+
DartWindows, Linux, MacDart 2.12+
GoWindows, Linux, MacGo 1.13+
JavaWindows, Linux, MacJava 8+ (KitKat+ for Android)
KotlinWindows, Linux, MacKotlin 1.3+
Node.jsWindows, Linux, MacNode v8+
Objective-CmacOS 10.10+, iOS 9.0+Xcode 12+
PHPLinux, MacPHP 7.0+
PythonWindows, Linux, MacPython 3.5+
RubyWindows, Linux, MacRuby 2.3+

二、入门使用(grpc-java)

gRpc的使用有三个基本步骤:

  1. 编写IDL文件(.proto)
  2. 将编写的.proto描述文件生成为Java代码
  3. 编写调用逻辑

2.1 编写 IDL 文件

  1. 我们新建一个maven工程,命名为yiwenup-sample-grpc-protocol,这个模块用于根据.proto文件生成对应Java代码。后续无论是消费方还是服务方都依赖此协议:消费方根据接口本地产生远程调用代理;服务方根据接口编写具体的服务逻辑。 修改pom.xml文件,为当前模块引入grpc-all依赖以及对应的代码生成插件

    <?xml version="1.0" encoding="UTF-8"?>
    <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">
        <parent>
            <artifactId>yiwenup-sample-grpc</artifactId>
            <groupId>cloud.yiwenup.sample</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    ​
        <artifactId>yiwenup-sample-grpc-generator</artifactId>
    ​
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-all</artifactId>
                <version>1.48.1</version>
            </dependency>
        </dependencies>
    ​
        <build>
            <extensions>
                <extension>
                    <groupId>kr.motd.maven</groupId>
                    <artifactId>os-maven-plugin</artifactId>
                    <version>1.7.0</version>
                </extension>
            </extensions>
    ​
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.10.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
    ​
                <plugin>
                    <groupId>org.xolstice.maven.plugins</groupId>
                    <artifactId>protobuf-maven-plugin</artifactId>
                    <version>0.6.1</version>
                    <configuration>
                        <pluginId>grpc-java</pluginId>
                        <protocArtifact>com.google.protobuf:protoc:3.21.5:exe:${os.detected.classifier}</protocArtifact>
                        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.49.0:exe:${os.detected.classifier}</pluginArtifact>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                                <goal>compile-custom</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build></project>
    
  2. main目录下新建proto目录,用于存放.proto文件,此目录下的文件将会被转换为.java源文件

  3. .proto文件内容参考如下,具体语法需要根据官方文档指导:

    syntax = "proto3"; // 协议版本
    ​
    // 选项配置
    option java_package = "cloud.yiwenup.sample.grpc.gen"; // 生成文件的包路径
    option java_outer_classname = "HelloWorldProto"; // 名称
    option java_multiple_files = true; // 生成多分java文件,便于管理
    ​
    // 定义请求体
    message SayHelloRequest {
      string serial = 1;
    }
    ​
    // 定义相应内容
    message SayHelloResponse {
      string code = 1;
      string msg = 2;
      string data = 3;
    }
    ​
    // 服务接口.定义请求参数和相应结果
    service SayHelloService {
      rpc sayHello (SayHelloRequest) returns (SayHelloResponse) {
      }
    }
    
  4. 运行命令protobuf:compile可以生成gRpc相关的Bean;运行命令protobuf:compile-custom可以生成gRpc的服务模块,对应生成的代码,在target/generated-sources/protobuf目录下

    image-20220829151455821.png

2.2 编写服务端(提供方)

服务端根据.proto文件在协议层生成的代码将具体逻辑填充

  1. 首先需要编写服务端的启动部分,保证服务端可以启动并且阻塞等待shutdown指令才可以关闭JVM停止服务。为此,新建一个maven工程yiwenup-sample-grpc-server,添加grpc-all依赖,服务端参考代码如下:

    public class GrpcServer {
        public static void main(String[] args) {
            try {
                // 启动服务端,直到接受到termination指令
                Server server = ServerBuilder
                        .forPort(7777)
                        // 添加服务实现逻辑
                        .addService(new SayHelloServiceImpl())
                        .build()
                        .start();
    ​
                System.out.println("[服务端] 启动成功...");
    ​
                // 当接收到shutdown信号的时候,停止服务端
                Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                    System.out.println("[服务端] 正在停止JVM...");
                    server.shutdown();
                    System.out.println("[服务端] 服务已关闭...");
                }));
    ​
    ​
                server.awaitTermination();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    
  2. 在服务启动代码中,需要添加一系列基于协议层实现的具体逻辑,譬如上述代码中的SayHelloServiceImpl便是如此。因此本maven工程还需要添加协议层依赖yiwenup-sample-grpc-protocol,实现逻辑参考代码如下:

    public class SayHelloServiceImpl extends SayHelloServiceGrpc.SayHelloServiceImplBase {
    ​
        private AtomicInteger atomicInteger = new AtomicInteger();
    ​
        @Override
        public void sayHello(SayHelloRequest request, StreamObserver<SayHelloResponse> responseObserver) {
            String serial = request.getSerial();
    ​
            try {
                responseObserver.onNext(SayHelloResponse.newBuilder()
                        .setCode("200")
                        .setMsg("成功")
                        .setData(serial + " <> " + atomicInteger.getAndIncrement()).build());
            } catch (Exception e) {
                responseObserver.onError(e);
            } finally {
                responseObserver.onCompleted();
            }
        }
    }
    
  3. 至此,服务端就准备完毕了,启动服务端,可以看到控制台的打印信息,以及在关闭服务端的时候也有信息打印。

    image-20220829153628181.png

2.3 编写客户端(消费方)

客户端根据.proto文件在协议层生成的代码直接本地调用,底层通过代理实现RPC

  1. 新建一个maven工程,添加两个依赖:grpc-all以及yiwenup-sample-grpc-protocol,本示例在客户端启动完成之后立即远程调用服务,之后关闭客户端。

    public class GrpcClient {
        public static void main(String[] args) {
            // 建立和服务端的一个 channel
            ManagedChannel channel = ManagedChannelBuilder
                    .forAddress("localhost", 7777)
                    .usePlaintext()
                    .build();
    ​
            try {
                // 获取接口的代理对象
                SayHelloServiceGrpc.SayHelloServiceBlockingStub sayHelloServiceBlockingStub = SayHelloServiceGrpc.newBlockingStub(channel);
    ​
                // 本地调用
                for (int i = 0; i < 100000; i++) {
                    SayHelloResponse sayHelloResponse = sayHelloServiceBlockingStub.sayHello(SayHelloRequest.newBuilder().setSerial(String.valueOf(i)).build());
                    System.out.println("[客户端 " + i + "] " + sayHelloResponse.getCode() + " => " + sayHelloResponse.getMsg() + " => " + sayHelloResponse.getData());
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                if (channel != null) {
                    channel.shutdown();
                }
            }
        }
    }
    
  2. 在服务端启动着的前提下,运行客户端程序。

    image-20220829153733905.png