gRPC是由Google主導(dǎo)開發(fā)的RPC框架菲茬,使用HTTP/2協(xié)議并用ProtoBuf作為序列化工具坚弱。其客戶端提供Objective-C盛正、Java接口,服務(wù)器側(cè)則有Java蚁孔、Golang奶赔、C++等接口,從而為移動端(iOS/Androi)到服務(wù)器端通訊提供了一種解決方案勒虾。 當(dāng)然在當(dāng)下的環(huán)境下纺阔,這種解決方案更熱門的方式是RESTFull API接口。該方式需要自己去選擇編碼方式修然、服務(wù)器架構(gòu)笛钝、自己搭建框架(JSON-RPC)质况。gRPC官方對REST的聲音是:
- 和REST一樣遵循HTTP協(xié)議(明確的說是HTTP/2),但是gRPC提供了全雙工流
- 和傳統(tǒng)的REST不同的是gRPC使用了靜態(tài)路徑玻靡,從而提高性能
- 用一些格式化的錯誤碼代替了HTTP的狀態(tài)碼更好的標(biāo)示錯誤
至于是否要選擇用gRPC结榄。對于已經(jīng)有一套方案的團(tuán)隊,可以參考下囤捻。如果是從頭來做臼朗,可以考慮下gRPC提供的從客戶端到服務(wù)器的整套解決方案,這樣不用客戶端去實現(xiàn)http的請求會話蝎土,JSON等的解析视哑,服務(wù)器端也有現(xiàn)成的框架用。從15年3月到現(xiàn)在gRPC也發(fā)展了一年了誊涯,慢慢趨于成熟挡毅。下面我們就以gRPC的Golang版本看下其在golang上面的表現(xiàn)。至于服務(wù)端的RPC暴构,感覺golang標(biāo)準(zhǔn)庫的RPC框架基本夠用了,沒必要再去用另一套方案跪呈。
1. 安裝protobuf
雖然gRPC也支持protobuf2.x,但是建議還是使用protobuf3.x取逾,盡管還沒有正式版本耗绿,不過golang版本基本沒有什么問題,另外3.x官方支持了Objective-C砾隅,這也是我們使用gRPC的初衷:提供一個移動端到服務(wù)器的解決方案误阻。去到Protocol Buffers下載最新版本(Version3.0.0 beta2),然后解壓到本地琉用。本地需要已經(jīng)安裝好autoconf automake libtool
.rpm系列(fedora/centos/redheat)可以用yum安裝堕绩。Mac上可以用brew進(jìn)行安裝
brew install autoconf automake libtool
然后執(zhí)行
./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”進(jìn)行安裝呢特姐?應(yīng)該grpc原本是google內(nèi)部的項目晶丘,歸屬golang,就放在了google.golang.org下面了唐含,后來對外開放浅浮,又將其遷移到github上面了,又因為golang比較坑爹的import路徑規(guī)則捷枯,所以就都沒有改路徑名了滚秩。
但是這樣就有個問題了。要如何去管理版本呢淮捆?這個目前我還沒有什么比較好的方法郁油,希望知道的朋友一起分享下本股。目前想到一個方法是手動下載某個版本,然后寫個腳本統(tǒng)一修改代碼中的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 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 {
string message = 1;
}
這里定義了一個服務(wù)Greeter拄显,其中有個API SayHello
。其接受參數(shù)為HelloRequest
類型案站,返回HelloReply
類型躬审。這里HelloRequest
和HelloReply
就是普通的PB定義
服務(wù)定義為:
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
service
定義了一個server。其中的接口可以是四種類型
- rpc GetFeature(Point) returns (Feature) {}
類似普通的函數(shù)調(diào)用蟆盐,客戶端發(fā)送請求Point到服務(wù)器承边,服務(wù)器返回相應(yīng)Feature. - rpc ListFeatures(Rectangle) returns (stream Feature) {}
客戶端發(fā)起一次請求,服務(wù)器端返回一個流式數(shù)據(jù)石挂,比如一個數(shù)組中的逐個元素 - rpc RecordRoute(stream Point) returns (RouteSummary) {}
客戶端發(fā)起的請求是一個流式的數(shù)據(jù)博助,比如數(shù)組中的逐個元素,服務(wù)器返回一個相應(yīng) - rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
客戶端發(fā)起的請求是一個流式數(shù)據(jù)誊稚,比如數(shù)組中的逐個元素翔始,二服務(wù)器返回的也是一個類似的數(shù)據(jù)結(jié)構(gòu)
后面三種可以參考官方的route_guide示例。
使用protoc命令生成相關(guān)文件:
protoc --go_out=plugins=grpc:. helloworld.proto
ls
helloworld.pb.go helloworld.proto
生成對應(yīng)的pb.go文件里伯。這里用了plugins選項城瞎,提供對grpc的支持,否則不會生成Service的接口疾瓮。
3.2 服務(wù)器端程序
然后編輯服務(wù)器端程序:
package main
import (
"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結(jié)構(gòu)脖镀,然后實現(xiàn)SayHello的接口,其定義在“your_path_to_gen_pb_dir/helloworld”
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
然后調(diào)用grpc.NewServer()
創(chuàng)建一個server s狼电。接著注冊這個server s到結(jié)構(gòu)server上面 pb.RegisterGreeterServer(s, &server{})
最后將創(chuàng)建的net.Listener傳給s.Serve()
蜒灰。就可以開始監(jiān)聽并服務(wù)了,類似HTTP的ListenAndServe肩碟。
3.3 客戶端程序
客戶端程序:
package main
import (
"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 := defaultName
if 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創(chuàng)建一個client强窖,然后直接調(diào)用client上面對應(yīng)的服務(wù)器的接口
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
接口,返回*HelloReply 對象削祈。
先運(yùn)行服務(wù)器翅溺,在運(yùn)行客戶端,可以看到髓抑。
./greeter_server &
./greeter_client
2016/03/10 21:42:19 Greeting: Hello world