共計 4664 個字符,預計需要花費 12 分鐘才能閱讀完成。
gRPC 的原理是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面丸趣 TV 小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
什么是 gRPC
gRPC 是什么?可以用官網的一句話來概括:A high-performance, open-source universal RPC framework。
所謂 RPC(remote procedure call 遠程過程調用) 框架實際是提供了一套機制,使得應用程序之間可以進行通信,而且也遵從 server/client 模型。使用的時候客戶端調用 server 端提供的接口就像是調用本地的函數一樣。如下圖所示就是一個典型的 RPC 結構圖。
gRPC vs Restful API
gRPC 和 restful API 都提供了一套通信機制,用于 server/client 模型通信,而且它們都使用 http 作為底層的傳輸協議 (嚴格地說,gRPC 使用的 http2.0,而 restful api 則不一定)。不過 gRPC 還是有些特有的優勢,如下:
gRPC 可以通過 protobuf 來定義接口,從而可以有更加嚴格的接口約束條件。關于 protobuf 可以參見下期 Protobuf 簡明教程,另外,通過 protobuf 可以將數據序列化為二進制編碼,這會大幅減少需要傳輸的數據量,從而大幅提高性能。
gRPC 可以方便地支持流式通信 (理論上通過 http2.0 就可以使用 streaming 模式, 但是通常 web 服務的 restful api 似乎很少這么用,通常的流式數據應用如視頻流,一般都會使用專門的協議如 HLS,RTMP 等,這些就不是我們通常 web 服務了,而是有專門的服務器應用。)
使用場景
需要對接口進行嚴格約束的情況,比如我們提供了一個公共的服務,很多人,甚至公司外部的人也可以訪問這個服務,這時對于接口我們希望有更加嚴格的約束,我們不希望客戶端給我們傳遞任意的數據,尤其是考慮到安全性的因素,我們通常需要對接口進行更加嚴格的約束。這時 gRPC 就可以通過 protobuf 來提供嚴格的接口約束。
對于性能有更高的要求時。有時我們的服務需要傳遞大量的數據,而又希望不影響我們的性能,這個時候也可以考慮 gRPC 服務,因為通過 protobuf 我們可以將數據壓縮編碼轉化為二進制格式,通常傳遞的數據量要小得多,而且通過 http2 我們可以實現異步的請求,從而大大提高了通信效率。
但是,通常我們不會去單獨使用 gRPC,而是將 gRPC 作為一個部件進行使用,這是因為在生產環境,我們面對大并發的情況下,需要使用分布式系統來去處理,而 gRPC 并沒有提供分布式系統相關的一些必要組件。而且,真正的線上服務還需要提供包括負載均衡,限流熔斷,監控報警,服務注冊和發現等等必要的組件。不過,這就不屬于本篇文章討論的主題了,我們還是先繼續看下如何使用 gRPC。
gRPC DEMO 實例詳解
通過 protobuf 來定義接口和數據類型
編寫 gRPC server 端代碼
編寫 gRPC client 端代碼
本文使用 golang 去實現 demo,其中 protobuf 和 grpc 擴展的安裝就跳過了。
新建 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
新建服務端 server.go
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)
}
啟動 server.go
go run server.go
新打開一個窗口,啟動 client.go
go run client.go
結果為
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 實現起來比較繁瑣,給開發帶來難度。總的來說 gRPC 是一個不錯的跨語言 rpc 解決方案,當然每個人都自己的看法或見解。針對不同的業務場景采用不同的解決方案,最終都是運行效率和開發效率的相互妥協的結果。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注丸趣 TV 行業資訊頻道,感謝您對丸趣 TV 的支持。