使用GRPC和协议缓冲区构建性能服务器
#网络开发人员 #教程 #go #grpc

流媒体大数据集的内存和I/O中的性能成本很高。现代化的序列化和绝望的数据方法使用了Google设计的协议缓冲区。由于微服务体系结构系统的流行,很难使用其他通信模型在系统之间进行通信。通过删除这些格式所履行的责任,协议缓冲区旨在击败JSON和XML之类的产品。 GRPC也是一种通信模型,可将协议缓冲区格式进一步采用。为了简化开发人员体验并提高效率,GRPC API应将协议缓冲区用于API定义。

什么是协议缓冲区

“协议缓冲区是一种灵活,高效,自动化的机制,用于序列化结构化数据 - 思考XML,但较小,更快,更简单。您定义了如何使数据结构化一次。然后,您可以使用特殊生成的数据源代码可以轻松编写和从各种数据流进行编写和读取您的结构化数据。 Google

协议缓冲区(通常称为Protobuf)是由Google设计的软件包,以序列化JSON和XML等结构化数据,但严格键入。 Protobuf有助于构建数据,可以在GO,Java,Python等语言中使用。Protobuf在微服务中至关重要,其中大量数据在服务中传输了很多数据。

为什么需要协议缓冲区

现代服务器体系结构(例如GRPC,REST和GRAPHQL)需要快速解析序列化和绝对序列化的结构化数据。 Protobuf用于生成数千条消息并有多种语言的通信协议。

ProtoBufs帮助串行的结构化数据格式,适用于大小的大型消息类型。数据格式用于长期数据存储。信息更改,包括新文件,并将现有字段删除到任何Protobuf的情况下,而无需更新现有数据。 Protobuf主要用于互换结构信息,服务间通信以及磁盘上的档案数据存储。 Facebook还使用了一个类似的协议,称为Apache Thrift,Amazon创建了离子。微软还使用了债券协议,为定义的GRPC服务提供了混凝土RPC协议堆栈。

先决条件

  • 数据结构的基本知识。
  • GO的工作安装
  • 对GO的基本理解。
  • Download您的机器上的.proto并下载代码扩展名,以便于格式化。 #协议缓冲器语言

Protobuf具有简约的语言语法,这意味着Protobuf也具有语言类型。当Protobuf编译时,Protobuf为特定编程语言生成文件,但是在您的情况下,您将拥有一个带有struct映射协议文件的.go文件。

协议缓冲区的类型

  • 标量值
  • 枚举和重复值
  • 嵌套值

标量值

上一条消息中使用的类型是标量;这些类型类似于GO类型。这些是intint32int64stringbool。以下是类型和Protobuf标量类型的比较。

go type Protobuf类型
float32 float
float64
uint32 固定32
uint64 固定64
[]字节 字节

当用户未为标量值分配值时,也会给出默认值。定义的标量类型将设置为默认值。

枚举和重复值

Protobuf中的枚举给出了一个排序号码,该数字将默认值顺序从 Zero设置为 n

syntax = "proto3";
message Scheldule {
    enum month {
        janury = 0
        Febuary = 1;
        March = 2; 
        April = 3;
        May = 4;
        June = 5;
        July = 6;
        August = 7;
        September = 8;
        October = 9;
        November = 10;
        December = 11;
    }
}

在某种情况下,您可能为多个枚举成员分配了相同的值。 Protobuf允许别名设置两个不同的成员。别名枚举看起来像这样:

enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
}

STARTEDRUNNING具有相同的分配值。这意味着两者都有一样。如果删除重复的值,则必须删除allow_alias。否则,Protobuf编译器将引发错误。

重复值

重复字段是一个带有给定键的列表的Protobuf消息。重复区域类似于元素的数组/列表。重复的网站看起来像这样:

message PhoneInfo{
    string serialNum = 1;
    int type = 2;
    repeated string name = 3;
}

最后一行的代码是一个重复字段,一个名称数组。该值可能是这样的["100.104.112.10", "100.104.112.12"]

