Go gRPC 学习笔记
一、环境准备
1. 安装 Go(>=1.20)
确保已安装 Go,并设置好 GOPATH(现代 Go 一般用 module,无需手动设 GOPATH)。
go version
2. 安装 Protocol Buffer 编译器(protoc)
macOS(用 Homebrew):
brew install protobuf
Linux(Ubuntu/Debian):
sudo apt install -y protobuf-compiler
Windows:
从 https://github.com/protocolbuffers/protobuf/releases 下载并解压,加入 PATH。
验证:
protoc --version
# 应输出:libprotoc x.x.x
3. 安装 Go 的 protoc 插件(用于生成 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(通常是~/go/bin),请确保该路径在你的PATH环境变量中。
验证:
protoc-gen-go --version
protoc-gen-go-grpc --version
二、定义 .proto 文件
文件路径:proto/helloworld.proto
// 指定使用 proto3 语法(最新版)
syntax = "proto3";
// 指定生成的 Go 包路径(相对于项目根目录)
// 注意:必须与实际 Go import 路径一致
option go_package = "./proto";
// 定义包名(避免命名冲突)
package helloworld;
// 定义一个 gRPC 服务 Greeter
service Greeter {
// 定义一个 RPC 方法 SayHello
// 输入是 HelloRequest,输出是 HelloReply
rpc SayHello (HelloRequest) returns (HelloReply);
}
// 定义请求消息结构
message HelloRequest {
string name = 1; // 字段编号 1,用于序列化
}
// 定义响应消息结构
message HelloReply {
string message = 1; // 字段编号 1
}
📌 执行命令生成代码:
protoc --go_out=. --go-grpc_out=. proto/helloworld.proto
三、gRPC Server
文件路径:server/main.go
package main
import (
"context" // 上下文,用于超时/取消
"log" // 日志
"net" // 网络监听
"grpc-demo/proto" // 导入生成的 proto 代码(根据你的 module 名调整)
"google.golang.org/grpc"
)
// 定义 server 结构体,嵌入 UnimplementedGreeterServer
// 这样即使只实现部分方法也不会报错(兼容未来新增方法)
type server struct {
proto.UnimplementedGreeterServer
}
// 实现 SayHello 方法(必须与 .proto 中定义的方法签名一致)
func (s *server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloReply, error) {
// 构造响应:拼接字符串
return &proto.HelloReply{Message: "Hello, " + req.GetName()}, nil
}
func main() {
// 监听 TCP 端口 50051(gRPC 默认端口)
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("监听失败: %v", err)
}
// 创建 gRPC 服务器实例
grpcServer := grpc.NewServer()
// 注册 Greeter 服务到服务器(传入我们实现的 server 实例)
proto.RegisterGreeterServer(grpcServer, &server{})
log.Println("gRPC 服务器启动,监听 :50051")
// 启动服务,阻塞等待连接
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("服务器启动失败: %v", err)
}
}
四、gRPC Client
文件路径:client/main.go
package main
import (
"context"
"log"
"time"
"grpc-demo/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" // 用于不加密连接
)
func main() {
// 创建与 gRPC 服务器的连接
// 使用 insecure 凭据(无 TLS),仅用于测试
conn, err := grpc.Dial(
"localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("无法连接服务器: %v", err)
}
// 程序退出前关闭连接
defer conn.Close()
// 创建 Greeter 客户端(由 protoc 自动生成)
client := proto.NewGreeterClient(conn)
// 创建带 1 秒超时的上下文(防止卡死)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// 调用远程 SayHello 方法(像调本地函数一样)
resp, err := client.SayHello(ctx, &proto.HelloRequest{Name: "Go"})
if err != nil {
log.Fatalf("调用 SayHello 失败: %v", err)
}
// 打印服务器返回的消息
log.Printf("收到回复: %s", resp.GetMessage())
}
五、高级功能 1:拦截器(Interceptor)
什么是拦截器?
- 类似中间件,可在 RPC 调用前后执行逻辑(如日志、认证、监控等)
- 分为 Unary(一元) 和 Stream(流式) 拦截器
示例:添加 Server 端日志拦截器
修改 server/main.go 的 main() 函数:
// 新增:定义一个简单的 Unary 拦截器
func loggingInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
// 调用前记录
log.Printf("收到 RPC 请求: %s", info.FullMethod)
// 继续处理请求
resp, err := handler(ctx, req)
// 调用后记录(可选)
if err != nil {
log.Printf("RPC 返回错误: %v", err)
} else {
log.Printf("RPC 成功完成: %s", info.FullMethod)
}
return resp, err
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("监听失败: %v", err)
}
// 创建带拦截器的 gRPC 服务器
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor), // 注册拦截器
)
proto.RegisterGreeterServer(grpcServer, &server{})
log.Println("带拦截器的 gRPC 服务器启动")
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("服务器启动失败: %v", err)
}
}
✅ 现在每次调用
SayHello都会打印日志!
六、高级功能 2:启用 TLS 加密通信
步骤 1:生成自签名证书(测试用)
# 生成私钥
openssl genrsa -out server.key 2048
# 生成证书(Common Name 填 localhost)
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
会提示输入信息,Common Name 必须为
localhost(或你实际访问的域名)
步骤 2:修改 Server(启用 TLS)
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
// ... 其他导入
)
func main() {
// 读取证书和私钥
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatalf("加载证书失败: %v", err)
}
// 创建 TLS 配置
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.NoClientCert, // 不验证客户端证书(单向 TLS)
}
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatal(err)
}
// 使用 TLS 凭据创建 gRPC 服务器
grpcServer := grpc.NewServer(
grpc.Creds(credentials.NewTLS(tlsConfig)),
)
proto.RegisterGreeterServer(grpcServer, &server{})
log.Println("gRPC 服务器(TLS)启动")
grpcServer.Serve(lis)
}
步骤 3:修改 Client(信任服务器证书)
// 读取服务器证书(用于验证)
creds, err := credentials.NewClientTLSFromFile("server.crt", "localhost")
if err != nil {
log.Fatalf("加载客户端 TLS 凭据失败: %v", err)
}
// 使用 TLS 连接
conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
// ... 后续不变
🔒 现在通信已加密!生产环境应使用正式 CA 签发的证书。
七、总结
| 功能 | 说明 |
|---|---|
.proto | 定义服务和消息 |
protoc | 生成 Go 代码 |
| Server | 实现接口 + 启动服务 |
| Client | 创建连接 + 调用方法 |
| 拦截器 | 添加通用逻辑(日志/认证等) |
| TLS | 加密通信,提升安全性 |
💡 初学建议:先掌握基本 Server/Client,再逐步加入拦截器和 TLS。
打赏下吧