2. Go 语言环境搭建与 gRPC 基础
本章将带你从零搭建 Go 与 gRPC 开发环境,并完成第一个 Hello World 级别的 gRPC 服务。这是后续所有缓存一致性实战的基础设施,请务必按步骤操作,避免版本不一致导致的问题。
2.1 环境依赖总览
gRPC 开发需要以下核心组件协同工作:
| 组件 | 版本建议 | 作用 |
|---|---|---|
| Go | ≥ 1.21 | 编译运行 gRPC 服务端与客户端 |
| Protocol Buffers 编译器 (protoc) | ≥ 25.0 | 将 .proto 文件编译为 Go 代码 |
| protoc-gen-go | ≥ 1.31 | protoc 的 Go 语言代码生成插件 |
| protoc-gen-go-grpc | ≥ 1.3 | 生成 gRPC 服务端/客户端桩代码 |
| google.golang.org/grpc | 最新稳定版 | Go gRPC 运行时库 |
版本对齐提示: 本课程所有代码均基于 Go 1.22 + protoc 27.0 + grpc-go v1.64 验证。请尽量使用相近版本,避免因 API 变更导致编译失败。
2.2 安装步骤
2.2.1 安装 Go
- 访问
https://go.dev/dl/下载对应操作系统的安装包。 - Linux/macOS 建议解压到
/usr/local/go,并配置环境变量:
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
- 验证安装:
go version
# 输出示例:go version go1.22.3 linux/amd64
2.2.2 安装 protoc
- 从
https://github.com/protocolbuffers/protobuf/releases下载对应系统的protoc-<version>-<os>-<arch>.zip。 - 解压后将
bin/protoc放入PATH:
unzip protoc-27.0-linux-x86_64.zip -d $HOME/protoc
export PATH=$HOME/protoc/bin:$PATH
- 验证:
protoc --version
# 输出示例:libprotoc 27.0
2.2.3 安装 Go 代码生成插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
确保 $GOPATH/bin 在 PATH 中,验证插件可用:
which protoc-gen-go
which protoc-gen-go-grpc
常见陷阱: 如果
protoc 找不到 protoc-gen-go,请检查 $GOPATH/bin 是否已加入 PATH,或使用 protoc --plugin=protoc-gen-go=$GOPATH/bin/protoc-gen-go 显式指定路径。
2.3 编写第一个 gRPC 服务:Hello World
我们将实现一个简单的 Greeter 服务,客户端发送名字,服务端返回问候语。这是 gRPC 的“Hello World”,也是后续缓存一致性服务的骨架。
2.3.1 项目结构
grpc-helloworld/
├── go.mod
├── proto/
│ └── helloworld.proto
├── server/
│ └── main.go
└── client/
└── main.go
2.3.2 定义 .proto 文件
创建 proto/helloworld.proto:
syntax = "proto3";
package helloworld;
option go_package = "grpc-helloworld/proto/helloworld";
// 定义 Greeter 服务
service Greeter {
// 一元 RPC:客户端发送请求,服务端返回响应
rpc SayHello (HelloRequest) returns (HelloReply);
}
// 请求消息:包含用户名
message HelloRequest {
string name = 1;
}
// 响应消息:包含问候语
message HelloReply {
string message = 1;
}
2.3.3 编译 .proto 文件
在项目根目录执行:
protoc --go_out=. --go-grpc_out=. proto/helloworld.proto
生成的文件结构:
proto/helloworld/
├── helloworld.pb.go // 消息序列化代码
└── helloworld_grpc.pb.go // gRPC 服务/客户端桩代码
2.3.4 实现服务端
server/main.go:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "grpc-helloworld/proto/helloworld"
)
// 服务端结构体,必须嵌入 UnimplementedGreeterServer
type server struct {
pb.UnimplementedGreeterServer
}
// 实现 SayHello 方法
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("收到请求: name=%s", req.GetName())
return &pb.HelloReply{Message: "Hello " + req.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("监听失败: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Println("gRPC 服务端启动于 :50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("服务启动失败: %v", err)
}
}
2.3.5 实现客户端
client/main.go:
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "grpc-helloworld/proto/helloworld"
)
func main() {
// 连接到服务端(本示例使用不安全连接)
conn, err := grpc.Dial("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("连接失败: %v", err)
}
defer conn.Close()
client := pb.NewGreeterClient(conn)
// 设置超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// 调用远程方法
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "gRPC 实战"})
if err != nil {
log.Fatalf("调用失败: %v", err)
}
log.Printf("服务端响应: %s", resp.GetMessage())
}
2.3.6 运行测试
启动服务端:
cd server && go run main.go
新终端运行客户端:
cd client && go run main.go
# 输出:服务端响应: Hello gRPC 实战
成功标志: 客户端打印出 "Hello gRPC 实战",说明你的 gRPC 环境搭建完成,第一个服务已跑通。
2.4 gRPC 调用流程解析
下图展示了 Hello World 示例中一次完整的一元 RPC 调用流程:
2.5 关键概念速查表
| 概念 | 说明 | 对应代码位置 |
|---|---|---|
| Service Definition | 在 .proto 中定义 RPC 方法签名 | service Greeter { rpc SayHello(...) } |
| Unimplemented 嵌入 | 保证向后兼容,未实现的方法返回 UNIMPLEMENTED | type server struct { pb.UnimplementedGreeterServer } |
| Register 函数 | 将服务实现注册到 gRPC 服务器 | pb.RegisterGreeterServer(s, &server{}) |
| Client Stub | 自动生成的客户端代理,封装远程调用细节 | pb.NewGreeterClient(conn) |
| Context | 传递超时、取消信号、元数据 | context.WithTimeout(...) |
2.6 常见问题与排查
- protoc 找不到插件: 确认
protoc-gen-go和protoc-gen-go-grpc在PATH中,或使用--plugin参数。 - go_package 路径错误:
option go_package必须与项目模块路径一致,否则生成的 import 路径会出错。 - 连接被拒绝: 确保服务端先启动,且端口未被占用;检查防火墙设置。
- 版本不兼容: grpc-go 的某些版本要求特定的 protoc-gen-go-grpc 版本,请参考 grpc-go 的 release notes。
调试技巧: 使用
grpcurl 工具可以无需编写客户端代码,直接通过命令行调用 gRPC 服务,非常适合快速验证服务端是否正常工作。