RPC 框架实践之: Google gRPC

2018-05-21 05:45:14 +08:00
 hansonwang99


概述

gRPC 是 Google 开源的通用高性能 RPC 框架,它支持的是使用Protocol Buffers来编写 Service 定义,支持较多语言扩平台并且拥有强大的二进制序列化工具集。与文章《 RPC 框架实践之:Apache Thrift 》 一文中实践的另一种通用 RPC 框架 Thrift 能通过 Generator 自动生成对应语言的 Service 接口类似,gRPC 也能 自动地生成 Server 和 Client 的 Service 存根( Stub ),我们只需要 一个命令 就能快速搭建起 RPC 运行环境。

下面实践一下 gRPC 框架,做的事情就是:Client 端通过远程 RPC 调用 Server 的获取时间的接口,从而将服务器时间获取到本地并显示。

类似于之前对于 RPC 框架: Thrift 的实践步骤,下面一一阐述。


开发 gRPC-API

        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>1.12.0</version>
        </dependency>

这个 grpc-all 包含了很多 grpc 相关的组件:grpc-netty、grpc-protobuf、grpc-stub 等等

这里添加两个 Maven 插件,目的是后面需要用这些插件来执行 Protocol Buffers 命令,从而自动生成相关的 Stub 代码:

os-maven-plugin:生成平台无关的属性 protobuf-maven-plugin:执行 Protocol Buffers 命令并生成 Stub 代码库

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.4.1.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <pluginId>grpc-java</pluginId>
                    <protocArtifact>com.google.protobuf:protoc:3.0.2:exe:${os.detected.classifier}</protocArtifact>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.2.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

这里.proto 文件的作用和写法就和我的前一篇文章《 RPC 框架实践之:Apache Thrift 》 一文中 Thrift 所要求的.thrift 文件编写一样,是有其自己的语法要求的!

syntax = "proto3 ”;   // 语法版本

// stub 选项
option java_package = "com.hansonwang99.grpc.api ”;
option java_outer_classname = “ RPCDateServiceApi ”;
option java_multiple_files = true;

// 定义包名,类似于我的文章《 RPC 框架实践之:Apache Thrift 》中的 Thrift 的 namespace
package com.hansonwang99.grpc.api;

// 服务接口定义,服务端和客户端都要遵守该接口进行通信
service RPCDateService {
  rpc getDate (RPCDateRequest) returns (RPCDateResponse) {}
}

// 定义消息(请求)
message RPCDateRequest {
  string userName = 1;
}

// 定义消息(响应)
message RPCDateResponse {
  string serverDate = 1;
}

mvn 编译完成以后,在target/generated-sources目录下就能看到根据上面.proto文件自动转化生成的Java 代码 Stub

代码生成结果如下所示

好了,既然 gRPC-API 已经有了,下面可以分别编写服务端和客户端


开发 gRPC 服务端

        <dependency>
            <groupId>com.hansonwang99</groupId>
            <artifactId>GrpcAPI</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

接下来一步比较关键

public class RPCDateServiceImpl extends RPCDateServiceGrpc.RPCDateServiceImplBase{
    @Override
    public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) {
        RPCDateResponse rpcDateResponse = null;
        Date now=new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("今天是"+"yyyy 年 MM 月 dd 日 E kk 点 mm 分”);
        String nowTime = simpleDateFormat.format( now );
        try {
            rpcDateResponse = RPCDateResponse
                    .newBuilder()
                    .setServerDate( "Welcome " + request.getUserName()  + ", " + nowTime )
                    .build();
        } catch (Exception e) {
            responseObserver.onError(e);
        } finally {
            responseObserver.onNext( rpcDateResponse );
        }
        responseObserver.onCompleted();
    }
}

我想此处重写的getDate()方法并不陌生吧,这正是上文 .proto 文件中定义的 Service 接口。 此处逻辑比较简单:获取当前时间,并且将其与请求RPCDateRequest中提取出的userName字段进行拼接,然后返回给调用端!形成一个闭环

public class GRPCServer {
    private static final int port = 9999;
    public static void main( String[] args ) throws Exception {
        Server server = ServerBuilder.
                forPort(port)
                .addService( new RPCDateServiceImpl() )
                .build().start();
        System.out.println( "grpc 服务端启动成功, 端口=" + port );
        server.awaitTermination();
    }
}

端口自定义的 9999,也就是在该端口监听。现在可以立即运行 GRPCServer,来启动服务端


开发 gRPC 客户端

        <dependency>
            <groupId>com.hansonwang99</groupId>
            <artifactId>GrpcAPI</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
public class GRPCClient {
    private static final String host = “ localhost ”;
    private static final int serverPort = 9999;

    public static void main( String[] args ) throws Exception {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress( host, serverPort ).usePlaintext().build();
        try {
            RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService = RPCDateServiceGrpc.newBlockingStub( managedChannel );
            RPCDateRequest  rpcDateRequest = RPCDateRequest
                    .newBuilder()
                    .setUserName(“ hansonwang99 ”)
                    .build();
            RPCDateResponse rpcDateResponse = rpcDateService.getDate( rpcDateRequest );
            System.out.println( rpcDateResponse.getServerDate() );
        } finally {
            managedChannel.shutdown();
        }
    }
}

现在立即启动 GRPCClient !


C-S 通信实验

还记得我们的目标吗?

RPC 完成的即是远程的过程调用,在本实验中那就是客户端可以远程调用服务端的 getDate()过程,并将结果取到客户端来显示!


后记

本文实验代码在此 → 需要自取

作者更多的原创文章:在 V2EX

作者其他一些 RPC 框架的实践如下:

扩展阅读:


3637 次点击
所在节点    程序员
7 条回复
virusdefender
2018-05-21 07:07:54 +08:00
代码中中英文的引号都能错,真的测试运行过么?
hansonwang99
2018-05-21 07:24:24 +08:00
@virusdefender 哪地方错了?
hansonwang99
2018-05-21 07:25:38 +08:00
@virusdefender proto 里可能是粘贴的原因,源码去我码云上取
6diyipi
2018-05-21 09:09:00 +08:00
这桌面秀的。
hansonwang99
2018-05-21 09:11:21 +08:00
@6diyipi 小弟不敢
cloverstd
2018-05-21 09:46:09 +08:00
显示器啥型号,底下的底座是自带的吗
SouthCityCowBoy
2018-05-21 20:51:50 +08:00
大佬

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/456393

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX