V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hansonwang99
V2EX  ›  程序员

RPC 框架实践之: Google gRPC

  •  
  •   hansonwang99 ·
    hansonwang99 · 2018-05-21 05:45:14 +08:00 · 3621 次点击
    这是一个创建于 2384 天前的主题,其中的信息可能已经有所发展或是发生改变。

    My Desktop


    概述

    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

    • 首先创建一个基于 Maven 的项目: GrpcAPI

    • pom 中加入 grpc 相关的依赖

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

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

    与 grpc 相关的包

    • pom 中加入 grpc 相关的 build 插件

    这里添加两个 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 的服务定义文件

    这里.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 compile命令来自动生成代码 Stub

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

    mvn 编译

    代码生成结果如下所示

    代码生成结果

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


    开发 gRPC 服务端

    • 创建基于 Maven 的项目:Server

    • pom 中添加 GrpcAPI 依赖

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

    接下来一步比较关键

    • 实现 gRPC 服务接口
    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字段进行拼接,然后返回给调用端!形成一个闭环

    • 创建 gRPC 服务端启动类
    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 客户端

    • 创建基于 Maven 的项目:Client

    • pom 中依然需要添加 GrpcAPI 依赖

            <dependency>
                <groupId>com.hansonwang99</groupId>
                <artifactId>GrpcAPI</artifactId>
                <version>1.0-SNAPSHOT</version>
                <scope>compile</scope>
            </dependency>
    
    • 创建 gRPC 客户端启动类
    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()过程,并将结果取到客户端来显示!

    RPC 调用成功


    后记

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

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

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

    扩展阅读:


    7 条回复    2018-05-21 20:51:50 +08:00
    virusdefender
        1
    virusdefender  
       2018-05-21 07:07:54 +08:00
    代码中中英文的引号都能错,真的测试运行过么?
    hansonwang99
        2
    hansonwang99  
    OP
       2018-05-21 07:24:24 +08:00 via iPhone
    @virusdefender 哪地方错了?
    hansonwang99
        3
    hansonwang99  
    OP
       2018-05-21 07:25:38 +08:00 via iPhone
    @virusdefender proto 里可能是粘贴的原因,源码去我码云上取
    6diyipi
        4
    6diyipi  
       2018-05-21 09:09:00 +08:00
    这桌面秀的。
    hansonwang99
        5
    hansonwang99  
    OP
       2018-05-21 09:11:21 +08:00 via iPhone
    @6diyipi 小弟不敢
    cloverstd
        6
    cloverstd  
       2018-05-21 09:46:09 +08:00
    显示器啥型号,底下的底座是自带的吗
    SouthCityCowBoy
        7
    SouthCityCowBoy  
       2018-05-21 20:51:50 +08:00
    大佬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4903 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 01:10 · PVG 09:10 · LAX 17:10 · JFK 20:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.