grpc入门-快速开始

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/inthat/article/details/88763628

grpc入门

gRPC
参考URL: https://blog.csdn.net/weiwangchao_/article/details/82023191
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。

特性

  • 基于HTTP/2
    HTTP/2 提供了连接多路复用、双向流、服务器推送、请求优先级、首部压缩等机制。可以节省带宽、降低TCP链接次数、节省CPU,帮助移动设备延长电池寿命等。gRPC 的协议设计上使用了HTTP2 现有的语义,请求和响应的数据使用HTTP Body 发送,其他的控制信息则用Header 表示。
  • IDL使用ProtoBuf
    gRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。ProtoBuf能够将数据进行序列化,并广泛应用在数据存储、通信协议等方面。压缩和传输效率高,语法简单,表达力强。
  • 多语言支持(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java)
    gRPC支持多种语言,并能够基于语言自动生成客户端和服务端功能库。

gRPC已经应用在Google的云服务和对外提供的API中,其主要应用场景如下:

  • 低延迟、高扩展性、分布式的系统
  • 同云服务器进行通信的移动应用客户端
  • 设计语言独立、高效、精确的新协议
  • 便于各方面扩展的分层设计,如认证、负载均衡、日志记录、监控等

HTTP2.0 特性

  • 多路复用 (Multiplexing)

在 HTTP/1.1 协议中 「浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞」。

HTTP/2 的多路复用(Multiplexing) 则允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。

  • HTTP/2 传输的数据是二进制的。
    相比 HTTP/1.1 的纯文本数据,二进制数据一个显而易见的好处是:更小的传输体积。

  • 首部压缩(Header Compression)
    HTTP是无状态协议。简而言之,这意味着每个请求必须要携带服务器需要的所有细节,而不是让服务器保存住之前请求的元数据。因为http2没有改变这个范式,所以它也需要这样(携带所有细节),因此 HTTP 请求的头部需要包含用于标识身份的数据比如 cookies,而这些数据的量也在随着时间增长。每一个请求的头部都包含这些大量的重复数据,无疑是一种很大的负担。对请求头部进行压缩,将会大大减轻这种负担,尤其对移动端来说,性能提高非常明显。
    。。。

与其他rpc比较

gRPC优缺点

1.语言中立,支持多种语言;
2. 基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;
3. 通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;
4. 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。

缺点:
1)GRPC尚未提供连接池,需要自行实现
2)尚未提供“服务发现”、“负载均衡”机制
3)因为基于HTTP2,绝大部多数HTTP Server、Nginx都尚不支持,即Nginx不能将GRPC请求作为HTTP请求来负载均衡,而是作为普通的TCP请求。(nginx1.9版本已支持)
4) Protobuf二进制可读性差(貌似提供了Text_Fromat功能)
默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)

快速开始

【推荐-可以快速开始】gRPC 官方文档中文版
参考URL: http://doc.oschina.net/grpc?t=58009

使用思路:
使用与thrift相同的设计,通过工具生成响应必要的代码,自己写代码实现其生成代码的接口。
定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型。gRPC 默认使用 protocol buffers 作为接口定义语言,来描述服务接口和有效载荷消息结构。如果有需要的话,可以使用其他替代方案。

gRPC 提供 protocol buffer 编译插件,能够从一个服务定义的 .proto 文件生成客户端和服务端代码。通常 gRPC 用户可以在服务端实现这些API,并从客户端调用它们。

对于开发者而言:

1)需要使用protobuf定义接口,即.proto文件
2)然后使用compile工具生成特定语言的执行代码,比如JAVA、C/C++、Python等。类似于thrift,为了解决跨语言问题。
3)启动一个Server端,server端通过侦听指定的port,来等待Client链接请求,通常使用Netty来构建,GRPC内置了Netty的支持。
4)启动一个或者多个Client端,Client也是基于Netty,Client通过与Server建立TCP长链接,并发送请求;Request与Response均被封装成HTTP2的stream Frame,通过Netty Channel进行交互。

gRPC 允许你定义四类服务方法:

  • 单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
  • 服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
  • 客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。
    ···
    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
    }
    ···
  • 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}

在 Windows 下安装并使用 Protocol Buffers

  1. Protocol 编译器安装
    直接在 GitHub 上下载已经编译好了的 binary。下载地址:https://github.com/protocolbuffers/protobuf/releases 。在这里我们下载protoc-3.7.0-win64.zip

  2. 执行命令
    生成java文件。

protoc.exe -I=E:\code  --java_out=e:/tmp E:\code\lianxi\src\main\java\grpc\proto\route_guide.proto

-I 这个路径需要是.proto文件所在目录
···
protoc.exe -I=E:\ --java_out=e:/tmp E:\code\lianxi\src\main\java\grpc\proto\route_guide.proto
···

** 使用工具时需要注意**

a. 需要指定一个–proto_path路径,否则报错–proto_path (or -I). You must specify a --proto_path which encompasses this file.

b. 需要在.proto文件中指定它的proto语法版本,这样生成时不会出现警告,

Intellij IDEA中使用Protobuf

