云原生应用开发之 gRPC 入门
gRPC is a modern, open source remote procedure call (RPC) framework that can run anywhere.
什么是 gRPC
gRPC 由谷歌开发的,是一种语言中立、平台中立、开源的远程调用过程。
什么是远程调用过程?简单理解就是公开本地应用给其他应用程序调用的方法。gRPC 是一项进程间通信技术,可以用来连接、调用、操作和调式分布式异构应用程序。也像 RPC 应用程序的特点一样:像调用本地函数一样。
特点
-
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
-
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。
-
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制,它的作用与 XML、json 类似,但它是二进制格式,性能好、效率高(缺点:可读性差)。
gRPC 和 REST 区别
它类似于 REST API 通信,通过它,您可以有效地将应用程序中的功能公开给使用 HTTP 连接作为通信媒介的其他应用程序。
虽然 REST 和 gRPC 有点相似,但您应该注意它们的工作方式存在区别:
-
gRPC 使用 HTTP/2 协议,而 REST 使用 HTTP 1.1
-
gRPC 使用协议缓冲区数据格式,而不是通常在 REST API 中使用的标准 JSON 数据格式
-
使用 gRPC,您可以根据需要利用 HTTP/2 功能,例如服务器端流式传输、客户端流式传输甚至双向流式传输。
Go 建立一个 gRPC 服务器
我们从在 Go 中定义一个非常简单的 gRPC 服务器开始。一旦我们有一个简单的服务器启动并运行,我们就可以着手创建一个能够与之交互的 gRPC 客户端。
gRPC 可以实现微服务,将大的项目拆分为多个小且独立的业务模块,也就是服务,各服务间使用高效的protobuf 协议进行 RPC 调用,gRPC 默认使用 protocol buffers ,这是 google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON )。可以用 proto files 创建 gRPC 服务,用 message 类型来定义方法参数和返回类型
-
安装 golang 的proto工具包:
go get -u github.com/golang/protobuf/proto
-
在开始建立 gRPC 之前,确保已安装 Protocol Buffers v3:
go get -u github.com/golang/protobuf/protoc-gen-go
-
在 Go 中安装 gRPC:
go get google.golang.org/grpc
然后写一个服务器,通过监听 TCP 连接的端口。如下的 main
函数:
package main
import (
"log"
"net"
)
func main() {
lis, err := net.Listen("tcp", ":8000")
if err != nil {
log.Fatalf("Fail to listen: %v", err)
}
}
接下来,我们要从 golang.org 导入官方的 gRPC 包,以便我们可以创建一个新的 gRPC 服务器,然后注册我们想要公开的端点,然后通过我们上面定义的现有 TCP 连接提供服务:
package main
import (
"log"
"net"
"google.golang.org/grpc"
)
func main() {
lis, err := net.Listen("tcp", ":8000")
if err != nil {
log.Fatalf("Fail to listen: %v", err)
}
grpcServer := grpc.NewServer()
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("Fail to serve: %v", err)
}
}
这是用 go 编写的最基础 gRPC 服务器,现在的功能还很有限。
添加一些功能
然后写一个客户端与前面的服务器进行交互,创建一个 client.proto
文件:
syntax = "proto3"; // 协议为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,如下所示:
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 方法,该方法将接收一条消息,读取消息的正文,然后返回它自己的消息:
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 服务器定义更高级的功能,那么我们可以通过定义一个基于我们的 Server 结构构建的新方法来实现,然后将该函数的名称添加到我们的 chat.proto
文件中,以便我们的应用程序可以将其公开为某种东西其他 gRPC 客户端可以访问。
完成这些最终更改后,让我们尝试运行我们的服务器:
$ go run server.go
Go gRPC Beginners Tutorial!
惊人的!我们现在在我们的机器上的 localhost:8000
上启动并运行了一个崭新的、闪亮的新 gRPC 服务器!
在 Go 中构建 gRPC 客户端
现在我们的服务器已经启动并运行了,让我们看看如何构建一个能够与之交互的简单客户端。更新一下 client.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(":8000", 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
2022/07/07 23:23:01 Response from server: Hello From the Server!
我们已经成功创建了一个非常简单的 gRPC 客户端,它现在可以与我们的新 gRPC 服务器通信!
安装问题
如果遇到 go get google.golang.org/grpc
安装出错,如下:
通过如下方式替换:
-
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
-
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
-
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
-
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
-
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
-
cd $GOPATH/src/
-
go install google.golang.org/grpc
总结
通过本文我们已经了解了如何在 Go 中构建一个简单的 gRPC 客户端和服务器。我们构建了一个基本服务器,它接受来自客户端的传入消息,然后向这些客户端返回响应。
参考链接:
《gRPC 与云原生应用开发》
- 点赞
- 收藏
- 关注作者
评论(0)