gRPC初探

概览

在gRPC的官方文档中这样描述grpc的特点:

第一点:强大的接口描述语言(Powerful IDL)

Protocol Buffers是一个强大的二进制序列化工具集和语言,你可以使用Protocol Buffers定义你的接口。

第二点:支持十种语言的类库

为各种语言编写的服务自动生成相应语言的客户端和服务端存根(也就是接口)

第三点:基于HTTP2协议

基于HTTP2标准设计,带了许多诸如双向流、流程控制、头部压缩、单TCP连接上的多路复用请求等特性。

这些特性使得其在移动设备上表现更好,更省电和节省空间占用,同时加速了运行在cloud上的服务和web应用。

那么什么是gRPC呢?

gRPC客户端可以直接调用另外一台机器上服务器应用程序的方法,就像调用本地对象一样,这使得创建分布式的应用和服务更加方便。

和众多RPC系统一样,gPRC的基本思想是:定义一个接口服务,指定服务中可以通过参数和返回值类型进行远程调用的方法。在服务端,实现这个接口,运行一个gRPC服务器来处理客户端调用请求。在客户端,保留了一个存根(指的是某种语言编写的客户端)提供了和服务端一样的方法。

进行通信的gRPC客户端和服务端可以运行在不同的环境上—从Google的内置服务到个人桌面应用—可以是任何gRPC支持的语言。所以,举例来说,你可以简单创建一个使用Java编写的gRPC服务,然后使用Go或者Python,或者Ruby编写的客户端去访问。另外,最新的Google APIs将会有gRPC版本,可以使你很方便地在你的应用中使用Google 的功能。

使用Protocol buffers

gRPC默认使用protocol buffers—Google 的成熟开源机制,用来序列化结构化数据(即便如此,它还可以和其他数据结构例如JSON一起使用)。如你所见,我们下边的例子,将使用proto文件定义gRPC服务,其方法参数和返回类型作为protocol buffer的消息类型。

Protocol buffer 版本

虽然protocol buffers已经在开源用户中使用有一段时间了,但是我们给的例子中使用的却是一个新版本的protocol buffers,叫做proto3。proto3有着轻量级的简化语法,一些有用的新特性,支持更多的语言。当前发布的beta版有Java、C++、Python、Objective-C和C#。alpha版本有protocol buffers Github repo上的JavaNano(Android Java),Ruby,C++和JavaScript,还有golang/protobuf Github repo上的Go语言生成器,更多语言正在开发中。你会在proto3语言指南和各种语言的参考文档中找到更多信息,在release note中看到当前版本的主要改进。

一般情况下,当你还停留在proto2的时候(当前默认protocol buffers版本),我们推荐你使用proto3和gRPC一起使用,因为它能让你使用gRPC支持的全部语言,而且能避免proto2客户端与proto3服务端或者proto3客户端与proto2服务端通信时带来的兼容性问题。

Hello gRPC!

既然你已经了解了gRPC,那么接下来,我们需要搞清它的工作原理,最简单的办法是看一个简单的例子。我们的Hello World将想你展示一个简单的gRPC客户端-服务器应用的构成,并教你如何下手:

1.创建一个protocol buffers schema,定一个简单的RPC服务,它只有一个HelloWorld方法。

2.创建一个服务器,用你喜欢的语言实现上边的接口。

3.用你最喜欢的语言编写一个客户端访问你的服务器。

这个例子的完整代码在我们Github仓库的examples目录下。我们使用Git版本控制系统进行代码管理:然而,你不需要知道任何关于Git的知识,除了知道如何安装和执行一些个git命令。

注:我们的服务端代码的例子不是适用于所有语言,比如gRPC PHP和Object-C只是用来做客户端。

这只是一个介绍性的例子,而不是针对某个特定语言的综合教程。你可以在本站找到更多的深入案例和参考文

设置

这个部分介绍如何在你本机上进行设置来运行我们的例子。如果你只是想阅读源代码,你可以直接跳过这个步骤

安装Git

略~

使用Java作为首选语言

1. 获取源码

 git clone https://github.com/grpc/grpc-java.git --克隆java源码
 cd grpc-java/examples   --切换目录到examples

2. 安装gRPC

