[TOC]
gRPC
gRPC介绍
gRPC是什么?
RPC和RESTful的区别是什么?
RPC的消息传输可以是TCP,可以是UDP,也可以是HTTP,当RPC消息传输是HTTP时,它的结构与RESTful的架构类似
RPC和RESTful有什么不同呢:
- 操作的对象不一样的,RESTful会更加灵活
RPC操作的是方法对象, RESTful操作的是资源
RPC的客户端和服务器端是紧耦合的,客户端需要知道服务端的函数名字,参数类型、顺序等,才能远程过程调用。
RESTful基于 http的语义操作资源,参数的顺序一般没有关系
- RCP更适合定制化
- RESTful执行的是对资源的操作,主要都是CURD(增删改查)的操作,若需要实现一个特定的功能,如计算一个班级的平均分,这个时候使用RPC定义服务器的方法(如:Stu.CalAvg)供客户端调用则显得更有意义
gRPC的特性是什么?
- gRPC是可以跨语言开发的
在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地过程调用一样,很容易去构建分布式应用和服务。客户端和服务端可以分别使用gRPC支持的不同语言实现。
- 基于HTTP2标准设计,比其他框架更优的地方有
- 支持
长连接,双向流、头部压缩、多复用请求
等 节省带宽
、降低TCP链接次数
、节省CPU使用
和延长电池寿命
- 提高了云端服务和Web应用的性能
- 客户端和服务端交互透明
- gRPC默认使用protobuf来对数据序列化
gRPC的数据交互模式是怎么样的?
请求应答式
客户端发出一次请求,可以从服务端读取一系列的消息
客户端写一系列消息给到服务端,等待服务端应答
客户端和服务端都可以通过读写数据流来发送一系列消息
数据的序列化方式 - protobuf
protobuf 是一个对数据序列化的方式,类似的有JSON,XML等
简单介绍protobuf的结构定义包含的3个关键字
- 以.proto做为后缀,除结构定义外的语句以分号结尾
- 结构定义可以包含:message、service、enum,三个关键字
- rpc方法定义结尾的分号可有可无
Message命名采用驼峰命名方式,字段是小写加下划线
message ServerRequest { | |
required string my_name = 1;} |
Enums类型名采用驼峰命名方式,字段命名采用大写字母加下划线
enum MyNum {VALUE1 = 1;VALUE2 = 2;}
Service与rpc方法名统一采用驼峰式命名
service Love {// 定义Confession方法 | |
rpc MyConfession(Request) returns (Response) {} | |
} |
关于prtobuf的安装可以看看之前写的一个安装步骤《5个步骤搞定PROTOBUF的安装》
在proto文件中使用package
关键字声明包名,默认转换成go中的包名与此一致,可以自定义包名,修改go_package
即可:
test.proto
syntax = "proto3"; // proto版本 | |
package pb; // 指定包名,默认go中包名也是这个 | |
// 定义Love服务 | |
service Love {// 定义Confession方法 | |
rpc Confession(Request) returns (Response) {} | |
} | |
// 请求 | |
message Request { | |
string name = 1; | |
} | |
// 响应 | |
message Response { | |
string result = 1; | |
} |
安装好protoc环境之后,进入到proto文件的目录下,(例如打开window git
)执行如下命令,将proto
文件编译成pb.go
文件
protoc --go_out=plugins=grpc:. test.proto
转换结果:
// Code generated by protoc-gen-go. DO NOT EDIT. | |
// versions: | |
// protoc-gen-go v1.25.0 | |
// protoc v3.13.0 | |
// source: test.proto | |
package test | |
import ( | |
context "context" | |
proto "github.com/golang/protobuf/proto" | |
grpc "google.golang.org/grpc" | |
codes "google.golang.org/grpc/codes" | |
status "google.golang.org/grpc/status" | |
protoreflect "google.golang.org/protobuf/reflect/protoreflect" | |
protoimpl "google.golang.org/protobuf/runtime/protoimpl" | |
reflect "reflect" | |
sync "sync" | |
) | |
const (// Verify that this generated code is sufficiently up-to-date. | |
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)// Verify that runtime/protoimpl is sufficiently up-to-date. | |
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) | |
) | |
// This is a compile-time assertion that a sufficiently up-to-date version | |
// of the legacy proto package is being used. | |
const _ = proto.ProtoPackageIsVersion4 | |
type Request struct { | |
state protoimpl.MessageState | |
sizeCache protoimpl.SizeCache | |
unknownFields protoimpl.UnknownFields | |
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` | |
} | |
type Response struct { | |
state protoimpl.MessageState | |
sizeCache protoimpl.SizeCache | |
unknownFields protoimpl.UnknownFields | |
Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` | |
} | |
// LoveServer is the server API for Love service. | |
type LoveServer interface {Confession(context.Context, *Request) (*Response, error) | |
} |
一个DEMO
目录结构为:
------------------------------- | |
| mygrpc | |
| ---------pb | |
| -------------test.proto | |
| ---------client.go | |
| ---------srv.go | |
------------------------------- |
client.go
package main | |
import ("context""log" | |
"mygrpc.com/pb" | |
"google.golang.org/grpc" | |
) | |
func main() {// 连接grpc服务 | |
conn, err := grpc.Dial(":8888", grpc.WithInsecure())if err != nil { | |
log.Fatal(err)}// 很关键defer conn.Close() | |
// 初始化客户端 | |
c := pb.NewLoveClient(conn) | |
// 发起请求 | |
response, err := c.Confession(context.Background(), &pb.Request{Name: "小魔童哪吒"})if err != nil { | |
log.Fatal(err)} | |
log.Println(response.Result) | |
} |
server.go
package main | |
import ("context""log""net" | |
"google.golang.org/grpc""mygrpc.com/pb" | |
) | |
// 定义Love服务 | |
type Love struct { | |
} | |
// 实现Love服务接口 | |
func (l *Love) Confession(ctx context.Context, request *pb.Request) (*pb.Response, error) { | |
resp := &pb.Response{} | |
resp.Result = "your name is " + request.Name | |
return resp, nil | |
} | |
func main() {// 监听8888端口 | |
listen, err := net.Listen("tcp", ":8888")if err != nil { | |
log.Fatal(err)} | |
// 实例化grpc server | |
s := grpc.NewServer() | |
// 注册Love服务 | |
pb.RegisterLoveServer(s, new(Love)) | |
log.Println("Listen on 127.0.0.1:8888...") | |
s.Serve(listen) | |
} |
下一次介绍关于gRPC的认证
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是小魔童哪吒,欢迎点赞关注收藏,下次见~