go的微服务调用——以gRPC举例 | 青训营

146 阅读6分钟

微服务概念

微服务是一种软件架构风格,将一个大型的应用程序拆分为一系列的小型、自治的服务,每个服务专注于完成一个特定的业务功能。这些服务之间通过轻量级的通信机制进行交互,可以独立部署、扩展和维护。

下面是微服务架构的一些关键概念:

  1. 服务拆分:将大型应用程序拆分为多个小型的服务,每个服务关注特定的业务功能,如用户管理、订单处理等。拆分后的服务可以独立部署和扩展。

  2. 服务自治:每个微服务都是自治的,拥有自己的独立数据库和业务逻辑。它们可以由不同的团队开发、部署和维护,彼此之间互不依赖。

  3. 通信机制:不同的微服务之间通过轻量级的通信机制进行交互,常见的通信方式有HTTP/REST、消息队列、RPC等。这些通信机制能够实现服务之间的解耦和灵活的协作。

  4. 自动化部署和弹性伸缩:由于每个微服务都是独立的,可以通过自动化的部署工具和弹性伸缩策略来管理和扩展服务。这样可以更快地响应业务需求和流量变化。

  5. 服务发现和治理:微服务架构中的服务通常使用服务注册和发现机制来进行动态的服务发现和负载均衡。此外,还需要实施监控、故障处理和容错机制等治理措施。

  6. 团队组织:微服务架构鼓励团队按照业务功能划分,每个团队负责开发和维护一组相关的微服务。这种组织结构可以提高团队的自治性和灵活性。

微服务架构的优势包括更好的可扩展性、灵活性、独立部署和维护能力。但同时也带来了复杂性和管理挑战,需要在设计和实施过程中考虑到服务边界的划分、服务通信的机制和要求、治理和监控的需求等方面。

gRPC介绍

image.png gRPC是RPC的一种,它使用Protocol Buffer(简称Protobuf)作为序列化格式,Protocol Buffer是来自google的序列化框架,比Json更加轻便高效,同时基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。用protoc就能使用proto文件帮助我们生成上面的option层代码。

在gRPC中,客户端应用程序可以直接在另一台计算机上的服务器应用程序上调用方法,就好像它是本地对象一样,从而使您更轻松地创建分布式应用程序和服务。

使用gRPC有以下几个好处:

  1. 高性能:gRPC基于HTTP/2协议,通过复用单个TCP连接和多路复用请求,从而更有效地利用网络资源。与传统的RESTful API相比,它可以更快地处理请求,并且在带宽和延迟方面具有更好的表现。

  2. 省带宽:gRPC使用Protocol Buffers作为默认的序列化机制,Protocol Buffers是一种高效且可扩展的二进制序列化格式。相比于JSON或XML等文本格式,它生成的数据包更小,可以节省带宽,并且减少网络传输的开销。

  3. 跨语言支持:gRPC支持多种编程语言,包括Go、Java、Python、C#等。通过使用相同的gRPC接口和结构定义文件,可以轻松地在不同语言之间进行通信和互操作,这对于构建跨语言的分布式系统非常有用。

  4. 自动生成代码:gRPC提供的工具可以自动从接口定义文件生成客户端和服务器端的代码。通过自动生成的代码,你可以快速地在应用程序中使用类型安全的接口和方法调用,并且可以避免手动编写大量重复的网络通信代码。

  5. 强类型约束:gRPC使用Protocol Buffers定义接口和消息结构,它提供了强类型的约束。这意味着在编译时就能够捕获到潜在的错误,例如参数类型不匹配、缺少必填字段等,从而减少了运行时错误和调试的时间成本。

  6. 可插拔的中间件:gRPC支持可插拔的中间件机制,使你可以在请求和响应的传输过程中插入自定义的中间件组件。这些中间件可以用于处理认证、日志记录、监控、错误处理等功能,使你能够轻松地实现自定义的网络层逻辑。

总的来说,gRPC具有高性能、省带宽、跨语言支持、自动生成代码、类型安全和可插拔的中间件等优势。它适用于构建高效、可扩展和跨平台的分布式系统,并且在云原生应用和微服务架构中得到广泛应用。

如何使用gRPC?

场景

假设现在我们需要完成查看商品和添加商品的场景。我们这个微服务只有两个功能,查看与添加。

下载

go get -u github.com/golang/protobuf/protoc-gen-go

除此之外,我们还需要下载github.com/protocolbuf… 把其添加到我们的环境路径中。

编写proto文件夹

syntax = "protoo3";
package proto;


service ProduceInfo{
  rpc insertProducts(Product) returns (ProductId);
  rpc selectProducts(ProductId) returns (Product);
}

message Product {
  string id = 1;
  string name = 2;
  string dsp = 3;
}

message ProductId{
  string value = 1;
}

然后找到当前目录protoc --go_out=plugins=grpc:../上级目录/proto文件名字

image.png 然后就会出现pb.go文件。然后就是创建并编写server文件和client文件,完成服务端和客户端的代码逻辑编写。

首先目录结构如下

image.png

首先实现服务端

package main
​
import (
   "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
}

客户端调用

import (
   "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)
}

总结

微服务的出现就是为了协同开发,将大模块分散降低开发难度,减轻耦合度。学习微服务是非常有必要的。这是未来软件开发的趋势!