七叶笔记 » golang编程 » Go 玩转 gRPC 通信

Go 玩转 gRPC 通信

欢迎地鼠同胞! 很多人要求我写一篇关于 gRPC 主题的教程,以及如何在 Go 中编写自己的基于 gRPC 的系统。因此,在本教程中,我们将深入了解 gRPC 精彩而令人兴奋的世界,并探索我们如何利用它来获得名利!

我们将深入研究 gRPC 背后的理论,以及为什么与HTTP RESTAPI等更传统的设置相比,它在某些情况下会更好。

一旦我们掌握了 gRPC 如何使我们的系统受益的问题,我们将看看如何构建用 Go 编写的简单 gRPC 客户端和服务器!

先决条件

在完成本教程之前 ,您必须在您的机器上安装以下内容:

  • 已安装 Protocol Buffers v3 – 这可以通过运行来完成 go get -u github.com/golang/protobuf/protoc-gen-go

您必须确保它$GOPATH/bin位于您的环境路径中,以便您可以protoc在本教程后面使用该工具。

gRPC 简介

因此,在深入研究之前,我们首先需要了解 gRPC 是什么,它是如何工作的等等。

定义 – gRPC 是一个现代的、开源的远程过程调用 (RPC) 框架,可以在任何地方运行

远程过程调用是我们在分布式系统中使用的东西,它允许我们在应用程序之间进行通信。更具体地说,它允许我们在我们的应用程序中公开我们希望其他应用程序能够调用的方法。

它类似于 REST API 通信,通过它,您可以有效地将应用程序中的功能公开给使用 HTTP 连接作为通信媒介的其他应用程序。

gRPC 和 REST 的区别

虽然 REST 和 gRPC 有点相似,但您应该注意它们的工作方式存在一些根本差异。

  1. gRPC 利用HTTP/2而 REST 利用HTTP 1.1
  2. gRPC 使用协议缓冲区数据格式,而不是通常在 REST API 中使用的标准 JSON 数据格式
  3. 使用 gRPC,您可以根据需要利用HTTP/2服务器端流式传输、客户端流式传输甚至双向流式传输等功能。

gRPC 的挑战

您应该记住,虽然 gRPC 确实允许您利用这些更新的技术,但由于无法使用 Postman HTTP 客户端等工具来轻松与暴露的 gRPC 交互,因此对 gRPC 服务进行原型设计更具挑战性服务。

您确实有使这成为可能的选项,但这并不是本机即可获得的。有一些选项可以使用诸如 envoy 之类的工具来反向代理标准 JSON 请求并将它们转码为正确的数据格式,但这是一个额外的依赖项,对于简单项目的设置可能会很棘手。

在 Go 中构建 gRPC 服务器

让我们从在 Go 中定义一个非常简单的 gRPC 服务器开始。一旦我们有一个简单的服务器启动并运行,我们就可以着手创建一个能够与之交互的 gRPC 客户端。

我们将首先在main函数中编写逻辑来侦听传入 TCP 连接的端口:

main.go

 package main

import ( 
  "log"
  "net"
)

func main() {
  lis, err := net.Listen("tcp", ":9000")
  if err != nil {
    log.Fatalf("failed to listen: %v", err)
  }
}
  

接下来,我们gRPC要从中导入官方包,golang.org以便我们可以创建一个新的 gRPC 服务器,然后在通过我们TCP上面定义的现有连接提供服务之前注册我们想要公开的端点:

main.go

 package main

import (
"log"
"net"

"google.golang.org/grpc"
)

func main() {

lis, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

grpcServer := grpc.NewServer()

if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
  

这是用 go 编写的 gRPC 服务器的绝对最低要求。但是,现在它并没有做太多。

添加一些功能

让我们看看我们如何开始通过我们的 gRPC 服务器公开一些功能,以便 gRCP 客户端可以以有意义的方式与我们的服务器交互。

让我们首先定义chat.proto将作为我们的合约的文件:

聊天.proto

 syntax = "proto3";
package chat;

message Message {
  string body = 1;
}

service ChatService {
  rpc SayHello(Message) returns (Message) {}
}
  

该.proto文件公开了我们的ChatService其中一个SayHello功能,该功能可以由任何用任何语言编写的 gRPC 客户端调用。

这些.proto定义通常在各种形状和大小的客户端之间共享,以便它们可以生成自己的代码来与我们的 gRPC 服务器通信。

让我们使用该protoc工具生成 Go 特定的 gRPC 代码:

 $ protoc --go_out=plugins=grpc:chat chat.proto
  

您会看到这将生成一个chat/chat.pb.go包含生成代码的文件,以便我们在代码中轻松调用。让我们更新我们的server.go以注册我们的 ChatService,如下所示:

服务器.go

 package main

import (
"fmt"
"log"
"net"

"github.com/tutorialedge/go-grpc-beginners-tutorial/chat"
"google.golang.org/grpc"
)

func main() {

fmt.Println("Go gRPC Beginners Tutorial!")

lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 9000))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

s := chat.Server{}

