grpc

100 阅读9分钟

简介

远程调用框架,2015 年,Google发布了开源 RPC 框架gRPC

在 gRPC 中,客户端可以直接调用不同机器上的服务端的方法,就像调用本地函数一样。

特性

  • gRPC 基于服务的思想:定义一个服务,描述这个服务的方法以及入参出参,服务器端有这个服务的具体实现,客户端保有一个存根,提供与服务端相同的服务
  • gRPC 默认采用 protocol buffer 作为 IDL (Interface Description Lanage) 接口描述语言,服务之间通信的数据序列化和反序列化也是基于 protocol buffer 的,因为 protocol buffer 的特殊性,所以 gRPC 框架是跨语言的通信框架(与编程语言无关性),也就是说用 Java 开发的基于 gRPC 的服务,可以用 GoLang 编程语言调用
  • gRPC 同时支持同步调用和异步调用,同步 RPC 调用时会一直阻塞直到服务端处理完成返回结果, 异步 RPC 是客户端调用服务端时不等待服务段处理完成返回,而是服务端处理完成后主动回调客户端告诉客户端处理完成
  • 基于 http2 协议的特性:gRPC 允许定义如下四类服务方法
    • 单项 RPC:客户端发送一次请求,等待服务端响应结构,会话结束,就像一次普通的函数调用这样简单
    • 服务端流式 RPC:客户端发起一起请求,服务端会返回一个流,客户端会从流中读取一系列消息,直到没有结果为止
    • 客户端流式 RPC:客户端提供一个数据流并写入消息发给服务端,一旦客户端发送完毕,就等待服务器读取这些消息并返回应答
    • 双向流式 RPC:客户端和服务端都一个数据流,都可以通过各自的流进行读写数据,这两个流是相互独立的,客户端和服务端都可以按其希望的任意顺序独写

步骤

grpc.io/docs/langua…

定义.proto文件

helloworld.proto

  • 定义 service ,rpc, request, reply
syntax = "proto3";

option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

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;
}

generate gRPC code

protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    helloworld/helloworld.proto

This will regenerate the helloworld/helloworld.pb.go and helloworld/helloworld_grpc.pb.go files, which contain:

  • helloworld.pb.go : request, response 结构体,及结构体的 Reset, String, ProtoReflect, GetXx属性 等方法
type HelloRequest struct {
   state         protoimpl.MessageState //`state` 保存 proto文件的反射信息 `sizeCache`序列化的数据总长度 `unknownFields` 不能解析的字段
   sizeCache     protoimpl.SizeCache
   unknownFields protoimpl.UnknownFields

   Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
  • helloworld_grpc.pb.go: client, server 的 interface 定义 及其 实现
type GreeterClient interface {
   // Sends a greeting
   SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}

type greeterClient struct {
   cc grpc.ClientConnInterface
}

func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {
   return &greeterClient{cc}
}

func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
   out := new(HelloReply)
   err := c.cc.Invoke(ctx, Greeter_SayHello_FullMethodName, in, out, opts...)
   if err != nil {
      return nil, err
   }
   return out, nil
}

core concepts

Service definition

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}
  • Unary RPC

    rpc SayHello(HelloRequest) returns (HelloResponse);
    
  • Server streaming RPC

    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
    
  • Client streaming RPC ,the client writes a sequence of messages and sends them to the server

     rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
    
  • Bidirectional streaming RPC

    rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
    

Metadata

Metadata is information about a particular RPC call (such as authentication details) in the form of a list of key-value pairs, where the keys are strings and the values are typically strings, but can be binary data.

Channels

A gRPC channel provides a connection to a gRPC server on a specified host and port. It is used when creating a client stub. Clients can specify channel arguments to modify gRPC’s default behavior, such as switching message compression on or off. A channel has state, including connected and idle.

RPC life cycle

Unary RPC

client sends a single request and gets back a single response.

  1. Once the client calls a stub method, the server is notified that the RPC has been invoked with the client’s metadata for this call, the method name, and the specified deadline if applicable.
  2. The server can then either send back its own initial metadata (which must be sent before any response) straight away, or wait for the client’s request message. Which happens first, is application-specific.
  3. Once the server has the client’s request message, it does whatever work is necessary to create and populate a response. The response is then returned (if successful) to the client together with status details (status code and optional status message) and optional trailing metadata.
  4. If the response status is OK, then the client gets the response, which completes the call on the client side.

channelz

grpc.io/blog/a-shor…

channelz, reports all known channels, subchannels, servers, and connections that process currently has. This information can be used to debug or diagnose a live program which may be exhibiting undesirable network behavior

grpc && http2

grpc.io/blog/grpc-o…

gRPC introduces three new concepts: channelsremote procedure calls (RPCs), and messages. The relationship between the three is simple: each channel may have many RPCs while each RPC may have many messages.

Channels are a key concept in gRPC. Streams in HTTP/2 enable multiple concurrent conversations on a single connection;channels extend this concept by enabling multiple streams over multiple concurrent connections.

Resolvers and Load Balancers

