您好,登錄后才能下訂單哦!
gRPC的原理是什么,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
gRPC是什么?可以用官網(wǎng)的一句話來概括:A high-performance, open-source universal RPC framework。
所謂RPC(remote procedure call 遠(yuǎn)程過程調(diào)用)框架實(shí)際是提供了一套機(jī)制,使得應(yīng)用程序之間可以進(jìn)行通信,而且也遵從server/client模型。使用的時(shí)候客戶端調(diào)用server端提供的接口就像是調(diào)用本地的函數(shù)一樣。如下圖所示就是一個(gè)典型的RPC結(jié)構(gòu)圖。
gRPC和restful API都提供了一套通信機(jī)制,用于server/client模型通信,而且它們都使用http作為底層的傳輸協(xié)議(嚴(yán)格地說,gRPC使用的http2.0,而restful api則不一定)。不過gRPC還是有些特有的優(yōu)勢(shì),如下:
gRPC可以通過protobuf來定義接口,從而可以有更加嚴(yán)格的接口約束條件。關(guān)于protobuf可以參見下期Protobuf簡明教程,另外,通過protobuf可以將數(shù)據(jù)序列化為二進(jìn)制編碼,這會(huì)大幅減少需要傳輸?shù)臄?shù)據(jù)量,從而大幅提高性能。
gRPC可以方便地支持流式通信(理論上通過http2.0就可以使用streaming模式, 但是通常web服務(wù)的restful api似乎很少這么用,通常的流式數(shù)據(jù)應(yīng)用如視頻流,一般都會(huì)使用專門的協(xié)議如HLS,RTMP等,這些就不是我們通常web服務(wù)了,而是有專門的服務(wù)器應(yīng)用。)
需要對(duì)接口進(jìn)行嚴(yán)格約束的情況,比如我們提供了一個(gè)公共的服務(wù),很多人,甚至公司外部的人也可以訪問這個(gè)服務(wù),這時(shí)對(duì)于接口我們希望有更加嚴(yán)格的約束,我們不希望客戶端給我們傳遞任意的數(shù)據(jù),尤其是考慮到安全性的因素,我們通常需要對(duì)接口進(jìn)行更加嚴(yán)格的約束。這時(shí)gRPC就可以通過protobuf來提供嚴(yán)格的接口約束。
對(duì)于性能有更高的要求時(shí)。有時(shí)我們的服務(wù)需要傳遞大量的數(shù)據(jù),而又希望不影響我們的性能,這個(gè)時(shí)候也可以考慮gRPC服務(wù),因?yàn)橥ㄟ^protobuf我們可以將數(shù)據(jù)壓縮編碼轉(zhuǎn)化為二進(jìn)制格式,通常傳遞的數(shù)據(jù)量要小得多,而且通過http2我們可以實(shí)現(xiàn)異步的請(qǐng)求,從而大大提高了通信效率。
但是,通常我們不會(huì)去單獨(dú)使用gRPC,而是將gRPC作為一個(gè)部件進(jìn)行使用,這是因?yàn)樵谏a(chǎn)環(huán)境,我們面對(duì)大并發(fā)的情況下,需要使用分布式系統(tǒng)來去處理,而gRPC并沒有提供分布式系統(tǒng)相關(guān)的一些必要組件。而且,真正的線上服務(wù)還需要提供包括負(fù)載均衡,限流熔斷,監(jiān)控報(bào)警,服務(wù)注冊(cè)和發(fā)現(xiàn)等等必要的組件。不過,這就不屬于本篇文章討論的主題了,我們還是先繼續(xù)看下如何使用gRPC。
通過protobuf來定義接口和數(shù)據(jù)類型
編寫gRPC server端代碼
編寫gRPC client端代碼
本文使用golang去實(shí)現(xiàn)demo,其中protobuf和grpc擴(kuò)展的安裝就跳過了。
新建userrpc.proto
syntax = "proto3"; package user; option go_package = "./grpc/user"; // The User service definition. service User { // Get all Users with id - A server-to-client streaming RPC. rpc GetUsers(UserFilter) returns (stream UserRequest) {} // Create a new User - A simple RPC rpc CreateUser (UserRequest) returns (UserResponse) {} } // Request message for creating a new user message UserRequest { int32 id = 1; // Unique ID number for a User. string name = 2; string email = 3; string phone= 4; message Address { string province = 1; string city = 2; } repeated Address addresses = 5; } message UserResponse { int32 id = 1; bool success = 2; } message UserFilter { int32 id = 1; }
編譯 .proto 文件
protoc --go_out=plugins=grpc:. userrpc.proto
package main import ( "log" "net" "golang.org/x/net/context" "google.golang.org/grpc" pb "userrpc/grpc/user" ) const ( port = ":50051" ) // server is used to implement user.UserServer. type server struct { savedUsers []*pb.UserRequest } // CreateUser creates a new User func (s *server) CreateUser(ctx context.Context, in *pb.UserRequest) (*pb.UserResponse, error) { s.savedUsers = append(s.savedUsers, in) return &pb.UserResponse{Id: in.Id, Success: true}, nil } // GetUsers returns all users by given id func (s *server) GetUsers(filter *pb.UserFilter, stream pb.User_GetUsersServer) error { for _, user := range s.savedUsers { if filter.Id == 0 { continue } if err := stream.Send(user); err != nil { return err } } return nil } func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } // Creates a new gRPC server s := grpc.NewServer() pb.RegisterUserServer(s, &server{}) s.Serve(lis) }
客戶端client.go
package main import ( "io" "log" "golang.org/x/net/context" "google.golang.org/grpc" pb "userrpc/grpc/user" ) const ( address = "localhost:50051" ) // createUser calls the RPC method CreateUser of UserServer func createUser(client pb.UserClient, user *pb.UserRequest) { resp, err := client.CreateUser(context.Background(), user) if err != nil { log.Fatalf("Could not create User: %v", err) } if resp.Success { log.Printf("A new User has been added with id: %d", resp.Id) } } // getUsers calls the RPC method GetUsers of UserServer func getUsers(client pb.UserClient, id *pb.UserFilter) { // calling the streaming API stream, err := client.GetUsers(context.Background(), id) if err != nil { log.Fatalf("Error on get users: %v", err) } for { user, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("%v.GetUsers(_) = _, %v", client, err) } log.Printf("User: %v", user) } } func main() { // Set up a connection to the gRPC server. conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() // Creates a new UserClient client := pb.NewUserClient(conn) user := &pb.UserRequest{ Id: 1, Name: "test", Email: "fasd@163.com", Phone: "132222222", Addresses: []*pb.UserRequest_Address{ &pb.UserRequest_Address{ Province: "hebei", City: "shijiazhuang", }, }, } // Create a new user createUser(client, user) // Filter with an id filter := &pb.UserFilter{Id: 1} getUsers(client, filter) }
啟動(dòng)server.go
go run server.go
新打開一個(gè)窗口,啟動(dòng)client.go
go run client.go
結(jié)果為
2019/07/04 17:01:16 A new User has been added with id: 1 2019/07/04 17:01:16 User: id:1 name:"test">
Api實(shí)現(xiàn)起來比較繁瑣,給開發(fā)帶來難度。總的來說gRPC是一個(gè)不錯(cuò)的跨語言rpc解決方案,當(dāng)然每個(gè)人都自己的看法或見解。針對(duì)不同的業(yè)務(wù)場(chǎng)景采用不同的解決方案,最終都是運(yùn)行效率和開發(fā)效率的相互妥協(xié)的結(jié)果。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。