嵌套场

协议缓冲区允许嵌套模型。内部JSON作为Outerjson的成员,代码看起来像这样:

message PhoneInfo {
    string serialNum = 1;
    int type = 2;
    repeated Proxy name = 3;
}
message Name {
    string serialNum = 1;
    int type = 2;
}

Name类型的嵌套字段进入了PhoneInfo。

让我们看一下与JSON的协议缓冲区官方页面中的消息的示例:

 message person {
    option string name = 1;
    option int32 id = 2;
    option string email  = 3
  }

上一个示例消息是用一种称为人员的类型定义的。 JSON类型看起来像这样:

 {
    "person": {
        "name": "atanda0x",
        "id": 1,
        "email": "atandanafiu@gmail.com"
    }
}

协议缓冲区允许更改支持以JSON样式编译,但结构是相同的。顺序数(1,2,3)以Protobuf顺序标签给出,以在两个系统之间序列化并进行序列化数据。上一个文件编译了目标语言的消息; GO结构和字段是空的默认值。

协议缓冲器编译器(ProtoC)

协议缓冲区编译器使用.proto在各种系统之间传输数据。下图用所选语言解释了原书的流动。在您的情况下,它是去的。

protobuf life circle

环境和文件夹设置

在本节中,您将设置Protobuf项目的开发EVNIROMNET。

初始化项目

第一步是初始化一个新的GO项目。打开一个新的终端并运行以下命令以创建一个新文件夹:您可以随心所欲地命名。我的将是protocol-buffer

mkdir protocol-buffer

接下来,使用以下命令进入该文件夹:

cd protocol-buffer

然后使用go mod命令初始化新的GO项目:

go mod init github.com/atanda0x/protobu_go

创建文件夹和服务器/客户端文件

运行以下命令以创建所有将保存项目文件的文件夹:

mkdir protofiles protoServer protoClient

上面的命令将创建三个文件夹:

  • Protofiles-将包含所有Protobuf文件。
  • protoserver-将包含服务器文件
  • protoclient-将包含客户端文件

安装库
您将为您的项目安装必要的库。

运行以下命令以在计算机上安装Protobuf编译器以进行WSL或检查其他OS安装here

 apt install -y protobuf-compiler

然后,您需要使用go get命令安装 go proto插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28


go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

完成此操作后,在项目中的 Protofiles 文件夹中创建一个user.proto文件,并在其中添加以下代码。这对用户的信息进行了建模。该示例包括一些消息:

syntax = "proto3";
package protofiles;
import "google/protobuf/timestamp.proto";
option go_package = "./";

message User {
    string name = 1;
    int32 id = 2;  // Unique ID number for this person.
    string email = 3;

    enum PhoneInfo {
      MOBILE = 0;
      HOME = 1;
      WORK = 2;
    }

    message PhoneNumber {
      string number = 1;
      PhoneInfo type = 2;
    }

    repeated PhoneNumber phones = 4;

    google.protobuf.Timestamp last_updated = 5;
}

message AddressBook {
    repeated User people = 1;
}

上面的代码将文件声明为proto3 sytax,而protofiles软件包导入google/protobuf/timestamp.proto并defiles文件,将生成的文件存储为User消息,使用fields nameididemail。 PhoneInfo enums还具有字段,UserAddressBook是用用户列表创建的。

package proteprofile告诉编译器添加有关软件包名称的生成文件。您需要将其编译到GO文件中,该文件提供了go访问User.proto文件。

接下来,您需要使用此命令对代码进行编译,以便ProtoC将生成一个GO文件将与:

进行交互的文件。
protoc --go_out=. *.proto

在同一目录中运行该命令后,它将自动生成User.pb.go文件。请打开文件;您会看到它包含产生的一块代码,看起来像这样:

需要在main.go文件中使用生成的代码块来创建Protobuf字符串。生成user.pb.go为您提供以下用户类型:

  • 带有User fieldAddressBook结构。
  • User结构,带有Name的字段,IdEmail,Phones
  • User_PhoneInfo结构,带有NumberType的字段。
  • Person_PhoneType类型和一个值为User.PhoneInfo枚举中的每个值定义。

