【grpc】1.初识
# grpc 初识
grpc 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支持的语言来编写。所以,你可以很容易地用 Java 创建一个 gRPC 服务端,用 Go、Python、Ruby 来创建客户端。此外,Google 最新 API 将有 gRPC 版本的接口,使你很容易地将 Google 的功能集成到你的应用里。
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。正如你将在下方例子里所看到的,你用 proto files 创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型。
# protobuf
习惯用
Json、XML
数据存储格式的你们,相信大多都没听过Protocol Buffer
Protocol Buffer
其实 是Google
出品的一种轻量 & 高效的结构化数据存储格式,性能比Json、XML
真的强!太!多!protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3
# grpc 和 rpc的区别
gRPC 是一款高性能、开源的 RPC 框架,产自 Google,基于 ProtoBuf 序列化协议进行开发,支持多种语言(Golang、Python、Java等)。gRPC 提供了一种简单的方法来定义服务,同时客户端可以充分利用 HTTP/2 stream 的特性,从而有助于节省带宽、降低 TCP 的连接次数、节省CPU的使用等。
rpc和grpc之间的关系是什么?
- rpc是一种协议,grpc是基于rpc协议实现的一种框架。
grpc的解决rpc三大问题
协议约定。gRPC 的协议是 Protocol Buffers,是一种压缩率极高的序列化协议,Google 在 2008 年开源了 Protocol Buffers,支持多种编程语言,所以 gRPC 支持客户端与服务端可以用不同语言实现。
传输协议。gRPC 的数据传输用的是 Netty Channel, Netty 是一个高效的基于异步 IO 的网络传输架构。Netty Channel 中,每个 gRPC 请求封装成 HTTP 2.0 的 Stream。
服务发现。gRPC 本身没有提供服务发现的机制,需要通过其他组件。
总结
grpc是一种实现了rpc协议的框架,并且分别通过protocol buffer、netty channel 以及服务发现组件解决rpc的协议约定、传输协议、服务发现三大问题。
# go 开发 grpc(旧版本)
# 1. 下载编译工具
- 地址 https://github.com/protocolbuffers/protobuf/releases
注意:protoc的版本需要和golang/protobuf保持一致 (尽量自己去下载最新的版本);下载完成后解压后记得将路径添加到环境变量中
# 2. 下载go的依赖包
go get -u google.golang.org/protobuf
# 3. proto文件
syntax = "proto3";
option go_package = ".;proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4. 生成go文件
protoc -I . helloword.proto --go_out=plugins=grpc:.
# 5. 服务端代码
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpc_demo/hello"
"net"
)
type Server struct {
}
func (s *Server) SayHello(ctx context.Context,request *hello.HelloRequest)(*hello.HelloReply,error){
return &hello.HelloReply{Message:"Hello "+request.Name},nil
}
func main() {
g := grpc.NewServer()
s := Server{}
hello.RegisterGreeterServer(g,&s)
lis, err := net.Listen("tcp", fmt.Sprintf(":8080"))
if err != nil {
panic("failed to listen: "+err.Error())
}
g.Serve(lis)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 6. 客户端
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpc_demo/proto"
)
func main() {
conn,err := grpc.Dial("127.0.0.1:8080",grpc.WithInsecure())
if err!=nil{
panic(err)
}
defer conn.Close()
c := hello.NewGreeterClient(conn)
r,err := c.SayHello(context.Background(),&hello.HelloRequest{Name:"bobby"})
if err!=nil{
panic(err)
}
fmt.Println(r.Message)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 新版本protoc示例
proto文件
protoc 命令: protoc -I . --go_out=. --go-grpc_out=. ./hello.proto
syntax = "proto3"; package proto; option go_package= ".;proto"; service Greeter { rpc Hello (Req) returns (Res); } message Req { string msg = 1; } message Res { string msg = 1; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15server.go
/** * @date: 2023/4/25 * @desc: */ package main import ( helloproto "bigox-rpc/proto" "context" "fmt" "google.golang.org/grpc" "log" "net" ) type GreeterServer struct { helloproto.UnimplementedGreeterServer } func (g *GreeterServer) Hello(ctx context.Context, req *helloproto.Req) (*helloproto.Res, error) { return &helloproto.Res{Msg: req.Msg}, nil } func main() { // 创建 gRPC 服务器 server := grpc.NewServer() // 创建服务 greeterServer := GreeterServer{} // 注册服务 helloproto.RegisterGreeterServer(server, &greeterServer) // 创建tcp lis, err := net.Listen("tcp", fmt.Sprintf(":8080")) if err != nil { panic("failed to listen: " + err.Error()) } // 启动 gRPC 服务器 if err := server.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42client.go
/** * @date: 2023/4/26 * @desc: */ package main import ( helloproto "bigox-rpc/proto" "context" "fmt" "google.golang.org/grpc" ) func main() { conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure()) if err != nil { panic(err) } defer conn.Close() c := helloproto.NewGreeterClient(conn) r, err := c.Hello(context.Background(), &helloproto.Req{Msg: "liushahe"}) if err != nil { panic(err) } fmt.Println(r.Msg) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# grpc 通信方式
gRPC主要有4种请求/响应模式
简单模式(Simple RPC)
服务端数据流模式(Server-side streaming RPC)
客户端数据流模式(Client-side streaming RPC)
双向数据流模式(Bidirectional streaming RPC)
proto文件
syntax = "proto3"; option go_package = ".;proto"; service Greeter{ rpc GetStream(StreamReqData) returns(stream StreamResData) ; // 服务端流模式 rpc PutStream(stream StreamReqData) returns(StreamResData) ; // 客户端流模式 rpc AllStream(stream StreamReqData) returns(stream StreamResData); // 双向数据流模式 } message StreamReqData{ string data = 1; } message StreamResData{ string data = 1; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 简单模式
- 简单请求响应
# 2. 服务端数据流模式
- 这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。
- 代码见双向数据流
# 3. 客户端数据流模式
与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据。
代码见双向数据流
# 4. 双向数据流模式
proto文件
syntax = "proto3"; option go_package = ".;proto"; service Greeter{ rpc GetStream(StreamReqData) returns(stream StreamResData) ; // 服务端流模式 rpc PutStream(stream StreamReqData) returns(StreamResData) ; // 客户端流模式 rpc AllStream(stream StreamReqData) returns(stream StreamResData); // 双向数据流模式 } message StreamReqData{ string data = 1; } message StreamResData{ string data = 1; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15server
/* * @date: 2021/10/27 * @desc: ... */ package main import ( "fmt" "google.golang.org/grpc" "net" "picturePro/grpcStreamTest/proto" "sync" "time" ) const ADDR = ":50052" type server struct { } // GetStream 服务端数据流模式 func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error { i := 0 for { i++ _ = res.Send(&proto.StreamResData{ Data: fmt.Sprintf("date: %v", time.Now().Unix()), }) time.Sleep(time.Second) if i > 10 { break } } return nil } // PutStream 客户端数据流模式 func (s *server) PutStream(clientStr proto.Greeter_PutStreamServer) error { for { if a, err := clientStr.Recv(); err != nil { fmt.Println("er:", err) break } else { fmt.Println(a.Data) } } return nil } func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error { wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() for { data, _ := allStr.Recv() fmt.Println("收到客户端消息:" + data.Data) } }() go func() { defer wg.Done() for { _ = allStr.Send(&proto.StreamResData{Data: "我是服务器"}) time.Sleep(time.Second) } }() wg.Wait() return nil } func main() { listen, err := net.Listen("tcp", ADDR) if err != nil { panic(err.Error()) } s := grpc.NewServer() proto.RegisterGreeterServer(s, &server{}) err = s.Serve(listen) if err != nil { return } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86client
/* * @date: 2021/10/27 * @desc: ... */ package main import ( "context" "fmt" "google.golang.org/grpc" "picturePro/grpcStreamTest/proto" "sync" "time" ) func main() { conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure()) if err != nil { return } defer conn.Close() c := proto.NewGreeterClient(conn) // 服务端流模式 res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "hello"}) for { a, err := res.Recv() if err != nil { fmt.Println(err.Error()) break } fmt.Println(a.Data) } // 客户端流模式 puts, _ := c.PutStream(context.Background()) for i := 0; i < 10; i++ { err = puts.Send(&proto.StreamReqData{ Data: fmt.Sprintf("test %v", i), }) if err != nil { fmt.Println(err.Error()) } time.Sleep(time.Second) } //双向流模式 allStr, _ := c.AllStream(context.Background()) wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() for { data, _ := allStr.Recv() fmt.Println("收到客户端消息:" + data.Data) } }() go func() { defer wg.Done() for { _ = allStr.Send(&proto.StreamReqData{Data: "慕课网"}) time.Sleep(time.Second) } }() wg.Wait() }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# gRPC 优势
与HTTP(Restful API)对比,gRPC的优势
- gRPC和restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,而restful api则不一定)。不过gRPC还是有些特有的优势,如下:
- gRPC可以通过protobuf来定义接口,可以有更加严格的接口约束条件,支持多种语言。
- protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高传输速度。
- gRPC可以支持streaming流式通信(http2.0),提高传输速度。
# gRPC 功能
- 上下文信息传递
- 健康检查
- keepalive
- 命名解析
- 中间件、拦截器
- 负载均衡
- 身份认证
- 超时重试
- 链路追踪 tracing
# gRPC 组件
- grpc 只是实现了 RPC 核心功能,缺少很多微服务的特性(服务注册发现、监控、治理、管理等),而基于 HTTP/2 相对来说比较容易进行扩展。
- grpc-ecosystem (opens new window)
- awesome-grpc (opens new window)
# gRPC 应用场景
- 低延迟、高扩展的分布式系统
- 与云服务通信
- 设计一个需要准确,高效且与语言无关的新协议
- 分层设计,以实现扩展,例如:身份验证,负载平衡,日志记录和监控等