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

  1. 访问 https://go.dev/dl/ 下载对应操作系统的安装包。
  2. Linux/macOS 建议解压到 /usr/local/go,并配置环境变量:
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
  1. 验证安装:
go version
# 输出示例:go version go1.22.3 linux/amd64

2.2.2 安装 protoc

  1. 从 https://github.com/protocolbuffers/protobuf/releases 下载对应系统的 protoc-<version>-<os>-<arch>.zip。
  2. 解压后将 bin/protoc 放入 PATH:
unzip protoc-27.0-linux-x86_64.zip -d $HOME/protoc
export PATH=$HOME/protoc/bin:$PATH
  1. 验证:
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 调用流程:

gRPC Hello World 调用流程 gRPC 一元 RPC 调用流程 Go 客户端 SayHello(name) HTTP/2 传输 Protobuf 序列化 Go 服务端 SayHello 实现 请求 请求 处理业务逻辑 响应 响应 接收响应 关键步骤: 1. 客户端将请求对象序列化为 Protobuf 二进制格式 2. 通过 HTTP/2 流式传输到服务端,服务端反序列化并处理 3. 服务端将响应序列化后返回,客户端反序列化得到结果

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 服务,非常适合快速验证服务端是否正常工作。