grpcServer := grpc.NewServer()

chat.RegisterChatServiceServer(grpcServer, &s)

if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}

  

然后我们将不得不定义SayHello接收 a的方法Message,读取消息的正文,然后返回Message它自己的 a:

聊天/chat.go

 package chat

import (
"log"

"golang.org/x/net/context"
)

type Server struct {
}

func (s *Server) SayHello(ctx context.Context, in *Message) (*Message, error) {
log.Printf("Receive message body from client: %s", in.Body)
return &Message{Body: "Hello From the Server!"}, nil
}

  

如果我们想为我们的 gRPC 服务器定义更高级的功能,那么我们可以通过定义一个从我们的服务器构建的新方法struct,然后将该函数的名称添加到我们的chat.proto文件中,以便我们的应用程序可以将其公开为其他 gRPC 客户端的东西可以打。

完成这些最终更改后,让我们尝试运行我们的服务器:

 $ go run server.go
Go gRPC Beginners Tutorial!
  

惊人的!我们现在localhost:9000在我们的机器上启动并运行了一个全新的、闪亮的新 gRPC 服务器!

在 Go 中构建 gRPC 客户端

现在我们的服务器已经启动并运行了,让我们看看如何构建一个能够与之交互的简单客户端。

客户端.go

 package main

import (
"log"

"golang.org/x/net/context"
"google.golang.org/grpc"

"github.com/tutorialedge/go-grpc-beginners-tutorial/chat"
)

func main() {

var conn *grpc.ClientConn
conn, err := grpc.Dial(":9000", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %s", err)
}
defer conn.Close()

c := chat.NewChatServiceClient(conn)

response, err := c.SayHello(context.Background(), &chat.Message{Body: "Hello From Client!"})
if err != nil {
log.Fatalf("Error when calling SayHello: %s", err)
}
log.Printf("Response from server: %s", response.Body)

}

  

当我们运行它时,我们应该看到我们的客户端Hello从服务器收到了一个非常好的消息,如下所示:

 $ go run client.go
2020/04/30 20:10:09 Response from server: Hello From the Server!
  

太棒了,我们已经成功创建了一个非常简单的 gRPC 客户端,它现在可以与我们的新 gRPC 服务器通信了!

挑战

挑战 – 添加一个新方法调用BroadcastMessage到我们的 gRPC 服务器。

挑战剧透

首先修改chat.proto包含 gRPC 合约的文件。您将新rpc定义添加到您ChatService的名称中BroadcastMessage:

 syntax = "proto3";
package chat;

message Message {
  string body = 1;
}

service ChatService {
  rpc SayHello(Message) returns (Message) {}
  rpc BroadcastMessage(Message) returns (Message) {}
}
  

有了这个,你就想chat.pb.go从这个更新的合同中重新生成你的文件:

 $ protoc --go_out=plugins=grpc:chat chat.proto
  

然后您必须将此方法定义添加到您的chat/chat.go文件中:

 package chat

import (
  "log"

  "golang.org/x/net/context"
)

type Server struct {
}

func (s *Server) SayHello(ctx context.Context, in *Message) (*Message, error) {
  log.Printf("Receive message body from client: %s", in.Body)
  return &Message{Body: "Hello From the Server!"}, nil
}

func (s *Server) BroadcastMessage(ctx context.Context, in *Message) (*Message, error) {
  log.Printf("Broadcasting new message from a client: %s", in.Body)
  return &Message{Body: "Broadcasted message!"}, nil
}
  

最后,您可以更新client.go代码以调用这个新的 gRPC 端点:

 package main

import (
  "log"

  "golang.org/x/net/context"
  "google.golang.org/grpc"

  "github.com/tutorialedge/go-grpc-beginners-tutorial/chat"
)

func main() {

  var conn *grpc.ClientConn
  conn, err := grpc.Dial(":9000", grpc.WithInsecure())
  if err != nil {
    log.Fatalf("did not connect: %s", err)
  }
  defer conn.Close()

  c := chat.NewChatServiceClient(conn)

  response, err := c.SayHello(context.Background(), &chat.Message{Body: "Hello From Client!"})
  if err != nil {
    log.Fatalf("Error when calling SayHello: %s", err)
  }
  log.Printf("Response from server: %s", response.Body)

  response, err = c.BroadcastMessage(context.Background(), &chat.Message{Body: "Message to Broadcast!"})
  if err != nil {
    log.Fatalf("Error when calling Broadcast Message: %s", err)
  }
  log.Printf("Response from server: %s", response.Body)

}
  

惊人的!您已成功添加新的 gRPC 端点!

结论

因此,在本教程中,我们研究了如何在 Go 中构建一个简单的 gRPC 客户端和服务器。我们构建了一个基本服务器,它接受来自客户端的传入消息,然后向这些客户端返回响应。

现在,这只是我深入研究 gRPC 的开始,在接下来的几周内,我们将扩展本文中提出的基础,我们将研究更复杂的主题,例如身份验证和授权以及双路流媒体!


相关文章