go学习netty学习

google grpc 快速入门

2017-10-21  本文已影响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地址

上一篇下一篇

猜你喜欢

热点阅读