gRPC初体验
gRPC是由Google主導開發的RPC框架,使用HTTP/2協議并用ProtoBuf作為序列化工具。其客戶端提供Objective-C、Java接口,服務器側則有Java、Golang、C++等接口,從而為移動端(iOS/Androi)到服務器端通訊提供了一種解決方案。 當然在當下的環境下,這種解決方案更熱門的方式是RESTFull API接口。該方式需要自己去選擇編碼方式、服務器架構、自己搭建框架(JSON-RPC)。gRPC官方對REST的聲音是:
- 和REST一樣遵循HTTP協議(明確的說是HTTP/2),但是gRPC提供了全雙工流
- 和傳統的REST不同的是gRPC使用了靜態路徑,從而提高性能
- 用一些格式化的錯誤碼代替了HTTP的狀態碼更好的標示錯誤
至于是否要選擇用gRPC。對于已經有一套方案的團隊,可以參考下。如果是從頭來做,可以考慮下gRPC提供的從客戶端到服務器的整套解決方案,這樣不用客戶端去實現http的請求會話,JSON等的解析,服務器端也有現成的框架用。從15年3月到現在gRPC也發展了一年了,慢慢趨于成熟。下面我們就以gRPC的Golang版本看下其在golang上面的表現。至于服務端的RPC,感覺golang標準庫的RPC框架基本夠用了,沒必要再去用另一套方案。
1. 安裝protobuf
雖然gRPC也支持protobuf2.x,但是建議還是使用protobuf3.x,盡管還沒有正式版本,不過golang版本基本沒有什么問題,另外3.x官方支持了Objective-C,這也是我們使用gRPC的初衷:提供一個移動端到服務器的解決方案。去到Protocol Buffers下載最新版本(Version3.0.0 beta2),然后解壓到本地。本地需要已經安裝好autoconf automake libtool.rpm系列(fedora/centos/redheat)可以用yum安裝。Mac上可以用brew進行安裝
brew install autoconf automake libtool然后執行
./configure --prefix=your_pb_install_path接著
make make install set your_pb_install_path to your $PATH檢查是否安裝完成
protoc --version libprotoc 3.0.0然后安裝golang protobuf直接使用golang的get即可
go get -u github.com/golang/protobuf/proto // golang protobuf 庫 go get -u github.com/golang/protobuf/protoc-gen-go //protoc --go_out 工具2. 安裝gRPC-go
gRPC-go可以通過golang 的get命令直接安裝,非常方便。
go get google.golang.org/grpc這里大家可能比較奇怪,為什么gRPC-go在github的地址是"https://github.com/grpc/grpc-go",但是為什么要用“google.golang.org/grpc”進行安裝呢?應該grpc原本是google內部的項目,歸屬golang,就放在了google.golang.org下面了,后來對外開放,又將其遷移到github上面了,又因為golang比較坑爹的import路徑規則,所以就都沒有改路徑名了。
但是這樣就有個問題了。要如何去管理版本呢?這個目前我還沒有什么比較好的方法,希望知道的朋友一起分享下。目前想到一個方法是手動下載某個版本,然后寫個腳本統一修改代碼中的import里面的路徑.
3. 示例程序
3.1 protobuf
該示例源自gRPC-go的examples的helloworld。先看PB的描述:
syntax = "proto3";option objc_class_prefix = "HLW";package helloworld;// The greeting service definition. service Greeter {// Sends a greetingrpc 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; }這里定義了一個服務Greeter,其中有個API SayHello。其接受參數為HelloRequest類型,返回HelloReply類型。這里HelloRequest和HelloReply就是普通的PB定義
服務定義為:
// The greeting service definition. service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {} }service定義了一個server。其中的接口可以是四種類型
- rpc GetFeature(Point) returns (Feature) {}
類似普通的函數調用,客戶端發送請求Point到服務器,服務器返回相應Feature. - rpc ListFeatures(Rectangle) returns (stream Feature) {}
客戶端發起一次請求,服務器端返回一個流式數據,比如一個數組中的逐個元素 - rpc RecordRoute(stream Point) returns (RouteSummary) {}
客戶端發起的請求是一個流式的數據,比如數組中的逐個元素,服務器返回一個相應 - rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
客戶端發起的請求是一個流式數據,比如數組中的逐個元素,二服務器返回的也是一個類似的數據結構
后面三種可以參考官方的route_guide示例。
使用protoc命令生成相關文件:
protoc --go_out=plugins=grpc:. helloworld.proto ls helloworld.pb.go helloworld.proto生成對應的pb.go文件。這里用了plugins選項,提供對grpc的支持,否則不會生成Service的接口。
3.2 服務器端程序
然后編輯服務器端程序:
package mainimport ("log""net"pb "your_path_to_gen_pb_dir/helloworld""golang.org/x/net/context""google.golang.org/grpc" )const (port = ":50051" )// server is used to implement helloworld.GreeterServer. type server struct{}// SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {return &pb.HelloReply{Message: "Hello " + in.Name}, nil }func main() {lis, err := net.Listen("tcp", port)if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer()pb.RegisterGreeterServer(s, &server{})s.Serve(lis) }這里首先定義一個server結構,然后實現SayHello的接口,其定義在“your_path_to_gen_pb_dir/helloworld”
SayHello(context.Context, *HelloRequest) (*HelloReply, error)然后調用grpc.NewServer() 創建一個server s。接著注冊這個server s到結構server上面 pb.RegisterGreeterServer(s, &server{}) 最后將創建的net.Listener傳給s.Serve()。就可以開始監聽并服務了,類似HTTP的ListenAndServe。
3.3 客戶端程序
客戶端程序:
package mainimport ("log""os"pb "your_path_to_gen_pb_dir/helloworld""golang.org/x/net/context""google.golang.org/grpc" )const (address = "localhost:50051"defaultName = "world" )func main() {// Set up a connection to the server.conn, err := grpc.Dial(address, grpc.WithInsecure())if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()c := pb.NewGreeterClient(conn)// Contact the server and print out its response.name := defaultNameif len(os.Args) > 1 {name = os.Args[1]}r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})if err != nil {log.Fatalf("could not greet: %v", err)}log.Printf("Greeting: %s", r.Message) }這里通過pb.NewGreeterClient()傳入一個conn創建一個client,然后直接調用client上面對應的服務器的接口
SayHello(context.Context, *HelloRequest) (*HelloReply, error)接口,返回*HelloReply 對象。
先運行服務器,在運行客戶端,可以看到。
./greeter_server &./greeter_client 2016/03/10 21:42:19 Greeting: Hello world作者:CZ_Golang
鏈接:https://www.jianshu.com/p/774b38306c30
總結
- 上一篇: gRPC的那些事 - streaming
- 下一篇: 以太坊源码学习 -- EVM