[推荐]用Maven实现一个protobuf的Java例子
参考URL: https://www.cnblogs.com/kaituorensheng/p/9022591.html

Intellij IDEA中使用Protobuf的正确姿势
参考URL: https://blog.csdn.net/u010674648/article/details/80673208

  1. .proto文件语法高亮显示

    需要安装Protobuf Support插件

  2. 将.proto文件转成Java类
    一般的做法,是执行protoc命令,依次将.proto文件转成Java类:

protoc.exe -I=d:/tmp --java_out=d:/tmp d:/tmp/monitor_data.proto

不过gRPC官方推荐了一种更优雅的使用姿势,可以通过maven轻松搞定
采用IDEA的插件方便执行PB的文件的JAVA编译:

需要对Protobuf Support插件进行配置.,maven的pom文件添加下面的代码

<dependency>
           <groupId>com.google.protobuf</groupId>
           <artifactId>protobuf-java</artifactId>
           <version>3.1.0</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-netty</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-protobuf</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-stub</artifactId>
           <version>${grpc.version}</version>
       </dependency>
<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:${protobuf.version}:exe:${os.detected.classifier}
                    </protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>
                        io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                    </pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

使用maven的编译命令,即可在target中看到根据.proto文件生成的Java类,如下所示:
protobuf-maven-plugin插件,会默认去main下proto目录找文件,否则报错如下:
E:\code\lianxi\src\main\proto does not exist. Review the configuration or consider disabling the plugin.

protobuf-maven-plugin插件:

  1. 默认会去src\main\proto下找.proto文件。
  2. 会根据maven中你的配置下载两个exe文件:protoc-3.7.0-windows-x86_64.exe、protoc-gen-grpc-java-1.19.0-windows-x86_64.exe到target的protoc-plugins目录下。
  3. 生成的代码放到了target目录下generated-sources\protobuf下。

总结: 注意maven引入,一个是引入grpc一个是引入protobuf,两个版本不一样。使用protobuf-maven-plugin插件,默认会去src\main\proto下找。

发现放在IDAE放在target下的源码也会duplicate检测,所以每次根据.proto生成完代码后,把.proto移动到其它地方,防止每次编译都生成源码,需要的时候再生成。- -!

gRPC Java简单示例

通过protobuf 直接从字节码转换成你要的对象

google protobuf 简单实例
参考URL: https://www.cnblogs.com/zhuawang/p/3971839.html

生成的代码放到了target目录下generated-sources\protobuf\java下

定义一个服务 helloworld.proto

[推荐,写的比较细致和全面]gRPC (1):入门及服务端创建和调用原理
参考URL: https://www.cnblogs.com/wxlevel/p/9154246.html

代码转自: gRPC学习记录(二)–Hello World
参考URL:https://www.jianshu.com/p/46d600e5a1b1

  1. proto文件
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

  1. 编写服务端代码
public class HelloWorldServer {

    private int port = 50051;
    private Server server;

    /**
     * 启动服务
     * @throws IOException
     */
    private void start() throws IOException {
        server = ServerBuilder.forPort(port)
                              .addService(new GreeterImpl())
                              .build()
                              .start();

        System.out.println("service start...");

        Runtime.getRuntime().addShutdownHook(new Thread() {

            @Override
            public void run() {
                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                HelloWorldServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    // block 一直到退出程序
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }


    public static void main(String[] args) throws IOException, InterruptedException {
        final HelloWorldServer server = new HelloWorldServer();
        server.start();
        server.blockUntilShutdown();
    }


    // 实现 定义一个实现服务接口的类
    private class GreeterImpl extends GreeterGrpc.GreeterImplBase {

        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            //获取参数
            System.out.println("收到的信息:"+req.getName());

            //这里可以放置具体业务处理代码 start

            //这里可以放置具体业务处理代码 end

            //构造返回
            HelloReply reply = HelloReply.newBuilder().setMessage(("Hello: " + req.getName())).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}
  1. 客户端代码
public class HelloWorldClient {

    private final ManagedChannel channel; //一个gRPC信道
    private final GreeterGrpc.GreeterBlockingStub blockingStub;//阻塞/同步 存根

   //初始化信道和存根
    public HelloWorldClient(String host,int port){
        this(ManagedChannelBuilder.forAddress(host, port)
                                  // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
                                  // needing certificates.
                                  .usePlaintext(true));
    }

    /** Construct client for accessing RouteGuide server using the existing channel. */
    private HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
        channel = channelBuilder.build();
        blockingStub = GreeterGrpc.newBlockingStub(channel);
    }

    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    //客户端方法
    public  void greet(String name){
        HelloRequest request = HelloRequest.newBuilder().setName(name).build();
        HelloReply response;
        try {
            response = blockingStub.sayHello(request);
        } catch (StatusRuntimeException e) {
            System.out.println("RPC调用失败:"+e.getMessage());
            return;
        }
        System.out.println("服务器返回信息:"+response.getMessage());
    }

    public static void main(String[] args) throws InterruptedException {
        HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
        try {
            for(int i=0;i<5;i++){
                client.greet("world:"+i);
            }
        }finally {
            client.shutdown();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/inthat/article/details/88763628
今日推荐