为了能够运行例子(和我们的教程示例,以及你创建的任何的gRPC项目),你需要为你选择的语言安装gRPC运行时。另外,如果你想要尝试生成gRPC代码,根据你选择的语言,可能需要安装protocol buffers编译器和相应的gRPC插件。未来的版本中我们会进一步简化这个过程。

注:这个例子也是Java gRPC自身构建的一部分

所以,运行它还是和运行一个常规的项目不太一样的。安装和构建请按照Quickstart中的指示。

添加下面的内容到你的build文件中,以安装你项目所需的运行时jar依赖:

Gradle:—使用Gradle作为构建工具

compile ‘io.grpc:grpc-all:0.15.0‘

Maven:—使用Maven作为构建工具,将如下添加到examples目录下的pom.xml文件的dependencies模块中

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

3. 定义一个服务

创建我们例子的第一步是定义一个服务:一个PRC服务指定了可以通过方法参数和返回值类型进行远程调用的方法。如你在overview篇章中所见,gRPC使用protocol buffers。我们使用protocol buffer接口定义语言(IDL)定义我们的服务方法,其参数和返回值都为protocol buffer消息类型。客户端和服务器端都会使用由服务定义产生的接口代码。

在我们的服务定义文件——helleworld.proto中使用了protocol buffers IDL。Greeter服务有一个方法SayHello指出:服务端接受一个来自远程客户端的HelloRequest消息,这个消息中包含了用户的姓名,然后返回一个打招呼的HelloReply。这是你可以在gRPC中定义的最简单的RPC——你可以在你所选择的语言的教程中找到更多类型。

syntax = "proto3";

option java_package = "io.grpc.examples";

package helloworld;

// The greeter 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;
}

如你所见,一个gRPC方法只接受一个protocol buffer消息类型作为它的请求,并只返回一个protocol buffer类型作为它的响应—这也是所有的gRPC方法的使用场景。然而,它不是限制你以何种样的数据在gRPC客户端和服务器之间进行传递—想要添加更多的参数或者返回值,你只需要向protocol buffer请求和响应类型中添加适当的字段即可。

生成gRPC代码

一旦我们定义好了我们的服务,我们使用protocol buffer编译器(protoc )来生成创建我们应用所需的特定的客户端和服务器端代码—你可以以任何gRPC支持的语言生成gRPC代码,即便PHP和Object-C仅支持创建客户端。生成的代码包含了客户端用来调用的代码和一个需要实现的服务端的抽象接口,这些方法都定义在我们的Greeter 服务器中。

正如在安装部分提到的,这个例子的构建系统是Java gRPC自身构建的一部分——简单起见,我们推荐你使用我们为这个例子预生成的代码。你可以参考README文件,了解如何由你的.proto文件生成代码。

我在另一篇文章中讲述如何使用maven插件来由.proto文件自动生成gRPC的基础代码:gRPC动手实践

这个例子的预生成代码在src/generated/main目录下。下面的类包含了我们为这个例子预生成的所有代码:

  1. HelloRequest.java, HelloResponse.java, and others which have all the

    protocol buffer code to populate, serialize, and retrieve our

    HelloRequest and HelloReply message types

  2. GreeterGrpc.java, 包含了 :

    2.1 一个抽象基类GreeterImplBase,由Greeter服务器端去实现

 public static abstract class GreeterImplBase {
        ...
        public void sayHello(Helloworld.HelloRequest request,
            StreamObserver<Helloworld.HelloReply> responseObserver) {
          ...
       }
 }  

2.2 一个存根类GreeterStub,客户端使用它和Greeter服务进行通信:

    public static class GreeterStub extends AbstractStub<GreeterStub> {
        ...
    }

编写服务端

现在让我们写些代码吧!首先我们将创建一个服务端应用实现我们的服务(你可还记得,我们可以使用除了Object-C和PHP外的所有语言)。在这一部分中我们不会对如何创建一个服务端进行深入的讲解——更多信息,会在你选择的语言的教程中。

服务实现

GreeterImpl.java 实现了我们的 Greeter 规定的行为。

如你所见,类 GreeterImpl 继承自通过IDL生成的抽象类 GreeterGrpc.GreeterImplBase ,并实现了sayHello方法。

@Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
  HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
  responseObserver.onNext(reply);
  responseObserver.onCompleted();
}

sayHello 有两个参数:

HelloRequest: 请求

StreamObserver:一个响应观察者,这是一个特殊的接口 ,被服务器的响应对象调用