服务器和客户端

现在,Protobuf已经生成了一个与之交互的文件,让我们创建服务器和客户端文件。服务器和客户端用于与生成的user.pb.go文件进行交互。

在您的protoServer文件夹中创建一个server.go文件并粘贴以下代码。

package main
import (
    "fmt"
    pb "github.com/atanda0x/protobuf-go/protofiles"
    "google.golang.org/protobuf/proto"
)
func main() {
    u := &pb.User{
        Id:    1234,
        Name:  "Atanda N",
        Email: "atandanafiu@gmail.com",
        Phones: []*pb.User_PhoneNumber{
            {
                Number: "+234", Type: pb.User_HOME,
            },
        },
    }
    u1 := &pb.User{}
    body, _ := proto.Marshal(u)
    _ = proto.Unmarshal(body, u1)
    fmt.Println("Original struct loaded from proto file:", u)
    fmt.Println("Marshaled proto data: ", body)
    fmt.Println("Unmarshaled struct: ", u1)
}

上面的代码将文件声明为main.go包,从package main开始,从protofile导入了protobuf pb,导入了proto软件包。 struct映射到原始菲尔的给定的原始库。使用并初始化了User structproto_marchal功能序列化了struct。由于proto库将数据序列化为二进制字节,因此未启动marshalled数据。 Protobuf在Go struct中通过编译文件而生成的,该文件可用于即时创建JSON字符串。

您需要使用终端运行/构建文件。运行以下命令以这样做:

go build


go run main.go

您将看到此输出打印到终端:

让我们修改此示例,但是您将创建另一个名为main_json.go的文件到另一个文件夹,因为在一个目录中使用相同的包装名称会丢弃错误。

package main
import (
    "encoding/json"
    "fmt"
    pb "github.com/atanda0x/protobuf-go/protofiles"
)
func main() {
    u := &pb.User{
        Id:    1234,
        Name:  "Atanda0x",
        Email: "atandanafiu@gmail.com",
        Phones: []*pb.User_PhoneNumber{
            {
                Number: "+23490", Type: pb.User_HOME,
            },
        },
    }
    body, _ := json.Marshal(u)
    fmt.Println(string(body))
}

运行下面的命令,编译将打印一个可以发送给任何了解JSON字符串的客户端的JSON字符串。

go run main_json.go

任何编程语言都可以选择JSON字符串,因为它更容易立即加载数据。

使用协议缓冲区而不是JSON的好处是,缓冲区旨在在两个较少开销的后端系统之间进行通信。二进制少于文本,协议编组的数据小于JSON字符串。

由于Protobuf只是一种数据格式,用于以RPC形式传递两个系统之间的消息,这使得在交流中不使用它, Google远程过程call ( GRPC)进一步扩展微服务通信,客户端和服务器在协议缓冲区中相互交谈。

Google远程过程简介调用(GRPC)

Google远程过程调用(GRPC)是一种开源通信机制,可在两个系统之间发送/接收消息。 GRPC使用支持身份验证,负载平衡和健康检查的体系结构。我写了一个step-by-step guide,介绍了如何将RPC与GO一起使用。在文章中,有一个我使用JSON RPC服务的部分,该服务类似于GRPC。

GRPC旨在以协议缓冲区的形式传输数据。 GRPC更进一步(易于使用且优雅),创建了API来定义服务并开始顺利运行。 GRPC和Protobuf的组合是无缝的,因为多种编程语言可以理解GRPC和协议缓冲区提供的数据结构。

GRPC比JSON,HTTP和REST的优点如下:

  • GRPC使用HTTP/2代替传统HTTP/1.1
  • GRPC使用JSON/XML的协议缓冲区。
  • 用GRPC更快地传输该消息。
  • GRPC具有内置的控制流。
  • GRPC支持双向流。
  • GRPC使用单个TCP连接,通过传统的HTTP/1.1
  • 在服务器和客户端之间发送和接收多个消息

