Google Protocol Buffer(简称 Protobuf)是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通信协议和数据存储等领域。前端
tax -xvf protobuf-all-xxx.tar.gz cd protobuf-xxx ./configure make make check sudo make install
go get github.com/golang/protobuf/proto // golang的protobuf库文件 // 插件 go get github.com/golang/protobuf/protoc-gen-go // 用于根据protobuf生成golang代码,语法 protoc --go_out=. *.proto
book/book.protogit
syntax="proto3"; package book; // import "xxx/xx.proto" // 出版社 message Publisher{ required string name = 1 } // 书籍信息 message Book { required string name = 1; message Author { required string name = 1; required string address = 1; } required Author author = 2; enum BookType{ SCIENCE = 1 ; LITERATURE = 2; } optional BookType type = 3; optional Publisher publisher = 4 }
protobuf采用以上的book.proto文件github
并使用如下命令生成go文件golang
protoc --go_out=. *.proto
在代码中使用编程
package main import ( b "book" "github.com/golang/protobuf/proto" ) func main(){ ... // 将实例转为proto编码 var b = &b.Book{Name:"xxx", Author:b.Author{Name:"yyy"}} protoBook, err := proto.Marshal(b) ... // 讲proto编码转化为实例 var b2 b.Book err = proto.Unmarshal(protoBook, &b2) ... }
gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf做为序列化工具。其客户端提供Objective-C、Java接口,服务器侧则有Java、Golang、C++等接口。使用grpc能够方便的调用其余进程的方法,调用须要传输的数据使用的是proto编码。这对于大型项目来讲,能够有效的提升数据的解编码效率和数据传输率。json
一个RPC service就是一个可以经过参数和返回值进行远程调用的method,咱们能够简单地将它理解成一个函数。由于gRPC是经过将数据编码成protocal buffer来实现传输的。所以,咱们经过protocal buffers interface definitioin language(IDL)来定义service method,同时将参数和返回值也定义成protocal buffer message类型。具体实现以下所示,包含下面代码的文件叫helloworld.proto:api
syntax = "proto3"; package helloworld; // The greeter 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; }
接着,根据上述定义的service,咱们能够利用protocal buffer compiler ,即protoc生成相应的服务器端和客户端的GoLang代码。生成的代码中包含了客户端可以进行RPC的方法以及服务器端须要进行实现的接口。服务器
假设如今所在的目录是$GOPATH/src/helloworld/helloworld,咱们将经过以下命令生成gRPC对应的GoLang代码:restful
protoc --go_out=plugins=grpc:. helloworld.proto
此时,将在目录下生成helloworld.pb.go文件数据结构
server.go
package main // server.go import ( "log" "net" "golang.org/x/net/context" "google.golang.org/grpc" pb "helloworld/helloworld" ) const ( port = ":50051" ) type server struct {} // 当接收到请求的时候回调用该方法 // 参数由grpc本身根据请求进行构造 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.Fatal("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) s.Serve(lis) }
其中pb是咱们刚才根据proto生成的go文件的包
package main //client.go import ( "log" "os" "golang.org/x/net/context" "google.golang.org/grpc" pb "helloworld/helloworld" ) const ( address = "localhost:50051" defaultName = "world" ) func main() { // 创建一个grpc链接 conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatal("did not connect: %v", err) } defer conn.Close() // 新建一个客户端,方法为:NewXXXClinent(conn),XXX为你在proto定义的服务的名字 c := pb.NewGreeterClient(conn) 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.Fatal("could not greet: %v", err) } log.Printf("Greeting: %s", r.Message) }
使用grpc的优势不少,二进制的数据能够加快传输速度,基于http2的多路复用能够减小服务之间的链接次数,和函数同样的调用方式也有效的提高了开发效率。不过使用grpc也会面临一个问题,咱们的微服务对外必定是要提供Restful接口的,若是内部调用使用grpc,在某些状况下要同时提供一个功能的两套API接口,这样就不只下降了开发效率,也增长了调试的复杂度。因而就想着有没有一个转换机制,让Restful和gprc能够相互转化。
grpc-gateway应运而生
首先你得要根据本文以前的步骤安装proto和grpc,而后以下安装一些库
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger go get -u github.com/golang/protobuf/protoc-gen-go
syntax = "proto3"; package example; import "google/api/annotations.proto"; message StringMessage { string value = 1; } service YourService { rpc Echo(StringMessage) returns (StringMessage) { option (google.api.http) = { post: "/v1/example/echo" body: "*" }; } }
option 表示处理哪些path的请求以及如何处理请求体(参数),见https://cloud.google.com/serv...
生成go文件
protoc -I/usr/local/include -I. \ -I$GOPATH/src \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ --go_out=plugins=grpc:. \ path/to/your_service.proto protoc -I/usr/local/include -I. \ -I$GOPATH/src \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ --grpc-gateway_out=logtostderr=true:. \ path/to/your_service.proto
以上生成的两个文件,第一个是pb.go文件,给grpc server用的;第二个是pb.gw.go文件,给grpc-gateway用的,用于grpc和restful的相互转化
package main import ( "flag" "net/http" "github.com/golang/glog" "golang.org/x/net/context" "github.com/grpc-ecosystem/grpc-gateway/runtime" "google.golang.org/grpc" gw "path/to/your_service_package" ) var ( echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService") ) func run() error { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts) if err != nil { return err } return http.ListenAndServe(":8080", mux) } func main() { flag.Parse() defer glog.Flush() if err := run(); err != nil { glog.Fatal(err) } }
curl -X POST -k http://localhost:8080/v1/example/echo -d '{"name": " world"} {"message":"Hello world"}
流程以下:curl用post向gateway发送请求,gateway做为proxy将请求转化一下经过grpc转发给greeter_server,greeter_server经过grpc返回结果,gateway收到结果后,转化成json返回给前端。