跳转至

快速体验 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)。

安装 Python gRPC 工具链:

pip install grpcio grpcio-tools
执行编译命令:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. helloworld.proto
生成产物: - helloworld_pb2.py:消息类型定义。 - helloworld_pb2_grpc.py:服务与客户端桩类定义。


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. 核心要点回顾

  1. 强类型保证:编译期自动生成的代码提供了类型提示,避免传参错误。
  2. 连接透明:客户端通过 grpc.Dialgrpc.insecure_channel 维护底层 HTTP/2 传输,在业务逻辑中直接以常规函数形式发起 RPC。
  3. 超时保护:所有 RPC 必须设置超时时间(如 context.WithTimeouttimeout=5.0),这是生产级调用的核心准则。