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的認證
技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
我是小魔童哪吒,歡迎點贊關注收藏,下次見~
本作品採用《CC 協議》,轉載必須註明作者和本文連結