跳转至

Go 语言生产级工程实践指南

在工业级微服务开发中,Go 语言以其原生协程并发模型和高性能运行时,成为了 gRPC 技术栈最为普及的一门语言。


1. 推荐工程目录结构

遵循标准 Go 工程布局规范(Standard Go Project Layout):

my-grpc-service/
├── api/
│   └── v1/
│       ├── order.proto          # 接口定义源文件
│       ├── order.pb.go          # protoc 生成的消息代码
│       └── order_grpc.pb.go     # protoc 生成的 gRPC 桩代码
├── cmd/
│   ├── server/
│   │   └── main.go              # 服务端主入口
│   └── client/
│       └── main.go              # 客户端测试或 CLI 工具
├── internal/
│   ├── service/
│   │   └── order_service.go     # 业务逻辑与接口实现
│   └── interceptor/
│       ├── logging.go           # 日志中间件
│       └── recovery.go          # Panic 恢复中间件
├── go.mod
├── go.sum
└── Makefile                     # 编译生成命令脚本

2. 契约定义 (api/v1/order.proto)

syntax = "proto3";

package order.v1;

option go_package = "my-grpc-service/api/v1;orderv1";

service OrderService {
  // 一元调用:创建订单
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);

  // 服务端流:实时跟踪订单履约状态流
  rpc TrackOrder(TrackOrderRequest) returns (stream TrackOrderResponse);
}

message CreateOrderRequest {
  string user_id = 1;
  double amount = 2;
  repeated string item_ids = 3;
}

message CreateOrderResponse {
  string order_id = 1;
  string status = 2;
}

message TrackOrderRequest {
  string order_id = 1;
}

message TrackOrderResponse {
  string status = 1;
  string message = 2;
  int64 timestamp = 3;
}

编译命令:

protoc -I. \
  --go_out=. --go_opt=paths=source_relative \
  --go-grpc_out=. --go-grpc_opt=paths=source_relative \
  api/v1/order.proto


3. 服务端生产级实现 (cmd/server/main.go)

涵盖拦截器链、优雅停机(Graceful Shutdown)与标准健康检查(Health Check):

package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "os"
    "os/signal"
    "syscall"
    "time"

    pb "my-grpc-service/api/v1"

    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/health"
    healthpb "google.golang.org/grpc/health/grpc_health_v1"
    "google.golang.org/grpc/status"
)

type orderServiceServer struct {
    pb.UnimplementedOrderServiceServer
}

func (s *orderServiceServer) CreateOrder(ctx context.Context, req *pb.CreateOrderRequest) (*pb.CreateOrderResponse, error) {
    if req.GetUserId() == "" {
        return nil, status.Error(codes.InvalidArgument, "user_id 不能为空")
    }
    orderID := fmt.Sprintf("ord_%d", time.Now().UnixNano())
    return &pb.CreateOrderResponse{
        OrderId: orderID,
        Status:  "CREATED",
    }, nil
}

func (s *orderServiceServer) TrackOrder(req *pb.TrackOrderRequest, stream pb.OrderService_TrackOrderServer) error {
    steps := []string{"订单已确认", "商品拣货中", "顺丰速递已揽件", "派送中", "已签收"}
    for _, step := range steps {
        // 监听客户端是否提前取消监听
        if err := stream.Context().Err(); err != nil {
            return status.FromContextError(err).Err()
        }

        err := stream.Send(&pb.TrackOrderResponse{
            Status:    step,
            Message:   fmt.Sprintf("订单 %s 状态已变更: %s", req.GetOrderId(), step),
            Timestamp: time.Now().Unix(),
        })
        if err != nil {
            return err
        }
        time.Sleep(500 * time.Millisecond) // 模拟状态推送间隔
    }
    return nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("端口监听失败: %v", err)
    }

    // 创建 gRPC 服务端并挂载拦截器
    server := grpc.NewServer()

    // 注册业务服务
    orderSvc := &orderServiceServer{}
    pb.RegisterOrderServiceServer(server, orderSvc)

    // 注册官方标准 Health Check 协议服务
    healthServer := health.NewServer()
    healthServer.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
    healthServer.SetServingStatus("order.v1.OrderService", healthpb.HealthCheckResponse_SERVING)
    healthpb.RegisterHealthServer(server, healthServer)

    // 协程启动监听
    go func() {
        log.Println("gRPC 生产服务启动在 :50051 ...")
        if err := server.Serve(lis); err != nil && err != grpc.ErrServerStopped {
            log.Fatalf("Serve 发生异常: %v", err)
        }
    }()

    // 优雅停机信号监听 (SIGINT, SIGTERM)
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("收到关机信号,执行优雅停止...")
    // 设置健康检查状态为 NOT_SERVING,通知负载均衡器摘除流量
    healthServer.SetServingStatus("", healthpb.HealthCheckResponse_NOT_SERVING)

    // GracefulStop 会等待所有活跃 RPC 运行结束并拒绝新请求
    server.GracefulStop()
    log.Println("gRPC 服务平滑退出完毕")
}

4. 客户端生产级实践 (cmd/client/main.go)

涵盖连接单例复用、超时控制与服务流消费:

package main

import (
    "context"
    "io"
    "log"
    "time"

    pb "my-grpc-service/api/v1"

    "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.NewOrderServiceClient(conn)

    // 1. 一元调用 (必须带超时)
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    createResp, err := client.CreateOrder(ctx, &pb.CreateOrderRequest{
        UserId:  "u-9988",
        Amount:  199.9,
        ItemIds: []string{"item-1", "item-2"},
    })
    if err != nil {
        log.Fatalf("创建订单错误: %v", err)
    }
    log.Printf("订单创建成功: ID=%s, Status=%s", createResp.GetOrderId(), createResp.GetStatus())

    // 2. 服务端流式消费
    streamCtx, streamCancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer streamCancel()

    stream, err := client.TrackOrder(streamCtx, &pb.TrackOrderRequest{
        OrderId: createResp.GetOrderId(),
    })
    if err != nil {
        log.Fatalf("订阅订单流失败: %v", err)
    }

    for {
        item, err := stream.Recv()
        if err == io.EOF {
            log.Println("服务端流传输正常结束")
            break
        }
        if err != nil {
            log.Fatalf("接收流数据异常: %v", err)
        }
        log.Printf("[流事件] 状态: %s, 详情: %s", item.GetStatus(), item.GetMessage())
    }
}