为了向客户端返回我们的响应,完成调用:

上边的代码中,我们使用接口定义中指定的消息构造出了一个HelloReply响应对象。

我们返回HelloReply给客户端,然后指明我们已经完成了RPC的处理。

Server实现

另外一个提供gRPC服务的主要的特性是,要使我们实现的服务能够通过网络被访问到。

HelloWorldServer.java为我们的Java例子提供了如下:

/* The port on which the server should run */
private int port = 50051;
private Server server;

private void start() throws Exception {
  server = ServerBuilder.forPort(port)
      .addService(new GreeterImpl())
      .build()
      .start();
  logger.info("Server started, listening on " + port);
  Runtime.getRuntime().addShutdownHook(new Thread() {
    @Override
    public void run() {
      // Use stderr here since the logger may has been reset by its JVM shutdown hook.
      System.err.println("*** shutting down gRPC server since JVM is shutting down");
      HelloWorldServer.this.stop();
      System.err.println("*** server shut down");
    }
  });
}

这里我们创建了一个合适的gRPC服务器,绑定了Greeter服务实现到我们创建的端口上。然后我们开始启动服务:服务器准备接收来在Greeter服务客户端在我们指定端口上的请求。我们会在语言-说明文档中更详细介绍这是如何工作的。

编写客户端

客户端gRPC很简单。在这一步骤,我们将使用生成的代码来编写一个简单的客户端,来访问在上一部分中创建的Greeter服务器。

连接服务

首先,我们来看如何连接到Greeter 服务器。首先我们需要创建一个gRPC通道,指明主机名hostname和我们想连接的服务器的端口号。然后我们使用通道构建客户端实例。

private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;

public HelloWorldClient(String host, int port) {
  channel = ManagedChannelBuilder.forAddress(host, port)
      .usePlaintext(true)
      .build();
  blockingStub = GreeterGrpc.newBlockingStub(channel);
}

在这个例子中,我们创建了一个阻塞的存根。这就意味着RPC调用等待一直等待着服务器响应,它会返回一个响应体或者抛出一个异常。gRPC Java也有其他一些存根可以构造非阻塞的调用,响应是异步的返回地。

调用RPC

现在我们可以连接服务获取一个“问候”:

我们构造和填充和一个HelloRequest发送给服务。

我们用我们的请求调用客户端存根的SayHello() RPC,如果RPC成功,得到一个封装过的HelloReply,从中我们可以获取到我们的“问候”。

HelloRequest req = HelloRequest.newBuilder().setName(name).build();
HelloReply reply = blockingStub.sayHello(req);

你可以在 HelloWorldClient.java中看到完整的代码。

试一下吧!

你可以尝试构建和运行我们给的使用同一种语言编写客户端和服务端的例子。或者你也可以试着gRPC最有用的特性—不同语言代码之间的互操作性—运行一个使用不同语言编写的服务端和客户端。每个服务和客户端都使用了由同一个proto生成的接口代码,也就说,任何Greeter客户端可以同任意的Greeter服务器进行通信。

首先,你需要切换到examples目录下,找到你的构建配置文件gradle.properties,修改gen-grpc-java的版本号(默认与上边的dependencies一致,为${grpc-Version}):

然后运行以下代码,构建客户端和服务器

$ ./gradlew -PskipCodegen=true installDist

然后运行服务器,它会监听在 50051端口上:

$ ./build/install/grpc-examples/bin/hello-world-server

一旦服务器运行起来之后,在另外一个命令窗口中运行客户端确认它能收到消息:

$ ./build/install/grpc-examples/bin/hello-world-client

时间: 2024-10-10 05:27:53

gRPC初探的相关文章

进阶之初探nodeJS

一.前言 在"初探nodeJS"随笔中,我们对于node有了一个大致地了解,并在最后也通过一个示例,了解了如何快速地开启一个简单的服务器. 今儿,再次看了该篇随笔,发现该随笔理论知识稍多,适合初级入门node,固萌生一个想法--想在该篇随笔中,通过一步步编写一个稍大一点的node示例,让我们在整体上更加全面地了解node. so,该篇随笔是建立在"初探nodeJS"之上的,固取名为"进阶之初探nodeJS". 好了,侃了这多,那么我们即将实现一个

从273二手车的M站点初探js模块化编程