A DNS resolver, for example, might resolve some host name to 13 IP addresses, and then a RoundRobin balancer might create 13 connections - one to each address - and round robin RPCs across each connection.

grpc.io/docs/guides…

(Special topic)  Each gRPC channel uses 0 or more HTTP/2 connections and each connection usually has a limit on the number of concurrent streams. When the number of active RPCs on the connection reaches this limit, additional RPCs are queued in the client and must wait for active RPCs to finish before they are sent. Applications with high load or long-lived streaming RPCs might see performance issues because of this queueing. There are two possible solutions:

  1. Create a separate channel for each area of high load in the application.
  2. Use a pool of gRPC channels to distribute RPCs over multiple connections (channels must have different channel args to prevent re-use so define a use-specific channel arg such as channel number).

实践

REST

Representational State Transfer (REST) 是一种源自 Roy Fielding 博士论文的架构风格。Fielding 是 HTTP 规范的主要作者之一,也是 REST 架构风格的鼻祖。REST 是面向资源架构 (ROA) 的基础,你可以将分布式应用程序建模为资源的集合,访问这些资源的客户端可以更改这些资源的状态(创建、读取、更新或删除)。

REST 的实际实现是 HTTP,在 HTTP 中,你可以将 RESTful Web 应用程序建模为可使用唯一标识符 (URL) 访问的资源集合,可通过HTTP方法(GET、POST、PUT、DELETE、PATCH 等)来变更这些资源的状态。资源状态以文本格式表示,例如 JSON、XML、HTML、YAML 等。

使用带有 HTTP 和 JSON 的 REST 架构风格构建应用程序已成为构建微服务的常见方式。然而,随着微服务数量的激增及网络交互的愈发复杂,RESTful 服务已经无法满足预期的现代需求。RESTful 服务有几个关键限制,使它难以成为现代基于微服务的应用程序的消息传递协议:

  1. 低效的基于文本的消息协议。 本质上,RESTful 服务构建在基于文本的传输协议(如 HTTP 1.x)之上,并使用人类可读的文本格式(如 JSON)进行传输。当涉及到服务到服务的通信时,使用 JSON 等文本格式是非常低效的,因为通信的过程中会涉及到文本格式和二进制格式的转换,而且同样的内容,使用文本格式编码与使用其他简单编码方式相比,占用的空间更多,需要传输的数据也就更大。
  2. 应用程序之间缺乏强类型接口。 当你开发 RESTful 服务时,不需要对应用程序之间共享的信息进行服务定义和类型定义。这会导致服务之间通信时容易出现不兼容、运行时错误和交互问题。
  3. REST 架构风格难以执行。 作为一种架构风格,REST 有很多“良好实践”,你需要遵循这些“良好实践”来实现真正的 RESTful 服务。但是它们没有作为实现协议(例如 HTTP)的一部分强制执行,这使得在实现阶段很难强制执行它们。

gRPC的优势

  1. 实现的进程间通信方式高效。gRPC 不使用 JSON 或 XML 等文本格式,而是使用基于二进制协议的protocol buffer与 gRPC 服务、客户端进行通信。此外,gRPC 是在 HTTP/2 之上实现的protocol buffer,这使得进程间通信更快。
  2. 具有简单、定义良好的服务接口和协议。你首先定义服务接口,然后处理实现细节。因此,与用于RESTful服务定义的 OpenAPI/Swagger 和用于 SOAP Web 服务的 WSDL 不同,gRPC 提供了简单但一致、可靠且可扩展的应用程序开发体验。
  3. 强类型。protocol buffer清楚地定义了应用程序之间通信的数据类型,这使得分布式应用程序开发更加稳定。因为静态类型有助于减少你在构建跨多个团队和技术的云原生应用程序时遇到的大多数运行时和交互错误。
  4. 支持多语言。gRPC被设计成支持多种编程语言。使用protocol buffer的服务定义与语言无关。因此,你可以选择grpc支持的任意语言,并与任何现有的 gRPC 服务或客户端进行通信。
  5. 支持双向流式传输。gRPC 对客户端或服务器端流式传输具有原生支持,这使得开发流媒体服务或流媒体客户端变得更加容易。
  6. 内置多种高级特性。gRPC 提供对高级特性的内置支持,例如身份验证、加密、元数据交换、压缩、负载平衡、服务发现等。
  7. 与云原生生态系统高度集成。gRPC 是 CNCF 的一部分,大多数现代框架和技术都为 gRPC 提供了开箱即用的原生支持。例如,Envoy等 CNCF 下的许多项目都支持使用 gRPC 作为通信协议。

gRPC的缺点

  1. 它可能不适合面向外部的服务。当你想将应用程序或服务提供给外部客户端使用时,gRPC 可能不是最合适的协议,因为大多数外部使用者对 gRPC 还很陌生。而且,gRPC 服务的协议驱动、强类型化特性可能会降低你向外部提供的服务的灵活性,因为外部使用者可以控制的东西要少得多。
  2. 生态系统相对较小。与传统的 REST/HTTP 协议相比,gRPC 生态系统仍然相对较小。浏览器和移动应用程序对 gRPC 的支持仍处于初级阶段。