美文网首页go学习netty学习
google grpc 快速入门

google grpc 快速入门

作者: 二月_春风 | 来源:发表于2017-10-21 09:17 被阅读178次

    为什么去使用gRPC

    使用gRPC,我们可以在.proto文件中定义我们的服务,并以任何gRPC支持的语言实现客户端和服务器。 使用gRPC还具有protocol buffer的所有优点,包括高效的序列化,简单的IDL和简单的接口更新。

    自己认为rpc框架的优点主要在于,性能好(使用高效的序列化框架,tcp传输协议),跨语言(服务端和客户端可以使用不同的语言开发)。

    引入依赖

    使用maven构建项目,maven依赖:

    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-netty</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-protobuf</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-stub</artifactId>
      <version>1.4.0</version>
    </dependency>
    

    使用Gradle构建项目,Gradle依赖:

    compile 'io.grpc:grpc-netty:1.4.0'
    compile 'io.grpc:grpc-protobuf:1.4.0'
    compile 'io.grpc:grpc-stub:1.4.0'
    

    Android开发,使用grpc-okhttp代替grpc-netty,使用grpc-protobuf-lite,或者grpc-protobuf-nano代替grpc-protobuf

    compile 'io.grpc:grpc-okhttp:1.4.0'
    compile 'io.grpc:grpc-protobuf-lite:1.4.0'
    compile 'io.grpc:grpc-stub:1.4.0'
    

    使用插件根据指定的.proto文件生成代码,要将.proto文件放在src/main/protosrc/test/proto目录下,这样插件就可以找到.proto文件并且生成代码。

    使用maven插件如下:

    <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>
            <protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
            <pluginId>grpc-java</pluginId>
            <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}</pluginArtifact>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
    

    或者使用gradle插件:

    apply plugin: 'java'
    apply plugin: 'com.google.protobuf'
    
    buildscript {
      repositories {
        mavenCentral()
      }
      dependencies {
        // ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier
        // gradle versions
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
      }
    }
    
    protobuf {
      protoc {
        artifact = "com.google.protobuf:protoc:3.2.0"
      }
      plugins {
        grpc {
          artifact = 'io.grpc:protoc-gen-grpc-java:1.4.0'
        }
      }
      generateProtoTasks {
        all()*.plugins {
          grpc {}
        }
      }
    }
    

    build.gradle中加入相应的插件之后,idea出现gradle插件图形显示

    gradle插件

    如果要将.proto文件放在其他路径,可以加入下面的指令:

    sourceSets {
      main {
        proto {
          // In addition to the default 'src/main/proto'
          srcDir 'src/main/protobuf'
          srcDir 'src/main/protocolbuffers'
          // In addition to the default '**/*.proto' (use with caution).
          // Using an extension other than 'proto' is NOT recommended,
          // because when proto files are published along with class files, we can
          // only tell the type of a file from its extension.
          include '**/*.protodevel'
        }
        java {
          ...
        }
      }
      test {
        proto {
          // In addition to the default 'src/test/proto'
          srcDir 'src/test/protocolbuffers'
        }
      }
    }
    

    相应的gradle插件的配置说明

    编写.proto文件

    src/main/proto目录下编写Student.proto文件

    syntax = "proto3";
    
    package com.zhihao.miao.proto;
    
    option java_package = "com.zhihao.miao.proto";
    option java_outer_classname ="StudentProto";
    //将属性生成多个文件,便于代码管理
    option java_multiple_files = true;
    
    //定义rpc方法
    service StudentService{
    
        //一元RPC
        rpc GetRealNameByUsername(MyRequest) returns (MyResponse){}
    }
    
    message MyRequest{
        string username = 1;
    }
    
    message MyResponse{
        string realname = 2;
    }
    

    使用gradle插件编译

    gradle generateProto 
    
    生成的代码

    将生成的代码copy到main/java的制定的包下,生成的类有以下几个,MyRequestMyRequestOrBuilderMyResponseMyResponseOrBuilderStudentProto是GRPC消息传输的Message,后面的一个StudentServiceGrpc类是远程调用的接口,服务端要重写.proto中的方法要实现其内部类StudentServiceImplBase

    编写服务器代码

    服务端业务接口实现

    package com.zhihao.miao.gprc;
    
    import com.zhihao.miao.proto.*;
    import io.grpc.stub.StreamObserver;
    
    public class StudentServiceImpl extends StudentServiceGrpc.StudentServiceImplBase{
    
    
        //第一个对象是请求参数对象,第二个参数是向客户端返回结果的对象,方法定义不返回值,而是通过responseObserver去返回值
        @Override
        public void getRealNameByUsername(MyRequest request, StreamObserver<MyResponse> responseObserver) {
            System.out.println("接收到客户端信息: "+request.getUsername());
    
            //onNext,onError,onCompletedB表示方法都是回调方法
            responseObserver.onNext(MyResponse.newBuilder().setRealname("王超").build());
    
            //onCompletedB表示方法方法执行完毕
            responseObserver.onCompleted();
    
    
        }
    }
    

    编写服务器代码
    编写服务器端代码,参考快速入门的服务端代码,HelloWorldServer.java

    cd /Users/naeshihiroshi/study/studySummarize/netty/grpc-java/examples
    cd src/main/java/io/grpc/examples/helloworld
    

    参考

    服务器端代码:

    import io.grpc.Server;
    import io.grpc.ServerBuilder;
    
    import java.io.IOException;
    
    public class GrpcServer {
    
        private Server server;
    
        //编写服务启动方法
        private void start() throws IOException{
            //增加实际业务代码的实列,如果使用spring的话可以直接将StudentServiceImpl对象注入进来
            this.server = ServerBuilder.forPort(8899).addService(new StudentServiceImpl()).build().start();
    
            System.out.println("server start!");
    
            //jvm回调钩子的作用,Runtime.getRuntime()可以获得java一些运行期间的一些信息。
            //不管程序是正常关闭还是异常终端,在jvm关闭的时候做一些清理工作
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                System.out.println("关闭jvm");
                GrpcServer.this.stop();
                System.out.println("服务关闭");
            }));
    
            System.out.println("执行到这里");
        }
    
        //停止服务器
        private void stop(){
            if(null != this.server){
                this.server.shutdown();
            }
        }
    
        //服务器阻塞
        private void blockUntilShutdown() throws InterruptedException {
            if(null != this.server){
                this.server.awaitTermination();
                //this.server.awaitTermination(3000, TimeUnit.MILLISECONDS);
            }
        }
    
        public static void main(String[] args) throws IOException,InterruptedException{
            GrpcServer server = new GrpcServer();
    
            server.start();;
            server.blockUntilShutdown();
        }
    }
    

    客户端代码:

    import com.zhihao.miao.proto.*;
    import io.grpc.ManagedChannel;
    import io.grpc.ManagedChannelBuilder;
    import io.grpc.stub.StreamObserver;
    
    import java.time.LocalDateTime;
    import java.util.Iterator;
    
    public class GrpcClient {
    
        public static void main(String[] args) throws Exception{
            ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",8899)
                    //使用TLS来连接服务端
                    .usePlaintext(true).build();
    
            //定义一个stub,用于实际和服务端连接的对象blockingStub
            StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc.newBlockingStub(managedChannel);
    
            MyResponse response =blockingStub.getRealNameByUsername(MyRequest.newBuilder()
                    .setUsername("chao.wang").build());
    
            System.out.println(response.getRealname());
        }
    }
    

    运行客户端与服务器端,服务器端控制台打印:

    server start!
    执行到这里
    接收到客户端信息: chao.wang
    

    客户端控制台打印:

    王超
    Disconnected from the target VM, address: '127.0.0.1:63197', transport: 'socket'
    
    Process finished with exit code 0
    

    关闭服务器,服务器控制台上打印,执行jvm回调钩子。:

    关闭jvm
    服务关闭
    

    参考资料

    官网java指南
    github地址

    相关文章

      网友评论

        本文标题:google grpc 快速入门

        本文链接:https://www.haomeiwen.com/subject/kmlpcxtx.html