Grpc原理

GRPC 是谷歌推出的一个高性能、开源和通用的 RPC 框架,面向服务端和移动端,基于 HTTP/2 设计。

rpc框架

rpc全称是Remote Procedure Call,即远程调用服务器的方法,它将服务器端的请求变成了本地方法调用。其屏蔽底层的传输方式(TCP/UDP)、序列化方式(XML/Json/ 二进制)和通信细节。可以看成是对数据的传输、序列化以及通信做了一层封装。服务调用者可以像调用本地接口一样调用远程的服务,而不需要关心底层通信细节和调用过程。
其具体过程如下:

grpc 互为服务器 istio grpc服务间调用_istio

rpc框架分为3类:
支持多语言的PRC框架,比较成熟的有 Google 的 gRPC、Apache(Facebook)的 Thrift;
支持特定语言的 RPC 框架,如新浪微博的 Motan;
支持服务治理等服务化特性的分布式服务框架,其底层内核仍然是RPC框架, 例如阿里的 Dubbo。

实现远程调用其实就是将远程的调用方法的接口放在本地,用户通过访问本地的接口即可以访问远程的调用方法,过程如下:

grpc 互为服务器 istio grpc服务间调用_序列化_02

Grpc的过程

建立连接
使用http/2协议,它比http1.x的特色在于采用新的二进制格式,多路复用,header压缩。这些特点都有利于提高传输效率。

寻址
由调用方提供要调用的方法,对方网络地址,端口。当然,这些细节都会在gRPC框架内做好映射:本地只要通过gRPC调用方法,就会自动去向对方请求,这个过程是透明的。

序列化与反序列化
网络传输过程都是用二进制流的形式。因为本地的序列化方式和远处的反序列化方式都是对应的,所以安全性也有了保障。哪怕有人截取了中间的信息,也只能得到二进制数据。这就不像http请求那样,会考虑到中间人类的问题。
为了提高序列化和反序列化的效率,gRPC采用的protobuf格式,压缩效率比常见的json和xml更高。

Grpc的特点
1.支持多种语言
2.基于IDL文件定义服务,通过proto3工具生成指定语言的数据结构
3.基于HTTP/2设计,支持双向流、消息头压缩、多路复用、服务端推送等特性,在移动端设备上更省流量
4.序列化支持PB和JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。
5.服务的实现不是通过反射实现,而是通过proto工具生成的代码实现,性能更优

Grpc与restful

REST描述的是在网络中client和server的一种交互形式;REST本身不实用,实用的是如何设计 RESTful API。
REST全称Resource Representational State Transfer:通俗来讲就是资源在网络中以某种表现形式进行状态转移

Resource:资源,即数据,可以理解为某个url资源
Representational:某种表现形式,比如用JSON,XML,JPEG等;
State Transfer:状态变化。通过HTTP动词实现。如Get、Post、Delete等

restful和grpc都是服务器与客户端的常用交互方式。RESTful通常使用 http+JSON 或 XML 的格式传输信息,而gRPC采用protobuf传输信息,proto会提供更严格的接口约束条件,安全性更高,对于高并发的场景更适用。但是grpc的可读性不如restful的可读性
restful通过暴露服务接口的方式去提供接口调用,但是采用grpc,其服务的接口会放在客户端。由于restful是采用的http,需要三次握手建立连接,因此其效率会比restful更快。

grpc 互为服务器 istio grpc服务间调用_grpc 互为服务器_03

实操

Grpc是一个rpc框架,因此其要实现数据通信、序列化和反序列等操作,首先需要生成proto文件自动补全序列化和反序列化代码。

环境准备

#安装grpc工具
pip install grpcio-tools

proto文件

hello.proto文件代码如下:

syntax = "proto3"; // 表明用的是proto3版本语法
message HelloRequest { // message是proto3里面最基本的结构类型,可以类比python中的class,定义消息类
	string name = 1;  // 里面的字段也是强制先说明字段类型的
}
message HelloResponse {
	string name2 = 2;  // 返回状态吗
}
service PageService {// 开始定义可以被本地调用的方法,这里service可以理解为远端提供的服务,定义服务类
    //声明函数方法,即提供的服务,函数名为hello,参数为helloRequest,返回类型为HelloResponse
	rpc Hello (HelloRequest) returns (HelloResponse) {}
}

利用grpc工具自动补齐序列化和反序列化代码

python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/hello.proto

然后编写服务端的文件,需要实现hello_pb_grpc.py里面的服务类声明的函数功能

server端文件

hello_server.py文件内容如下:

import grpc
import hello_pb2
import hello_pb2_grpc
from concurrent import futures    ##用于初始化grpc.server对象使用

##继承proto工具自动生成的代码中的servericer类,实现服务端的功能
class Server(hello_pb2_grpc.HelloServiceServicer):
    def Hello(self, request, context):
    ##返回proto文件中定义的消息类,否则会造成响应消息序列化失败的问题
        return hello_pb2.HelloResponse(name2="welcome "+request.name)

def server():
    ##初始化grpc.server对象,在服务端部署 
    server=grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    ##用实现服务端功能的Server实例去绑定生成的grpc.server对象
    hello_pb2_grpc.add_HelloServiceServicer_to_server(Server(),server)
    ##给该服务绑定一个监听的socket地址
    server.add_insecure_port('[::]:50050')
    ##启动服务
    server.start()
    ##等待线程一起关闭
    server.wait_for_termination()

if __name__ == '__main__':
    server()

client端文件

hello_client.py文件内容:

import grpc
import hello_pb2  as hello__pb2
import hello_pb2_grpc

def run():
  #生成channel对象,用于连接远端服务器提供某个服务的socket
    channel=grpc.insecure_channel('localhost:50050')
  #初始化stub,stub位于客户端,客户端通过grpc stub去获取服务
    stub=hello_pb2_grpc.HelloServiceStub(channel)
    ##使用stub去请求服务
    res=stub.Hello(hello__pb2.HelloRequest(name="hxv"))
    print(res)

if __name__ == '__main__':
    run()

grpc的组件:
1.stub:是保存远端服务接口的一个对象,需要用grpc.Channel对象去初始,channel用于客户端去连接一个grpc服务器
2.server:位于服务端,server需要服务端提供服务的一个类去进行初始化,通过grpc server去暴露服务,而不是暴露服务本身
3.Registration Function:注册函数,用服务端提供服务功能的servicer去注册一个grpc.server对象

proto文件里面通常要定义消息(message)和服务(service)的类,然后使用grpc工具去自动生成代码。

grpc 互为服务器 istio grpc服务间调用_istio_04

 GRPC的Client(stub)与Server,均通过Netty Channel作为数据通信,序列化、反序列化则使用Protobuf,每个请求都将被封装成HTTP2的Stream,在整个生命周期中,客户端Channel应该保持长连接,而不是每次调用重新创建Channel、响应结束后关闭Channel(即短连接、交互式的RPC),目的就是达到链接的复用,进而提高交互效率。