gPRC server being called by any language

上图显示,任何后端系统或移动应用程序都可以通过触发Protobuf请求直接与GRPC服务器通信。

在GO中使用GRPC和Protobuf进行双向流媒体流

现在您知道了GRPC和Protobuf是什么以及他们可以做什么。让我们创建一个基于API的项目。在此用例中,客户将货币转移请求发送给服务器。服务器执行一些任务,并将这些步骤详细信息发送回服务器,以作为响应流。

每当执行某些处理时,服务器需要通知客户端。这称为服务器推动模型。当客户端仅要求一次时,服务器可以将结果流发送回。这与投票不同,客户每次都要求某些内容。当需要完成一系列时间计时的步骤时,这可能很有用。 GRPC客户端可以将该作业升级到GRPC服务器。然后,服务器花时间并将消息传递给客户端,该消息会读取它们并做一些有用的事情。这个概念类似于Websocket,但在任何类型的平台之间。

您已经在protobufs部分中安装了Go proto plugins。项目Outlook

serverPush
├── datafiles
│ └── transaction.proto
│ └── transaction.pb.go
│ └── transaction_grpc.pb.go
└── grpcServer
│ └── main.go
└── grpcClient
  └── main.go

使用定义的消息和服务为项目创建原始文件。在protofile目录中命名您的文件transaction.proto

syntax = "proto3";
package protofiles;
option go_package = "./";

message TransferRequest {
    string from = 1;
    string to = 2;
    float amount = 3;
}

message TransferResponse {
    bool confirmation = 1;
}

service MoneyTransfered {
    rpc MoneyTransfered (TransferRequest) returns (TransferResponse);
}

上面的代码在协议缓冲区文件中有两条消息和一个服务。令人兴奋的部分是在服务中;它返回流而不是简单的响应:

    rpc MakeTransaction(TransactionRequest) returns (stream TransactionResponse) {}

用以下命令编译代码,并确保您在protofiles目录中:

protoc --go_out=. --go_opt=paths=source_relative     --go-grpc_out=. --go-grpc_opt=paths=source_relative     protofile/transaction.proto

这将在DataFiles目录中创建一个名为transaction.pb.go的新文件。你
在服务器和客户端程序中使用此文件的定义,我们将很快创建。现在,编写GRPC服务器代码。由于引入流。

,此代码与上一个示例有所不同。

创建服务器和客户端文件

让S创建服务器和客户端文件的目录。该服务器实现了从protofile生成的接口。

mkdir -p grpcServer


mkdir -p grpcClient

您将cd彼此接近。在grpcServer中创建一个main.go文件。

package main
import (
    "flag"
    "fmt"
    "log"
    "net"
    "time"
    pb "github.com/atanda0x/protobuf-go/StreamwithGRPC/datafiles"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
)
var (
    port      = flag.Int("port", 50051, "Server port")
    noOfSteps = 3
)
type server struct {
    pb.UnimplementedMoneyTransactionServer
}
func (s *server) MakeTransaction(in *pb.TransactionRequest, stream pb.MoneyTransaction_MakeTransactionServer) error {
    log.Printf("Got request for money.....")
    log.Printf("Amount: $ %f, From A/c:%s, To A/c: %s", in.Amount, in.From, in.To)
    for i := 0; i < noOfSteps; i++ {
        time.Sleep(time.Second * 2)
        if err := stream.Send(&pb.TransactionResponse{Status: "good", Step: int32(i), Description: fmt.Sprintln("Description of step &d", int(i))}); err != nil {
            log.Fatalf("%v.Send(%v)  = %v", stream, "status", err)
        }
    }
    log.Printf("Successfully transfered amount $%v from %v to %v", in.Amount, in.From, in.To)
    return nil
}
func main() {
    flag.Parse()
    s := grpc.NewServer()
    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }
    pb.RegisterMoneyTransactionServer(s, &server{})
    reflection.Register(s)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}

