RPC:远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。
常见 RPC 技术和框架有:应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。
Google gRPC 框架是基于 HTTP2 协议实现的,底层使用到了 Netty 框架的支持。
一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。
RPC 的核心功能是指实现一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分:
一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。
重要组成:
一次 RPC 调用流程如下:
RPC的目标就是要2~7这些步骤都封装起来,让用户对这些细节透明。
官网:https://www.grpc.io/
gRPC 官方文档中文版:http://doc.oschina.net/grpc
RPC
目标:让远程服务调用更加简单、透明,其负责屏蔽底层的传输方式(TCP/UDP
)、序列化方式(XML/Json
)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2
设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go.
其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP
和 C#
支持.
gRPC 也是基于以下理念:
gRPC 的功能优点:
gRPC 的组成部分:
http2
作为网络传输层protobuf
这个高性能的数据包序列化协议protoc gprc
插件生成易用的 SDK
。ProtoBuf是一种跨平台、语言无关、可扩展
的序列化结构数据的方法,可用于网络数据交换及存储。
在序列化结构化数据的机制中,ProtoBuf是灵活、高效、自动化
的,相对常见的XML、JSON
,描述同样的信息,ProtoBuf序列化后数据量更小
(在网络中传输消耗的网络流量更少)、序列化/反序列化速度更快
、更简单。
一旦定义了要处理的数据的数据结构之后,就可以利用ProtoBuf的代码生成工具生成相关的代码。只需使用 Protobuf 对数据结构进行一次描述
,即可利用各种不同语言对你的结构化数据轻松读写。
ProductInfo.proto
文件中声明的,服务器端和客户端都会使用该文件来生成代码。这里假设 ProductInfo 服务使用 Go
语言来实现,消费者使用 Java
语言来实现,两者之间的通信则通过 HTTP/2
来进行。(客户端端存根)Stub
,一方面接收应用层的参数,并对其序列化后通过底层协议栈发送到服务端,另一方面接收服务端序列化后的结果数据,反序列化后交给客户端应用层;(服务端存根)Skeleton
,其功能与Stub相反,从传输层接收序列化参数,反序列化后交给服务端应用层,并将应用层的执行结果序列化后最终传送给客户端Stub;// Java类
public class Person
{private String name;private Int id;private String email;
...
}
proto3协议
syntax = "proto3"; // 协议版本 必须写:syntax = "proto3";package protocobuff_Demo; // 关注1:包名,防止不同 .proto 项目间命名 发生冲突option java_package = "com.chenj.protobuf"; 作用:指定生成的类应该放在什么Java包名下
option java_outer_classname = "Demo";//作用:生成对应.java 文件的类名(不能跟下面message的类名相同)
// 关注2:option选项,作用:影响 特定环境下 的处理方式// 关注3:消息模型 作用:真正用于描述 数据结构
// 下面详细说明
// 生成 Person 消息对象(包含多个字段,下面详细说明)
message Person {string name = 1;//(proto3消息定义时,移除了 “required”、 “optional” :)int32 id = 2;//(proto3消息定义时,移除了 “required”、 “optional” :)string email = 3;//(proto3消息定义时,移除了 “required”、 “optional” :)enum PhoneType {MOBILE = 0;HOME = 1;WORK = 2;}message PhoneNumber {string number = 1;PhoneType type = 2 ;//(proto3消息定义时,移除了 default 选项:)}repeated PhoneNumber phone = 4;
}message AddressBook {repeated Person person = 1;
}
字段类型
字段类型主要有 三 类:
枚举类型
作用:为字段指定一个 可能取值的字段集合,该字段只能从 该指定的字段集合里 取值
下面例子,电话号码 可能是手机号、家庭电话号或工作电话号的其中一个,那么就将PhoneType定义为枚举类型,并将加入电话的集合( MOBILE、 HOME、WORK)
// 枚举类型需要先定义才能进行使用// 枚举类型 定义enum PhoneType {MOBILE = 0;HOME = 1;WORK = 2;
// 电话类型字段 只能从 这个集合里 取值}// 特别注意:
// 1. 枚举类型的定义可在一个消息对象的内部或外部
// 2. 都可以在 同一.proto文件 中的任何消息对象里使用
// 3. 当枚举类型是在一消息内部定义,希望在 另一个消息中 使用时,需要采用MessageType.EnumType的语法格式message PhoneNumber {required string number = 1;optional PhoneType type = 2 [default = HOME];// 使用枚举类型的字段(设置了默认值)}
首先安装 ProtoBuf 编译器 protoc,这里有详细的安装教程,安装完成后,可以使用以下命令生成 Java 源代码:
protoc --java_out=./src/main/java ./src/main/idl/customer.proto。
Protocol Buffer将 消息里的每个字段 进行编码后,再利用T - L - V
存储方式 进行数据的存储,最终得到的是一个 二进制字节流T - L - V
的数据存储方式即 Tag - Length - Value
,标识 - 长度 - 字段值 存储方式;以 标识 - 长度 - 字段值 表示单个数据,最终将所有数据拼接成一个 字节流,从而 实现 数据存储 的功能, Length可选存储,如 储存Varint编码数据就不需要存储Length。
优点从上图可知,T - L - V
存储方式的优点是
因为 Protocol Buffer对于数据字段值的 独特编码方式
& T - L - V
数据存储方式,使得 Protocol Buffer
序列化后数据量体积如此小。
Protocol Buffer的序列化 & 反序列化简单 & 速度快的原因是:
Protocol Buffer的数据压缩效果好(即序列化后的数据量体积小)的原因是: