快速体验 Quickstart¶
本章将通过一个经典的 Greeter 问候服务,带你在 10 分钟内完成从编写 .proto 契约、生成代码到运行 gRPC 服务端与客户端的全流程。
1. 核心流程三部曲¶
flowchart LR
Step1["1. 编写 .proto 契约文件"] --> Step2["2. protoc 编译生成代码"]
Step2 --> Step3["3. 实现服务端并启动监听"]
Step2 --> Step4["4. 客户端建立连接发起 RPC"]
Step3 -.-> Step4
2. 步骤一:定义接口契约 (helloworld.proto)¶
创建接口契约文件 helloworld.proto,声明服务与消息结构:
syntax = "proto3";
package helloworld;
// 指定 Go 生成代码的包路径
option go_package = "example.com/grpc-demo/helloworld";
// 定义问候服务
service Greeter {
// 单项 RPC 方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 客户端请求载荷
message HelloRequest {
string name = 1;
}
// 服务端响应载荷
message HelloReply {
string message = 1;
}
[!NOTE] 这里的
= 1不是赋默认值,而是该字段在二进制 Wire 传输中的 唯一标识序号(Field Tag Number)。
3. 步骤二:生成目标语言代码¶
[!TIP] 如果本地尚未安装
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
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld.proto
helloworld.pb.go:包含结构体定义与序列化反序列化代码。
- helloworld_grpc.pb.go:包含服务端抽象接口(GreeterServer)与客户端桩(GreeterClient)。
4. 步骤三:编写服务端代码¶
package main
import (
"context"
"fmt"
"log"
"net"
pb "example.com/grpc-demo/helloworld"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("收到客户端请求: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName() + "!"}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("无法监听端口: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
fmt.Println("gRPC 服务端启动在端口 :50051 ...")
if err := s.Serve(lis); err != nil {
log.Fatalf("启动失败: %v", err)
}
}
from concurrent import futures
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
logging.info("收到客户端请求: %s", request.name)
return helloworld_pb2.HelloReply(message=f"Hello, {request.name}!")
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
logging.info("gRPC 服务端已在 50051 端口启动...")
server.start()
server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
serve()
5. 步骤四:编写客户端代码并调用¶
package main
import (
"context"
"log"
"time"
pb "example.com/grpc-demo/helloworld"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
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()
r, err := client.SayHello(ctx, &pb.HelloRequest{Name: "Gopher"})
if err != nil {
log.Fatalf("调用错误: %v", err)
}
log.Printf("来自服务端的应答: %s", r.GetMessage())
}
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='Pythonista'), timeout=5.0)
logging.info("来自服务端的应答: %s", response.message)
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
run()
6. 核心要点回顾¶
- 强类型保证:编译期自动生成的代码提供了类型提示,避免传参错误。
- 连接透明:客户端通过
grpc.Dial或grpc.insecure_channel维护底层 HTTP/2 传输,在业务逻辑中直接以常规函数形式发起 RPC。 - 超时保护:所有 RPC 必须设置超时时间(如
context.WithTimeout或timeout=5.0),这是生产级调用的核心准则。