gRPC 的幾種常見模式html
在學習 gRPC 的時候,相信你們對於它的四種模式都有了解,咱們來簡單回顧一下:golang
接下來咱們經過一個小例子來看看 gRPC 具體的使用流程。api
假設咱們有一個聊天機器人,現須要增長一個對外提供服務的接口。具體需求爲,接口傳入參數是一我的名,返回一段內容是「Hello 人名」的音頻。若是這個是讓你在不使用 gRPC 的狀況下,你會怎麼作?你們可能會選擇使用 restful api 來實現這個功能,傳入人名,返回音頻二進制數據。跨域
那麼若是使用 gRPC,咱們須要怎麼來設計呢?數組
第一步,須要定義一個接口文檔,也就是 proto 文件。在定義內會定義一個 Service,接下來再在 Service 裏定義一個 SayHello 的方法。下面定義傳入參數,輸入 name 返回 message,須要注意 message 是 bytes 類型,即返回的格式是二進制數據。對於 Golang 底層對應的是一個 bytes 數據,對於其餘語言多是字節流或二進制。瀏覽器
syntax = "proto3"; package helloworld; // 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 { bytes message = 1;
定義完成後 ,下一步就是使用 protoc 命令行工具生成代碼。下圖左側是初始化的項目,你會發現,有一個單獨的目錄 protoc,存放了 hello.proto 這個文件,這個文件就是前面定義好的。安全
下圖右側是自動生成代碼後的項目結構,生成了一個 pkg/helloworld 的包,裏面有一個 hello.pb.go,打開這個文件,你會發現剛纔定義的 proto 已經被翻譯成了 Go 語言。具體 protoc 命令行工具如何使用,能夠自行搜索下,這裏再也不過多展開。服務器
定義好了 proto 文件,以及生成了相應的 package 代碼,下一步咱們就能夠編寫業務邏輯了。restful
Hello gRPC - 服務端業務代碼數據結構
import ( "google.golang.org/grpc" pb "grpc-hw/pkg/helloworld" ) // 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()) tempFile := "srv.wav" err := exec.Command("flite", "-t", "Hello "+in.GetName(), "-o", tempFile).Run() if err != nil { return nil, fmt.Errorf("make audio failed: %v", err) } data, _ := ioutil.ReadFile(tempFile) return &pb.HelloReply{Message: data}, nil } func main() { lis, _ := net.Listen("tcp", port) s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) s.Serve(lis) }
在服務端側,須要實現 SayHello 的方法來知足 GreeterServer 接口的要求。SayHello 方法的傳入參數,是在 proto 文件中定義的 HelloRequest,傳出參數,是在 proto 文件中定義的 HelloReply,以及一個 error。
業務邏輯也比較簡單,獲取 HelloRequest 中 Name 字段,而後經過命令行行工具轉換成對應的音頻,將 bytes 數組存在在 HelloReply 中返回。
Hello gRPC - 客戶端業務代碼
func main() { flag.StringVar(&address, "addr", address, "server address") flag.StringVar(&name, "name", "world", "name") flag.Parse() // Set up a connection to the server. conn, _ := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock()) 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) } tempFile := "cli.wav" ioutil.WriteFile(tempFile, r.Message, 0666) exec.Command("afplay", tempFile).Run() }
咱們再來看一下如何實現 Client。首先,創建一個 gRPC 的鏈接,並初始化 GreeterClient,而後直接調用下 GreeterClient 的 SayHello 的方法,將把返回結果另存爲一個文件,經過播放器播放就能夠了。
整體而言,整個使用過程很簡單,而且很是容易上手,讓開發能夠更加關注在客戶端、服務端業務的實現,不用操心傳輸層的事情。
gRPC 的使用總結
經過剛剛的小例子,咱們來總結一下 gRPC 的使用:
以上 5 步就是 gRPC 的簡單使用方法了。
接下來咱們來聊聊 gRPC 跟 Protobuf 之間的聯繫,固然在這以前咱們須要先知道 Protobuf 是什麼。
Protobuf
Protobuf 是一個語言無關、平臺無關的可擴展的結構化數據序列化方案。你們可能會以爲它跟 JSON 好像沒什麼區別,功能上看起來是同樣的,但像上文那樣去定義 SayHello 的操做,JSON 是作不到的,JSON 只能定義一個請求體或者一個返回體,無法定義一個方法,可是 Protobuf 是能夠的。
Protobuf 多用於協議通信、數據存儲和其餘更多用途。它是一個比較靈活的、高效的、自動化的結構化數據序列機制,可是更小,更快而且更簡單。一旦定義好數據如何構造, 就可使用特殊生成的代碼來輕易地讀寫結構化數據,無需關心用什麼語言來實現。你甚至能夠更新數據結構而不打破已部署的使用"舊有"格式編譯的程序。
上圖是 Protobuf 與 JSON 以及 JSON stream 三者之間的性能比較,能夠明顯的看到在解碼的時候 Protobuf 比其餘兩項快了不僅一星半點。
上圖中,咱們能夠看到 Protobuf 仍是有一些缺點的,好比瀏覽器的支持沒有其餘幾個支持的好。可是,在數據安全方面,因爲傳輸過程當中採用的是加密壓縮後的字節流,通常沒法直接查看,安全性很是好。以及在處理速度方面,由於編解碼效率很高使得總體吞吐量有了顯著提高。還有一點,定義方法,這個是其餘兩種序列化協議所作不到的。
gRPC 跟 Protobuf 的聯繫
雖然每次 gRPC 與 Protobuf 都是同時出現的,可是其實二者之間並無很深的聯繫。只是由於二者都是由 Google 開發的,和 gRPC 自己負載無關,在使用時也能夠選擇 JSON 等等,可是考慮到 Protobuf 有定義方法的優點,在微服務裏仍是很推薦使用的。
上圖是 gRPC 與 Restful API 的對比,日常咱們可能更多使用 Restful API。但從圖上能夠看到,由於 gRPC 用的是 Protobuf,自己就比較小因此很快,不像 JSON 體積比較大、比較慢。另外,gRPC 是加載 HTTP/2 上面的,延遲比較低,也由於 HTTP/2 支持連接複用,這就可讓多個 stream 共用一個鏈接,從而進一步提高速度。對比 Restful 則使用的是 HTTP 1.1,延遲比較高,並且在通常狀況下,每次請求都須要建一下新的鏈接。
gRPC 是雙向的。什麼是雙向呢?好比咱們日常作 Restful,都是從客戶端到服務端,可是服務端沒辦法直接主動向客戶端發送信息,gRPC 則能夠。gRPC 也支持流,Restful只支持Request/Response 這樣的機制。gRPC是面向 API 的,沒有限制,也面向增刪改查。gRPC 能夠經過 Protobuf 直接生成代碼,而 Restful 須要依賴第三方。
另外 gRPC 支持 RPC 能夠調用服務器上的一些方法,而 Restful 是基於HTTP的語法,不少東西須要本身去定義。這方面相信你們都有感觸,好比 REST 定義post/put/delete/get時,由於每一個人都有本身的習慣,因此協做時須要溝通討論進行指定。可是 gRPC 就不須要了,它支持定義函數式辦法,不須要你們去考慮如何設計語法。
以上就是 gRPC 跟 Restful API 的對比。
那麼當咱們引入 gRPC 的時候須要考慮什麼呢?如下幾點確定是不可避免的考慮項:
這個也是咱們引入一項新的東西時,每每須要考慮到的問題。
從選擇 gRPC 到整個項目落地,以及如今上線後正常使用。整個過程當中,我對於項目的思考能夠包括了過去、如今和將來三個階段。
對我而言,過去就是要去看我選擇的這個東西,用的人多很少,完善程度怎麼樣了?而如今則是要結合項目,看看合不合適,能不能使用。固然不能思考到能使用就結束,咱們還須要考慮這個項目在將來的 3-5 年的發展,你引入它後在這個時間內需不須要大的變更。這個是很是重要的,雖然咱們如今常說敏捷開發,也常常會進行不少的調整,可是在相似 gRPC 這種底層基礎來講,是固定的。
以上就是我今天的所有分享內容,講的比較簡單,但願能帶給你們一些收穫。