背景

目前在实习,对go web开发不太了解。正好借此次笔记活动记录自己学习的过程以及遇到的问题的解决办法。

环境安装

(我用的vscode,常用的还有Goland)

wsl安装

vscode安装

ubuntu安装

docker安装

go安装

mysql安装

redis安装

grpc库+protoc(2/3,看到抖音项目接口文档里用的是2)+protoc-gen-go+protobuf编译工具安装

(百度解决大部分问题,此处不记录安装过程的问题,已经忘了.jpg,或许有空会补链接)

upd:已经发布了安装过程教程地址

gRPC简单介绍

RPC的全称是Remote Procedure Call,远程过程调用。这是一种协议,是用来屏蔽分布式计算中的各种调用细节,使得你可以像是本地调用一样直接调用一个远程的函数。

gRPC,用官方的话来说:
A high-performance, open-source universal RPC framework。 gRPC是一个高性能的、开源的通用的RPC框架。 在gRPC中,我们称调用方为client,被调用方为server。

跟其他的RPC框架一样,gRPC也是基于”服务定义“的思想。简单的来讲,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个”服务定义“的过程中,我们描述了我们提供的服务服务名是什么,有哪些方法可以被调用,这些方法有什么样的入参,有什么样的回参。因此,gRPC使用了Protocol Buffers

这是官方文档:Go | gRPC

gRPC的特性

  1. 使用Protocal Buffers——强大的结构数据序列化工具
  2. 可以跨语言使用
  3. 安装简单,扩展方便(用该框架每秒可达到百万个RPC)
  4. 基于HTTP2协议

gRPC使用流程

  1. 定义标准的proto文件
  2. 生成标准代码
  3. 服务端使用生成的代码提供服务
  4. 客户端使用生成的代码调用服务

笔记链接

过程

示例项目:使用 Protocol Buffers 作为前后端接口,开发一个简单的留言板message

项目结构

├── README.md
├── client
├── proto
└── server

定义标准的proto文件

在文件的第一行,我们写上:

syntax = "proto3";

说明我们使用的是proto3语法。

然后写上:

option go_package = "生成文件包位置;message";

表示最后生成的go文件处于哪个包中,.表示在当前目录生成。

定义message关键字,它的功能类似于Go中的结构体:

message Message {
	string name = 1; //不是赋值,定义这个变量在Message中的位置
	string title = 2;
}

定义一个服务,服务中需要有一个方法——接受客户端的参数,再返回服务端的相应。

service MessageService {
	rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) {}
}

我们的SendMessageRequestSendMessageResponse可以定义成以下这样:

message SendMessageRequest {
  string name = 1;
}

message SendMessageResponse {
  string responseName = 1;
}

生成标准代码

在proto文件中写完这些后,在proto文件目录下执行以下命令:

protoc --go_out=. message.proto
protoc --go-grpc_out=. message.proto

可以看到当前的目录生成了message.pb.gomessage_grpc.pb.go

可能需要注意的问题:

  • 更新版本的protoc-gen-go,但是用了旧版本的生成命令。
  • google/github版本的生成命令和生成文件不同。
  • 安装protoc时遇到的小坑,运行报错:
google/protobuf/xxxx.proto: File not found.
message/message.proto:5:1: Import “google/protobuf/xxxx.proto” was not found or had errors
message/message.proto:21:5: “google.protobuf.xxxx is not defined.

原因:文件夹里有readme.txt, 没仔细阅读。

解决:需要尝试着把 include/google 移动到 本机的 /usr/local/include/google 重新生成。

  • 找不到我们要import的proto文件
Import "google/api/annotations.proto" was not found or had errors.

尝试下载mod,但无法指定依赖,暂未找到原因。

歪解:把引用该包的句子删了。

更详细的资料:Release v1.20.0 · protocolbuffers/protobuf-go (github.com)

服务端

注册

在server目录下创建server.go,加入以下代码:

type server struct {
	Send func(context.Context, *SendMessageRequest) (*SendMessageResponse, error)
}

func CreateGrpcServer() *grpc.Server {
	s := grpc.NewServer()
	pb.RegisterMessageServiceServer(s, &server{})
	return s
}

这部分我们创建了一个GrpcServer,注册了Service。在server实例中有一个方法,这个方法就是我们定义的Send方法,我们需要在服务端实现这个方法:

func SendMessage(ctx context.Context, req *SendMessageRequest) (*Message, error) {
    log.Println("receive message:", req.Get())
    resp := &SendMessageResponse{}
    resp.Response = "!"
	return nil, status.Errorf(codes.Unimplemented, "method GetMessage not implemented")
}

那么已成功在服务端实现我们声明的方法。

监听

类似于web服务器,先创建Handler再对端口进行监听。

lis, err := net.Listen("tcp", port)
if err != nil {
	log.Fatalf("failed to listen: %v", err)
}
s := impl.CreateGrpcServer()
reflection.Register(s)
if err := s.Serve(lis); err != nil {
	log.Fatalf("failed to serve: %v", err)
}

监听port端口的TCP连接,然后启动服务器。

那么服务端的开发已经完毕~

客户端

在客户端中,我们应该先与服务端建立连接,然后才能够调用各种方法。先跟port端口建立连接。

conn, err := grpc.Dial(port, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
	log.Fatalf("did not connect: %v", err)
}
defer conn.Close()

按照定义,我们调用服务端的方法。

client := message.NewMessageServiceClient(conn)
resp, err := client.Send(context.Background(), &SendMessageRequest{})
if err != nil {
   log.Fatalf("could not greet: %v", err)
}

即在本地创建一个client,然后调用早就定义好的Send方法,就可以将服务端和客户端都运行起来。

至此message的开发完毕~

整理一下:

我们在.proto文件中定义方法,在服务端实现定义的RPC方法的具体逻辑,然后在客户端调用方法

其他的部分,由proto buffer负责对Golang中存储的数据结构与RPC传输中的数据进行转换gRPC负责封装所有的逻辑

写在最后

  • 这些代码可以进一步完善,此处只做了简要的记录和说明,如有错误/修改意见请读者联系我~