上面代码中的MakeTransaction是对我们感兴趣的函数。它将请求和流作为参数。在该函数中,您正在循环浏览步骤数(这里是三个)并执行计算。服务器正在使用time.Sleep函数模拟模拟I/O或计算:

stream.Send() 

此功能将流从服务器发送到客户端。现在,让我们组成客户程序。

在生成的transaction.pb.go文件中,您将看到RegisterMoneyTransaferedSever函数和MakeTransfer函数是MoneyTransferServer接口的一部分。 MoneyTransferhas rpc请求详细信息。这是一个映射到Protobuf文件中定义的TransferRequest消息的结构:

rpc MakeTransfered(TransferRequest) returns (TransferResponse) {}

客户端文件

将客户端文件cd创建到grpcclient目录中。您应该有一个main.go文件。

package main
import (
    "context"
    "flag"
    "io"
    "log"
    pb "github.com/atanda0x/protobuf-go/StreamwithGRPC/datafiles"
    "google.golang.org/grpc"
)
var (
    address = flag.String("add", "localhost:50051", "The address to connect")
)
func ReceiveStream(client pb.MoneyTransactionClient, request *pb.TransactionRequest) {
    log.Println("Started listening to the server stream!!!!")
    stream, err := client.MakeTransaction(context.Background(), request)
    if err != nil {
        log.Fatalf("%v.MakeTransaction(_) = _, %v", client, err)
    }
    for {
        response, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf("%v.MakeTransaction(_) = _, %v", client, err)
        }
        log.Printf("Status: %v, Operation: %v", response.Status, response.Description)
    }
}
func main() {
    flag.Parse()
    conn, err := grpc.Dial(*address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Did not connect: %v", err)
    }
    defer conn.Close()
    client := pb.NewMoneyTransactionClient(conn)
    from := "1234"
    to := "5678"
    amount := float32(1250.75)
    ReceiveStream(client, &pb.TransactionRequest{From: from, To: to, Amount: amount})
}

ReceiveStream上方的代码是编写的自定义功能,该功能是为发送请求并接收消息流。它需要两个参数:MoneyTransactionClientTransactionRequest。使用第一个参数创建流并倾听。每当服务器耗尽所有消息时,客户端将停止侦听并终止。然后,如果客户端试图接收消息,将返回io.EOF错误。响应记录是从GRPC服务器收集的。第二个参数TransactionRequest用于首次将请求发送到服务器。

打开一个终端以使用下面的命令运行grpcServer,在运行实际文件之前运行go build

go build 


go run main.go

打开另一个端子供grpcClient运行main.go文件。

go build


go run main.go

TCP服务器在端口50051上运行。服务器和客户端的输出看起来像这样。

客户端输出:

服务器同时向控制台提供了日志消息。

客户端向grpcServer提出了请求,并将其全部从A/C号码传递给A/C号码和金额。服务器拾取了详细信息,处理了它们,并回答了一切都可以。

此过程与服务器同步发生。客户保持活跃,直到所有流消息都发送回。服务器可以在给定时间处理任何数量的客户端。每个客户请求都被视为个体实体。这是服务器发送响应流的一个示例。其他情况也可以使用协议缓冲区和GRPC来实现:客户端发送流请求以从服务器获得最终响应,客户端和服务器都同时发送流请求和响应。

结论

就是这样!您刚刚使用GRPC和Protobuf构建了双向流。您了解了Google RPC开源通信工具和协议缓冲区,为什么要与其他通信模型相比,为什么在项目中使用它,以及如何生成pb文件并在客户端/服务器文件中与之进行交互。使用他的Advance GRPC,它支持多种编程语言和内置功能,用于负载平衡和身份验证,并支持您构建项目的双向流。 GRPC可帮助开发人员在开发人员选择的任何编程语言中量身定制的高效且健壮的分布式系统。选择可能取决于项目要求和构建分布式系统,兼容性和易用性之间的权衡。

非常感谢您的阅读。愉快的编码!