一. gRPC 是什么
- 官網(wǎng)簡(jiǎn)介: A high-performance, open-source universal RPC framework
- gRPC 是一個(gè)高性能的, 開源統(tǒng)一的 RPC 框架
- RPC (Remote Procedure Calls), 是指遠(yuǎn)程過程調(diào)用
- 包含了傳輸協(xié)議和編碼 (對(duì)象序列號(hào)) 協(xié)議
- 服務(wù)的程序 A 調(diào)用另一臺(tái)服務(wù)的程序 B
- 除了 gRPC 框架之外, 還有
net/rpc
(go 標(biāo)準(zhǔn)庫(kù)),Thrift
(C++),Motan
,Dubbo
(Java) 等 RPC 框架.- 衍生出來的框架一般都具備簡(jiǎn)單, 通用, 安全, 效率等特點(diǎn)
- 是基于 http 開發(fā)的, 不同的是 RPC:
- 是長(zhǎng)鏈接, 不必每次通信都要像 HTTP 一樣去 3 次握手什么的, 減少了網(wǎng)絡(luò)開銷
- 對(duì)數(shù)據(jù)進(jìn)行深度編碼和解碼, 減少了對(duì)網(wǎng)絡(luò)帶寬的壓力
- 一般都有注冊(cè)中心, 有豐富的監(jiān)控管理
- 發(fā)布、下線接口赏枚、動(dòng)態(tài)擴(kuò)展等, 對(duì)調(diào)用方來說是無感知枚冗、統(tǒng)一化的操作
- 參考: 有了HTTP,為什么還要RPC?
二. gRPC 的用途
- 一般應(yīng)用于大型的系統(tǒng), 提高性能和效率
- gRPC 是微服務(wù)架構(gòu)中, 首選的 RPC 框架之一
三. gRPC 的特點(diǎn)有哪些特點(diǎn), 為什么要使用它
- 支持多種語(yǔ)言
- 輕量級(jí), 高性能
- 序列化支持 Protocol Buffer 和 Json
- Json 是由 JavaScript 動(dòng)態(tài)語(yǔ)言推廣出來的, 特性是隨著 JavaScript, 字段和數(shù)據(jù)類型是可以隨意定義的
- Protocol Buffer 的字段和數(shù)據(jù)類型是預(yù)定義的, 更適合靜態(tài)語(yǔ)言, 更安全. 另外 Protocol Buffer 是一種語(yǔ)言無關(guān)的高性能序列化框架
- Protocol Buffer 可以加速站點(diǎn)之間數(shù)據(jù)傳輸速度, 解決數(shù)據(jù)傳輸不規(guī)范的問題
- 可插拔
- IDL
- 基于文件定義服務(wù), 通過 proto3 工具生成指定語(yǔ)言的數(shù)據(jù)結(jié)構(gòu)撵溃、服務(wù)端接口以及客戶端 Stub
- 基于標(biāo)準(zhǔn)的 HTTP2 設(shè)計(jì)
- 支持雙向流邢笙、消息頭壓縮、單 TCP 的多路復(fù)用腥寇、服務(wù)端推送等特性
- 這些特性使得 gRPC 在移動(dòng)端設(shè)備上更加省電和節(jié)省網(wǎng)絡(luò)流量
- 服務(wù)而非對(duì)象成翩、消息而非引用
- 有利于解耦
- 促進(jìn)微服務(wù)的系統(tǒng)間, 粗粒度的消息交互
- 負(fù)載無關(guān)
- 不同的服務(wù)需要使用不同的消息類型和編碼
- 阻塞式和非阻塞式
- 支持異步和同步處理在客戶端和服務(wù)端間交互的消息序列
- 元數(shù)據(jù)交換
- 常見的橫切關(guān)注點(diǎn), 依賴數(shù)據(jù)交換
- 標(biāo)準(zhǔn)化狀態(tài)碼
- 客戶端通常以有限的方式響應(yīng) API 調(diào)用返回的錯(cuò)誤
- 良好的設(shè)計(jì)理念
- 生態(tài)好
四. gRPC 實(shí)踐
目標(biāo): 一步一步實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 gRPC 示例
1. 開發(fā)環(huán)境
- macOS
- go 1.15
2. 安裝依賴和環(huán)境配置
- 安裝 protobuf 工具
# 使用 brew 命令
# 安裝 protobuf, 即安裝 protoc 可執(zhí)行文件/命令
brew install protobuf
- 安裝 go-gRPC 工具, 配置環(huán)境
# 安裝 Go-gRPC 的編譯器插件
# 用于生成 *.pb.go 和 *_grpc.pb.go 兩個(gè)文件所需的 (在 $GOPATH/bin 下)
go get google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc
# go mod
export GO111MODULE=on # Enable module mode
# protoc 編譯器可以找到的插件路徑
export PATH="$PATH:$(go env GOPATH)/bin"
3. 編寫和編譯 proto
sayhello.proto
// 指定使用 proto3 版本
syntax = "proto3";
// 隨便定義一個(gè)報(bào)名
package grpc.service.sayhello;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
- 編譯
# --go_out 參數(shù)是指定生成 *.pb.go 文件的路徑
# --go-grpc_out 參數(shù)是指定生成 *_grpc.pb.go 文件的路徑
# -I, 相當(dāng)于 -IPATH 和 --proto_path, 指定 proto 文件依賴的包所要搜索的路徑 (這里沒有依賴, 可以不寫)
protoc -I. --go_out=. --go-grpc_out=. *.proto
- 編譯報(bào)錯(cuò)
protoc-gen-go: unable to determine Go import path for
Please specify either:
? a "go_package" option in the .proto source file, or
? a "M" argument on the command line.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.
--go_out: protoc-gen-go: Plugin failed with status code 1.
要我們定義 go_package 或者 M 命令行參數(shù)
- 選擇修改 proto, 定義 go_package
syntax = "proto3";
// ./ 是文件生成的路徑
// sayhello_proto 是生成的 .go 文件的包名
// 兩者之間, 用 ; 號(hào)分隔
// 參考: https://blog.csdn.net/raoxiaoya/article/details/109533734
option go_package = "./;sayhello_proto";
package grpc.service.sayhello;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
- 重新編譯 proto
protoc -I. --go_out=. --go-grpc_out=. *.proto
編譯成功
4. 實(shí)現(xiàn)服務(wù)端
package main
import (
"context"
"log"
"net"
proto "github.com/-/sayhellogrpc/proto" // 此處隱姓埋名
"google.golang.org/grpc"
)
const (
// 綁定的端口
port = ":8080"
)
type server struct {
proto.UnimplementedGreeterServer
}
// 實(shí)現(xiàn)定義的 SayHello API
// 參數(shù)和返回類型, 參考生成的 sayhello_grpc.pb.go 的
func (s *server) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
msg := in.GetName() + " say hello for gRPC"
reply := &proto.HelloReply{
Message: msg,
}
return reply, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
srv := grpc.NewServer()
// 注冊(cè)服務(wù)
proto.RegisterGreeterServer(srv, &server{})
log.Println("gRPC server is running...")
// 起服務(wù)
if err := srv.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
5. 實(shí)現(xiàn)客戶端
package main
import (
"context"
"log"
"os"
"time"
proto "github.com/-/sayhellogrpc/proto" // 此處隱姓埋名
"google.golang.org/grpc"
)
const (
address = "localhost:8080"
content = "xxxx"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := proto.NewGreeterClient(conn)
// Contact the server and print out its response.
name := content
if len(os.Args) > 1 {
// 通過命令行獲取發(fā)送的內(nèi)容
name = os.Args[1]
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &proto.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
go.mod
如果報(bào) undefined: grpc.SupportPackageIsVersion7
這樣的錯(cuò)誤, 則要修改 google.golang.org/grpc
的版本, v1.32.0
及以上
module github.com/-/sayhellogrpc // 此處隱姓埋名
go 1.15
require (
github.com/golang/protobuf v1.5.0
google.golang.org/grpc v1.37.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 // indirect
google.golang.org/protobuf v1.26.0
)
文件結(jié)構(gòu)
.
├── client
│ └── main.go
├── go.mod
├── go.sum
├── proto
│ ├── sayhello.pb.go
│ ├── sayhello.proto
│ └── sayhello_grpc.pb.go
└── server
└── main.go
6. 驗(yàn)證
go run server/main.go
go run client/main.go
go run client/main.go abc
實(shí)現(xiàn)參考: https://github.com/grpc/grpc-go/tree/master/examples