技术介绍
gRPC
是由 google
开发的一个高性能、通用的开源RPC
框架,主要面向移动应用开发且基于HTTP/2
协议标准而设计,同时支持大多数流行的编程语言。
这里有几个关键词
- google开发 大公司背景 相对于dubbo2 rpc框架,grpc提供了跨语言的调用 生态更加完整
- HTTP/2 ,底层基于HTTP2进行数据通信,因此天然支持HTTP REST API 、GRPC接口调用转换
Quick Start
官网Example
- 下载源码代码
git clone https://github.com/grpc/grpc-go.git
- 下载依赖
// cd grcp-go
go mod tidy
- example 地址
如图,上面的代码案例 展示grpc的入门以及各种特性应用的demo,后面一个个学习下。今天学下下grpc入门级别的demo。代码谓语helloworld目录下
服务端代码
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
客户端代码
package main
import (
"context"
"flag"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
代码结构如下
AndydeMacBook-Pro:helloworld andy$ tree
.
├── greeter_client
│ └── main.go
├── greeter_server
│ └── main.go
└── helloworld
├── helloworld.pb.go
├── helloworld.proto
└── helloworld_grpc.pb.go
执行结果
- 启动服务器
- 启动客户端
- 输出结果
服务端:
客户端:
至此,一个简单的grpc远程调用执行完毕, 接下来使用抓包工具Wireshark 分析下如何利用http2进行数据通信,以及http2数据通信的一些细节。关于Wireshark抓包工具的安装这里不做介绍了。
抓包分析
抓包步骤
选择网卡
由于Quick Start的demo只是在本机做远程调用,因此选择 loopback 回环网卡,双击进入
过滤参数
为了防止其他数据的干扰,过滤来源、目标端口为50051的数据即可。过滤参数为:tcp.dstport50051 or tcp.srcport50051。并重新启动客户端。
如上图,已经抓到了一次grpc调用的完整的数据包,只是上述的数据偏向底层,无法直观的进行分析,幸运的是wireshark支持HTTP2协议,只需要选择合适的编码就可以让数据展示更加直观。
选择HTTP2编码
- 选中任一数据行,右键选择编码
- 选择HTTP2编码
- 展示最终结果
协议分析
结论:
-
HTTP2 在TCP链接的基础上多了几次数据交互的过程
-
在GRPC中 所有的请求默认都转换为HTTP POST请求
-
POST请求中的URI对应为protobuf文件中定义/package/class/method
-
一次HTTP请求 将header、body分开发送
ERROR vs HTTP STATUS CODE
GRPC异常 与 HTTP状态码对应关系如下:
HTTP Status Code | GRPC Status Code |
---|---|
400 Bad Request | INTERNAL |
401 Unauthorized | UNAUTHENTICATED |
403 Forbidden | PERMISSION_DENIED |
404 Not Found | UNIMPLEMENTED |
429 Too Many Requests | UNAVAILABLE |
502 Bad Gateway | UNAVAILABLE |
503 Service Unavailable | UNAVAILABLE |
504 Gateway Timeout | UNAVAILABLE |
All other codes | UNKNOWN |
最佳性能实践
摘自官方文档:
- 尽可能的重用利用Channel
- 保持HTTP2底层TCP连接处于活跃状态,即利用心跳检测保持长链接(即C++通道参数 GRPC_arg_keepalive_TIME_MS)
- 客户端、服务端双向通信使用 streaming RPC
- 每个gRPC通道使用0个或更多HTTP/2连接,并且每个连接通常对并发流的数量有限制。当连接上的活动RPC数达到此限制时,其他RPC将在客户端中排队,并且必须等待活动RPC完成后才能发送。具有高负载或长寿命流式RPC的应用程序可能会因为这种排队而出现性能问题。有两种可能的解决方案
- 每个高负载区域创建独立的Channel通道
- 使用 gRPC 连接池