grpc介绍(一)——rpc、protobuf和grpc
RPC
概念
RPC(Remote Procedure Call)远程过程调用。是一种通过网络向远程计算机请求服务信息,但又不需要了解底层网络技术的通信方式。就是像调用本地服务一样调用远程服务。
RPC采用客户端/服务端的模式,通过request-reponse消息模式实现。
实现步骤
RPC的主要实现步骤如下(加粗部分为rpc框架要实现的步骤):
- 客户端发起本地连接请求连接服务端的服务;
- 客户端程序句柄(stub)负责将客户端请求的消息序列化成二进制消息;
- 客户端通过网络模块将消息发送至服务端;
- 服务端程序句柄(stub)将收到的消息进行反序列化;
- 服务端程序句柄(stub)根据解析后的方法调用本地服务;
- 服务端上的服务处理完成后将消息发送至程序句柄(stub);
- 服务端程序句柄(stub)将消息序列化成二进制消息;
- 服务端通过网络模块将消息发送至客户端;
- 客户端程序句柄(stub)将消息反序列化;
- 客户端程序句柄(stub)根据解析后的方法调用本地服务;
实现原理
RPC 架构主要包括三部分:
- 服务注册中心(Registry),负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用,主要用于实现负载均衡和故障切换。
- RPC Server,服务提供者,提供服务接口定义与服务实现类,服务提供者启动后主动向服务注册中心(Registry)注册机器IP、端口以及提供的服务列表;
- RPC Client,服务消费者,通过远程代理对象调用远程服务,启动时向服务注册中心(Registry)获取服务提供方地址列表。
优缺点
优点
- 对于使用者来说,简单高效,无需关心底层逻辑,仅调用接口即可;
- 主流的rpc框架(如Dubbo、grpc、Hessian等)跨语言支持;
缺点
- 开发难度大;
- 异常处理困难
Stub
Stub是一段用于转换参数的代码。
为什么使用存根(Stub)?
rpc调用中,服务端提供方法和接口实现,客户端调用方法。但往往客户端和服务端位于不同的服务器上,主要有以下差异:
- 编码不同。例如一端是GBK,另一端是UTF-8;
- 大小端不同;
- 编译语言不同。例如一段是c++,另一端是java;
- 内存布局不同;
- 尾款不同。有32/64位之分;
存根(Stub)选取一种中间形式,让两端的数据都可以正确可逆的与中间数据进行转换。常用的中间数据类型包括:纯字符串、XML、JSON、protobuf。
HTTP协议
从RPC的实现中可以看出,RPC的通信需要底层网络协议的支持,HTTP是一种比较好的实现方式。
概念
HTTP(HyperText Transfer Protocol)超文本传输协议,是一个双向协议,可以用来传输文字、图片、音视频等数据。
HTTP协议在3.0版本之前是基于TCP的,3.0版本底层改成了UDP。
HTTP/1.0
优点
- 简单,报文的格式为header+body;
- 灵活、易于扩展;
- 应用广泛、跨平台;
缺点
- 无状态;
- 明文传输;
- 不安全(后续HTTPS做了相关方面的修复);
HTTP/1.1
优点
- 支持tcp长连接,改善了HTTP/1.0短连接造成的开销;
- 支持管道(pipeline)网络传输;
缺点
- 请求 / 响应头部(Header)未压缩,首部信息越多延迟越大;
- 无请求优先级控制;
- 服务器按请求顺序响应,会造成队头阻塞;
- 请求只能从客户端开始,服务端被动响应;
HTTP/2.0
优点
- 头部压缩;
- 二进制格式。头信息和数据体都是二进制,统称为帧:头信息帧和数据帧;
- 数据流;
- 多路复用。一个连接中并发处理多个请求和响应;
- 服务器推送。服务器可以主动向客户端发送消息;
缺点
- 多个请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求;
HTTP/3.0
优点
- 底层协议改为UDP。基于UDP的QUIC协议可以实现类似TCP的可靠性传输,QUIC是一个在UDP之上的伪TCP+TLS+HTTP/2的多路复用协议;
GRPC-WEB
gRPC-Web是一个JavaScript客户端库,使Web应用程序能够直接与后端gRPC服务通信,而不需要HTTP服务器充当中介。
关于详细的grpc-web可以查看:
ProtoBuf
概念
protobuf(protocol buffer)一种用于序列化结构数据的工具,用于数据的存储和交换。是开源的一种数据格式,适合高性能,对响应速度有要求的数据传输场景。因为protobuf是二进制数据格式,需要编码和解码。
序列化和反序列化
在前面介绍rpc实现步骤时讲到,客户端或服务端的程序句柄(stub)在接收自身服务的消息后,会将消息进行序列化,在接收网络模块发送的消息后,会将数据进行反序列化。
- 序列化:将结构数据或者对象转换成能够用于存储和传输的格式;
- 反序列化:将序列化后的数据还原为结构数据和对象;
为什么要进行序列化和反序列化?
- 减少存储空间;
- 方便网络传输;
- 可以在进程间传递对象;
特点
- 使用二进制数据交换格式;
- 使用扩展名为.proto的文件定义存储类的内容;
- 使用自己的编译器protoc;
优点
- 数据压缩性高;
- 传输速度快;
- 序列化速度快;
- 简单、可扩展性高、加密性好;
- 跨平台、跨语言、可扩展性强;
安装protoc
1)下载通用编译器
下载地址:https://github.com/protocolbuffers/protobuf/releases
下载21.7-win64版本即可。
2)添加至环境变量
3)安装go专用的protoc的生成器
go get github.com/golang/protobuf/protoc-gen-go
安装时会出现一些报错,解决方案可参考:https://blog.csdn.net/www_dong/article/details/127149660
示例
1)创建.proto文件
// 当前proto的版本
syntax = "proto3";// 格式: option go_package= "path;name";
// path: 指定生成文件的存放地址
// name: 生成的go文件所属的文件名
option go_package = "../service";// 指定文件从生成出来的package
package service;// 传输的对象
message User {string username = 1;int32 age = 2;
}
2)编译生成user.pb.go文件
执行命令
protoc --go_out=./ user.proto
如图所示,成功生成user.pb.go文件。
3)测试
测试程序main.go如下:
package mainimport ("fmt""service"
)func main() {user := &service.User{Username: "dong",Age: 18,}// 序列化marshal, err := proto.Marshal(user)if err != nil {panic(err)}// 反序列化newUser := &service.User{}err := proto.Unmarshal(marshal, newUser)if err != nil {panic(err)}fmt.Println(newUser.String())
}
程序执行结果如下:
proto文件
message
message是定义一个消息类型的关键字。
字段规则
requried: 消息体中必填字段,不设置会导致解码异常;optional: 消息体中的可选字段;repeated: 消息体中可重复的字段,重复的值的顺序会被保留;
示例:
.proto中定义类型如下:
message User {optional string passwd = 3;repeated string address = 4;
}
生成的go文件中的定义如下:
// 传输的对象
type User struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsPasswd *string `protobuf:"bytes,3,opt,name=passwd,proto3,oneof" json:"passwd,omitempty"`Address []string `protobuf:"bytes,4,rep,name=address,proto3" json:"address,omitempty"`
}
字段映射
默认值
类型 | 默认值 |
---|---|
bool | false |
整型 | 0 |
string | “” |
enum | 第一个枚举元素的值,默认值为0 |
message | DEFAULT_INSTANCE |
标识号
在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号的范围是[0, 2^29-1]。
// 传输的对象
message User {string username = 1;int32 age = 2;optional string passwd = 3;repeated string address = 4;
}
嵌套消息
message Student
{message BoyStudent {string name = 1;int32 age = 2;repeated int32 height = 3;}repeated BoyStudent boy = 1;
}
message StudentMsg
{Student.BoyStudent boy = 1;
}
定义接口
service AccessService {// rpc 服务的函数名 (传入参数) 返回 (返回参数)rpc ListDevices(ListRequest) returns (ListResponse);
}
gRPC
概念
概念
gRPC是由开发的一个高性能、通用的开源RPC
框架,主要面向移动应用开发且基于HTTP/2
协议标准而设计,同时支持大多数流行的编程语言。
理念
定义一个服务,指定其能被远程调用的方法(包括参数和返回类型),在服务端实现这个接口,并运行一个gRPC服务器来处理客户端调用。客户端拥有一个存根能够保存像服务端一样的方法。
框架
安装
1)查看GOPATH的路径
go env
2)下载grpc源码
cd $GOPATHmkdir -p src/google.golang.orgcd src/google.golang.org/git clone https://github.com/grpc/grpc-gomv grpc-go grpc
3)下载grpc依赖1
cd $GOPATHmkdir -p src/golang.org/xcd src/golang.org/x/git clone https://github.com/golang/net
git clone https://github.com/golang/text
4)下载grpc依赖2
cd $GOPATHcd src/google.golang.org/git clone https://github.com/google/go-genprotomv go-genproto genproto
依赖需要下载完整,否则使用时报错!!!
实例
1)创建.proto文件
syntax = "proto3";option go_package="../service";package service;message ProductRequest {int32 prod_id = 1;
}message ProductReponse {int32 prod_stock = 1;
}// 定义接口
service ProdService {rpc GetProductStock(ProductRequest) returns(ProductReponse);
}
2)编译.proto文件
// 指定grpc插件
protoc --go_out=plugins=grpc:./ Product.proto
3)创建接口实现文件
// product.gopackage serviceimport "context"var ProductService = &productService{}type productService struct {}// 接口实现
func (p *productService) GetProductStock(context context.Context, request *ProductRequest) (*ProductReponse, error) {stock := p.GetStockById(request.ProdId)return &ProductReponse{ProdStock: stock}, nil
}func (p *productService) GetStockById(id int32) int32 {return 100
}
4)创建服务端
// grpc_server.gopackage mainimport ("service""google.golang.org/grpc""net""log""fmt"
)func main() {// 创建rpc实例rpcServer := grpc.NewServer()// 服务注册service.RegisterProdServiceServer(rpcServer, service.ProductService)// 启动监听listener, err := net.Listen("tcp", ":8800")if err != nil {log.Fatal("启动监听失败", err)}// 启动服务err = rpcServer.Serve(listener)if err != nil {log.Fatal("启动服务失败", err)}fmt.Println("启动服务成功")
}
5)创建客户端
// grpc_client.gopackage mainimport ("google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""service""context""fmt"
)func main() {// 创建连接conn, err := grpc.Dial(target:":8800", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatal("服务端连接失败: ", err)}// 退出时关闭连接defer conn.Close()// 创建客户端实例productServiceClient := service.NewProdServiceClient(conn)// 方法请求resq, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})if err != nil {log.Fatal("调用gRPC方法失败: ", err)} fmt.Println("调用gRPC方法成功, ProdStock = ", resq.ProdStock)
}
6)测试
启动grpc_server,开始监听。
go run grpc_server.go
启动grpc_client,连接成功。
go run grpc_client.go
打印如下:
7)工程目录
部分参考:
rpc介绍:http://wjhsh.net/GreenForestQuan-p-11543779.html
HTTP协议:https://blog.csdn.net/qq_34827674/article/details/104732605
grpc-web:https://www.cncf.io/blog/2018/10/24/grpc-web-is-going-ga/
Protobuf官网:https://developers.google.com/protocol-buffers/docs/proto3
gRPC官网:https://grpc.io
grpc介绍(一)——rpc、protobuf和grpc相关推荐
- Wireshark Protobuf 和 gRPC 内置解析器使用介绍
Wireshark Protobuf 和 gRPC 内置解析器使用介绍 目录 Wireshark Protobuf 和 gRPC 内置解析器使用介绍 1. 主要功能 2. 示例中使用的.proto文件 ...
- 深入理解gRPC(一):gRPC介绍
背景 随着微服务架构和云原生架构的出现,传统的单体应用程序被分解为一组细粒度的.自治的和面向业务能力的"微服务",网络通信链路的数量激增,进程间(或服务间/应用程序间)通信技术也因 ...
- go语言rpc,grpc介绍
目录 rpc RPC调用 net/rpc RPC over HTTP 和 RESTful server client RPC over TCP 和 RESTful server client 序列化/ ...
- gRPC创建Java RPC服务
1.说明 本文介绍使用gRPC创建Java版本的RPC服务, 包括通过.proto文件生成Java代码的方法, 以及服务端和客户端代码使用示例. 2.创建生成代码工程 创建Maven工程,grpc-c ...
- go grpc压缩_跟我学 gRPC—1. gRPC 及相关介绍
Go语言中文网,致力于每日分享编码.开源等知识,欢迎关注我,会有意想不到的收获! 项目地址:https://github.com/EDDYCJY/go-grpc-example 作为开篇章,将会介绍 ...
- 漫谈grpc 4:grpc和其他rpc框架的横向对比,到底好在哪里?
1,什么是grpc gRpc 是一个高性能.开源和通用的 RPC 框架,面向移动和 HTTP/2 设计.目前提供 C.Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc- ...
- java rpc 框架 常用_常用的RPC架构系列---gRPC
gRPC是谷歌的一个高性能,开源的高性能 RPC 框架,gRPC面向移动和HTTP/2设计.gRPC隐藏了底层的实现细节,包括序列化(json,xml),数据传输(TCP,HTTP,UDP),反序列化 ...
- grpc 报错 rpc: the client connection is closing
grpc 报错 rpc: the client connection is closing 第一次写golang微服务,也是第一次接触gprc,底层原理还不太了解,盲猜跟openFeign差不多生成代 ...
- gRPC(2)- PHP使用gRPC
PHP使用GRPC 前言 下载Protoc 编写proto文件 代码实现 准备 服务端服务实现 客户端请求实现 前言 在上节已经介绍了GRPC,不了解GRPC的可以先看下. 本文将讲述PHP如何使用G ...
最新文章
- 变量相关命令(env,export,set,read, array, declare)
- 启动子级时出错_【本音知识】弹钢琴时如何背谱?
- UVA11992不错的线段树段更新
- iOS设计模式——MVC(Model-View-Controller)
- 从代码到 Docker、Kubernetes、Istio、Knative……,或许是时候重新思考从代码到云的编程了...
- Kali Linux 网络扫描秘籍 第六章 拒绝服务(一)
- 向Excle中插入多个表
- DXUT框架剖析(13)
- Momentum(动量/冲量)的理解及应用
- HTML中视频的压缩方式,快速将视频压缩到最小的技巧!
- 前端纯CSS导入otf字体包
- 七剑下天山,独领自动化测试技术
- BAT疯狂抢人, AI应届博士生年薪201万, 网友: 转行来得及吗???
- arduino 328P的BootLoader
- js阿拉伯数字转中文汉字小写 支持到12位
- mysql 轨迹数据存储_基于Tablestore实现海量运动轨迹数据存储
- 黑镜.潘达斯奈基 高清中字
- 世界上第一台计算机内存容量,29、世界上第一台电子计算机ENIAC诞生于.doc
- Android系统的编舞者Choreographer
- Android Camera 四 Camera HAL 分析
热门文章
- 中华英才网爬虫程序解析(4)-分布式爬虫redis
- 2020北京考研英语一80+经验
- CRC校验原理的完整学习
- 小程序:Thu May 05 2022 11:03:00 GMT+0800 (中国标准时间) 渲染层错误
- 2022-07-10 第七小组 闫馨月 学习笔记
- redis-删除所有key
- 时间序列预测基础教程系列(14)_如何判断时间序列数据是否是平稳的(Python)
- 理解 OpenStack + Ceph (3):Ceph RBD 接口和工具 [Ceph RBD API and Tools]
- MODIS NDVI下载处理 MOD13A1, win10
- tomcat 7.0