gRPC 是 Google 开源的一个高性能的 RPC(Remote Procedure Call) 框架,它具备如下的长处:

  • 提供高效的过程间通信。gRPC 没有应用 XML 或者 JSON 这种文本格式,而是采纳了基于 protocol buffers 的二进制协定;同时,gRPC 采纳了 HTTP/2 做为通信协议,从而可能疾速的解决过程间通信。
  • 简略且良好的服务接口和模式。gRPC 为程序开发提供了一种契约优先的形式,必须首先定义服务接口,能力解决实现细节。
  • 反对多语言。gRPC 是语言中立的,咱们能够抉择任意一种编程语言,都可能与 gRPC 客户端或者服务端进行交互。
  • 成熟并且已被宽泛应用。通过在 Google 的大量实战测试,gRPC 曾经倒退成熟。

上面通过一个简略的 demo 来初步理解 gRPC 的应用。

咱们构建一个商品服务,命名为 ProductInfo,客户端和服务端的交互模式如下:

首先咱们须要定义 protobuf:

syntax = "proto3";package product;service ProductInfo {  //增加商品  rpc addProduct(Product) returns (ProductId);  //获取商品  rpc getProduct(ProductId) returns (Product);}message Product {  string id = 1;  string name = 2;  string description = 3;}message ProductId {  string value = 1;}

咱们定义了一个 ProductInfo 服务,其中有两个办法,别离是增加商品和获取商品,而后在 proto 文件所在的目录下执行命令 protoc --go_out=plugins=grpc:../product ProductInfo.proto

如果没有装置 protoc,执行命令 go get -u github.com/golang/protobuf/protoc-gen-go 进行装置。尽管 gRPC 反对多种语言,然而为了对立,我文章中的代码都应用 Go。

执行完之后,在 proto 文件同级目录下会呈现一个 ProductInfo.pb.go 文件:

而后在 product 文件夹下新建一个 server 文件夹,而后新建一个 main.go 文件,首先在文件中实现 ProductInfo 服务的两个办法的业务逻辑:

package mainimport (   "context"   "github.com/gofrs/uuid"   "google.golang.org/grpc/codes"   "google.golang.org/grpc/status"   "grpc-demo/product")type server struct {   productMap map[string]*product.Product}//增加商品func (s *server) AddProduct(ctx context.Context, req *product.Product) (resp *product.ProductId, err error) {   resp = &product.ProductId{}   out, err := uuid.NewV4()   if err != nil {      return resp, status.Errorf(codes.Internal, "err while generate the uuid ", err)   }      req.Id = out.String()   if s.productMap == nil {      s.productMap = make(map[string]*product.Product)    }      s.productMap[req.Id] = req   resp.Value = req.Id   return}//获取商品func (s *server) GetProduct(ctx context.Context, req *product.ProductId) (resp *product.Product, err error) {   if s.productMap == nil {      s.productMap = make(map[string]*product.Product)   }   resp = s.productMap[req.Value]   return}

而后持续在 main.go 文件中增加一个 main 办法,建设一个 gRPC 服务器:

func main() {   listener, err := net.Listen("tcp", port)   if err != nil {      log.Println("net listen err ", err)      return   }   s := grpc.NewServer()   product.RegisterProductInfoServer(s, &server{})   log.Println("start gRPC listen on port " + port)   if err := s.Serve(listener); err != nil {      log.Println("failed to serve...", err)      return   }}

服务端的逻辑就到这里了,接下来再写一下客户端的逻辑,建设一个 client 文件夹,而后新建一个 main.go 文件,内容如下:

package mainimport (   "context"   "google.golang.org/grpc"   "grpc-demo/product"   "log")const (   address = "localhost:50051")func main() {   conn, err := grpc.Dial(address, grpc.WithInsecure())   if err != nil {      log.Println("did not connect.", err)      return   }   defer conn.Close()   client := product.NewProductInfoClient(conn)   ctx := context.Background()   id := AddProduct(ctx, client)   GetProduct(ctx, client, id)}// 增加一个测试的商品func AddProduct(ctx context.Context, client product.ProductInfoClient) (id string) {   aMac := &product.Product{Name: "Mac Book Pro 2019", Description: "From Apple Inc."}   productId, err := client.AddProduct(ctx, aMac)   if err != nil {      log.Println("add product fail.", err)      return   }   log.Println("add product success, id = ", productId.Value)   return productId.Value}// 获取一个商品func GetProduct(ctx context.Context, client product.ProductInfoClient, id string) {   p, err := client.GetProduct(ctx, &product.ProductId{Value: id})   if err != nil {      log.Println("get product err.", err)      return   }   log.Printf("get prodcut success : %+v\n", p)}

而后先启动 server/main.go ,再启动 client/main.go,这样一次服务端和客户端之间的连贯便实现了,能够看到运行的后果输入了:

当然你也能够在服务端的办法中,加上一些日志来验证一下。

最初,这个小的 demo 的目录构造就是这样的:


我的项目的代码在我的GitHub 上:https://github.com/roseduan/grpc-demo