前言 这几天在看273M站点时被他们的页面交互方式所吸引,他们的首页是采用三次加载+分页的方式.也就说分为大分页和小分页两种交互.大分页就是通过分页按钮来操作,小分页是通过下拉(向下滑动)时异步加载数据. 273这个M站点是产品推荐我看的.第一眼看这个产品时我就再想他们这个三次加载和翻页按钮的方式,那么小分页的pageIndex是怎么计算的.所以就顺便看了下源码. 提到看源码时用到了Chrome浏览器的格式化工具(还是朋友推荐我的,不过这个格式化按钮的确不明显,不会的话自行百度). 三次加载和分

[转载]HDFS初探之旅

转载自 http://www.cnblogs.com/xia520pi/archive/2012/05/28/2520813.html , 感谢虾皮工作室这一系列精彩的文章. Hadoop集群(第8期)_HDFS初探之旅 1.HDFS简介 HDFS(Hadoop Distributed File System)是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超大文件的需求而开发的,可以运行于廉价的商用服务器上.它所具有的高容错.高可靠性.高可扩展性.高

MongoDB初探系列之二:认识MongoDB提供的一些常用工具

在初探一中,我们已经可以顺利的将MongoDB在我们自己的机器上跑起来了.但是在其bin目录下面还有一些我们不熟知的工具.接下来,将介绍一下各个小工具的用途以及初探一中MongoDB在data文件夹下创建的文件的用途. 1.bin目录下面的各种小工具简介及使用方式 bsondump.exe 用于将导出的BSON文件格式转换为JSON格式mongo.exe mongoDB的客户端 mongod.exe 用于启动mongoDB的Server mongodump.exe 用于从mongodb数据库中导

Asynchronous Pluggable Protocols 初探

Asynchronous Pluggable Protocols,异步可插入协议,允许开发者创建可插协议处理器,MIME过滤器,以及命名空间处理器工作在微软IE4.0浏览器以及更高版本或者URL moniker中.这涉及到Urlmon.dll动态链接库所公开(输出)的可插协议诸多功能,本文不进行深入的原理讲解,只对它其中之一的应用进行解析,那就是如何将一个应用程序注册为URL协议. 应用场景: tencent协议: 当我们打开"tencent://message/?uin=要链接的QQ号 &qu

重新认识HTML,CSS,Javascript 之node-webkit 初探

今天我们来系统的.全面的 了解一下前端的一些技术,将有助于我们写出 更优秀的 产品 出来. 什么是HTML? HTML 是用来描述网页的一种语言. HTML 包含一些根节点,子节点,文本节点,属性节点,组成, 它通过一系列预定义标签来描述网页结构,如: <title>This is title</title> ,这个表明该网页的标题是 This is title. 什么是CSS? CSS 指层叠样式表 (Cascading Style Sheets),它描述浏览器显示如何显示htm

java进阶06 线程初探

线程,程序和进程是经常容易混淆的概念. 程序:就是有序严谨的指令集 进程:是一个程序及其数据在处理机上顺序执行时所发生的活动 线程:程序中不同的执行路径,就是程序中多种处理或者方法. 线程有两种方法实现 一:继承Thread 覆盖run方法 package Thread; public class Thread1 { public static void main(String[] args){ MyThread1 thread1=new MyThread1(); thread1.setName

数据加密解密初探

在一次网络通信或者是进程通信中,如果传输数据采用明文的方式,那么很容易被第三方"窃听"到,安全性难以保障. 而所谓加密是让数据从明文变成密文,传输过程中是密文,传送过去之后对方接收到的也是密文.--可以理解为密文就是乱码,看不出内在的任何意义,通常也都是逐位对应的. 在接收方接收到密文之后只有把它还原为原来的样子才可以理解对方说的具体是什么,此过程就叫做解密. 所谓系统的安全要实现的目标应该包括:机密性-confidentiality,完整性-integrity 和可用性-availa

Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用

一.高可用的Session服务器场景简介 1.1 应用服务器的无状态特性 应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性. PS:提到无状态特性,不得不说下Http协议.我们常常听到说,Http是一个无状态协议,同一个会话的连续两个请求互相不了解,他们由最新实例化的环境进行解析,除了应用本身可能已经存储在全局对象中的所有信息外,该环境不保存与会话有关的任何信息.之所以我们在使用ASP.NET WebForm开发中会感觉不到Http的无状态特