grpc入门-快速开始


文章目录

  • grpc入门
    • 特性
    • HTTP2.0 特性
    • 与其他rpc比较
    • gRPC优缺点
    • 快速开始
      • 在 Windows 下安装并使用 Protocol Buffers
      • Intellij IDEA中使用Protobuf
      • gRPC Java简单示例
        • 通过protobuf 直接从字节码转换成你要的对象
        • 定义一个服务 helloworld.proto

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文件添加下面的代码
com.google.protobuf protobuf-java 3.1.0 io.grpc grpc-netty ${grpc.version} io.grpc grpc-protobuf ${grpc.version} io.grpc grpc-stub ${grpc.version}

kr.motd.maven os-maven-plugin 1.4.1.Final org.xolstice.maven.plugins protobuf-maven-plugin 0.5.0 com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} grpc-javaio.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} compile compile-custom

使用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入门-快速开始】代码转自: 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 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); }//客户端方法 publicvoid 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(); } } }

    推荐阅读