40分钟学 Go 语言高并发:RPC服务开发实战

RPC服务开发实战

一、RPC服务基础概览

开发阶段 关键点 重要程度 考虑因素
接口设计 API定义、协议选择、版本控制 ⭐⭐⭐⭐⭐ 可扩展性、兼容性
服务实现 业务逻辑、并发处理、资源管理 ⭐⭐⭐⭐⭐ 性能、可靠性
错误处理 异常捕获、错误码、故障恢复 ⭐⭐⭐⭐ 稳定性、可维护性
性能测试 负载测试、压力测试、基准测试 ⭐⭐⭐⭐ 性能指标、瓶颈分析

让我们通过一个完整的订单服务示例来学习RPC服务开发:

go 复制代码
syntax = "proto3";

package order;
option go_package = "./pb";

// 订单服务定义
service OrderService {
    // 创建订单
    rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
    // 查询订单
    rpc GetOrder (GetOrderRequest) returns (GetOrderResponse);
    // 更新订单状态
    rpc UpdateOrderStatus (UpdateOrderStatusRequest) returns (UpdateOrderStatusResponse);
    // 取消订单
    rpc CancelOrder (CancelOrderRequest) returns (CancelOrderResponse);
    // 订单状态流式更新
    rpc WatchOrderStatus (WatchOrderRequest) returns (stream OrderStatusUpdate);
}

// 订单信息
message Order {
    string order_id = 1;
    string user_id = 2;
    repeated OrderItem items = 3;
    OrderStatus status = 4;
    double total_amount = 5;
    int64 created_at = 6;
    int64 updated_at = 7;
}

// 订单项
message OrderItem {
    string product_id = 1;
    int32 quantity = 2;
    double price = 3;
}

// 订单状态
enum OrderStatus {
    UNKNOWN = 0;
    PENDING = 1;
    PAID = 2;
    PROCESSING = 3;
    SHIPPED = 4;
    DELIVERED = 5;
    CANCELLED = 6;
}

// 创建订单请求
message CreateOrderRequest {
    string user_id = 1;
    repeated OrderItem items = 2;
}

// 创建订单响应
message CreateOrderResponse {
    Order order = 1;
}

// 查询订单请求
message GetOrderRequest {
    string order_id = 1;
}

// 查询订单响应
message GetOrderResponse {
    Order order = 1;
}

// 更新订单状态请求
message UpdateOrderStatusRequest {
    string order_id = 1;
    OrderStatus status = 2;
}

// 更新订单状态响应
message UpdateOrderStatusResponse {
    Order order = 1;
}

// 取消订单请求
message CancelOrderRequest {
    string order_id = 1;
    string reason = 2;
}

// 取消订单响应
message CancelOrderResponse {
    bool success = 1;
    string message = 2;
}

// 监听订单状态请求
message WatchOrderRequest {
    string order_id = 1;
}

// 订单状态更新
message OrderStatusUpdate {
    string order_id = 1;
    OrderStatus status = 2;
    string message = 3;
    int64 timestamp = 4;
}

现在实现服务端代码:

go 复制代码
package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "sync"
    "time"

    pb "your_project/pb"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)

type orderServer struct {
    pb.UnimplementedOrderServiceServer
    mu         sync.RWMutex
    orders     map[string]*pb.Order
    statusSubs map[string][]chan *pb.OrderStatusUpdate
}

func newOrderServer() *orderServer {
    return &orderServer{
        orders:     make(map[string]*pb.Order),
        statusSubs: make(map[string][]chan *pb.OrderStatusUpdate),
    }
}

// 生成订单ID
func generateOrderID() string {
    return fmt.Sprintf("ORD%d", time.Now().UnixNano())
}

// 计算订单总金额
func calculateTotal(items []*pb.OrderItem) float64 {
    var total float64
    for _, item := range items {
        total += float64(item.Quantity) * item.Price
    }
    return total
}

// 创建订单
func (s *orderServer) CreateOrder(ctx context.Context, req *pb.CreateOrderRequest) (*pb.CreateOrderResponse, error) {
    if req.UserId == "" {
        return nil, status.Error(codes.InvalidArgument, "user_id is required")
    }
    if len(req.Items) == 0 {
        return nil, status.Error(codes.InvalidArgument, "order items cannot be empty")
    }

    now := time.Now().Unix()
    order := &pb.Order{
        OrderId:     generateOrderID(),
        UserId:      req.UserId,
        Items:       req.Items,
        Status:      pb.OrderStatus_PENDING,
        TotalAmount: calculateTotal(req.Items),
        CreatedAt:   now,
        UpdatedAt:   now,
    }

    s.mu.Lock()
    s.orders[order.OrderId] = order
    s.mu.Unlock()

    return &pb.CreateOrderResponse{Order: order}, nil
}

// 查询订单
func (s *orderServer) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.GetOrderResponse, error) {
    s.mu.RLock()
    order, exists := s.orders[req.OrderId]
    s.mu.RUnlock()

    if !exists {
        return nil, status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)
    }

    return &pb.GetOrderResponse{Order: order}, nil
}

// 更新订单状态
func (s *orderServer) UpdateOrderStatus(ctx context.Context, req *pb.UpdateOrderStatusRequest) (*pb.UpdateOrderStatusResponse, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

    order, exists := s.orders[req.OrderId]
    if !exists {
        return nil, status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)
    }

    // 状态校验
    if !isValidStatusTransition(order.Status, req.Status) {
        return nil, status.Errorf(codes.FailedPrecondition, 
            "invalid status transition from %v to %v", order.Status, req.Status)
    }

    order.Status = req.Status
    order.UpdatedAt = time.Now().Unix()

    // 通知订阅者
    s.notifyStatusUpdate(order)

    return &pb.UpdateOrderStatusResponse{Order: order}, nil
}

// 取消订单
func (s *orderServer) CancelOrder(ctx context.Context, req *pb.CancelOrderRequest) (*pb.CancelOrderResponse, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

    order, exists := s.orders[req.OrderId]
    if !exists {
        return nil, status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)
    }

    if order.Status != pb.OrderStatus_PENDING && order.Status != pb.OrderStatus_PAID {
        return &pb.CancelOrderResponse{
            Success: false,
            Message: "order cannot be cancelled in current status",
        }, nil
    }

    order.Status = pb.OrderStatus_CANCELLED
    order.UpdatedAt = time.Now().Unix()

    // 通知订阅者
    s.notifyStatusUpdate(order)

    return &pb.CancelOrderResponse{
        Success: true,
        Message: "order cancelled successfully",
    }, nil
}

// 监听订单状态
func (s *orderServer) WatchOrderStatus(req *pb.WatchOrderRequest, stream pb.OrderService_WatchOrderStatusServer) error {
    if req.OrderId == "" {
        return status.Error(codes.InvalidArgument, "order_id is required")
    }

    s.mu.RLock()
    _, exists := s.orders[req.OrderId]
    s.mu.RUnlock()

    if !exists {
        return status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)
    }

    // 创建状态更新通道
    updates := make(chan *pb.OrderStatusUpdate, 10)
    s.mu.Lock()
    s.statusSubs[req.OrderId] = append(s.statusSubs[req.OrderId], updates)
    s.mu.Unlock()

    defer func() {
        s.mu.Lock()
        // 移除订阅
        subs := s.statusSubs[req.OrderId]
        for i, ch := range subs {
            if ch == updates {
                s.statusSubs[req.OrderId] = append(subs[:i], subs[i+1:]...)
                break
            }
        }
        s.mu.Unlock()
        close(updates)
    }()

    // 监听状态更新
    for update := range updates {
        if err := stream.Send(update); err != nil {
            return err
        }
    }

    return nil
}

// 通知订阅者状态更新
func (s *orderServer) notifyStatusUpdate(order *pb.Order) {
    update := &pb.OrderStatusUpdate{
        OrderId:   order.OrderId,
        Status:    order.Status,
        Message:   fmt.Sprintf("Order status updated to %v", order.Status),
        Timestamp: time.Now().Unix(),
    }

    // 发送更新给所有订阅者
    for _, ch := range s.statusSubs[order.OrderId] {
        select {
        case ch <- update:
        default:
            // channel已满,跳过
        }
    }
}

// 验证状态转换是否有效
func isValidStatusTransition(from, to pb.OrderStatus) bool {
    switch from {
    case pb.OrderStatus_PENDING:
        return to == pb.OrderStatus_PAID || to == pb.OrderStatus_CANCELLED
    case pb.OrderStatus_PAID:
        return to == pb.OrderStatus_PROCESSING || to == pb.OrderStatus_CANCELLED
    case pb.OrderStatus_PROCESSING:
        return to == pb.OrderStatus_SHIPPED
    case pb.OrderStatus_SHIPPED:
        return to == pb.OrderStatus_DELIVERED
    default:
        return false
    }
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    s := grpc.NewServer(
        grpc.UnaryInterceptor(orderInterceptor),
    )
    pb.RegisterOrderServiceServer(s, newOrderServer())

    log.Println("Order server starting on :50051...")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

// 订单服务拦截器
func orderInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 
    handler grpc.UnaryHandler) (interface{}, error) {
    
    start := time.Now()
    resp, err := handler(ctx, req)
    duration := time.Since(start)

    // 记录请求日志
    log.Printf(
        "Method: %s, Duration: %v, Error: %v",
        info.FullMethod,
        duration,
        err,
    )

    return resp, err
}

让我们创建服务的工作流程图:

现在添加性能测试代码:

go 复制代码
package main

import (
    "context"
    "log"
    "sync"
    "testing"
    "time"

    pb "your_project/pb"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func BenchmarkCreateOrder(b *testing.B) {
    // 建立连接
    conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        b.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewOrderServiceClient(conn)
    ctx := context.Background()

    // 准备测试数据
    testOrder := &pb.CreateOrderRequest{
        UserId: "test-user",
        Items: []*pb.OrderItem{
            {
                ProductId: "prod-1",
                Quantity: 1,
                Price:    99.99,
            },
        },
    }

    b.ResetTimer()
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            _, err := client.CreateOrder(ctx, testOrder)
            if err != nil {
                b.Errorf("CreateOrder failed: %v", err)
            }
        }
    })
}

// 并发压力测试
func TestOrderConcurrency(t *testing.T) {
    conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        t.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewOrderServiceClient(conn)
    ctx := context.Background()

    concurrentUsers := 100
    ordersPerUser := 10
    var wg sync.WaitGroup
    errorChan := make(chan error, concurrentUsers*ordersPerUser)

    start := time.Now()

    for i := 0; i < concurrentUsers; i++ {
        wg.Add(1)
        go func(userID int) {
            defer wg.Done()
            for j := 0; j < ordersPerUser; j++ {
                order := &pb.CreateOrderRequest{
                    UserId: fmt.Sprintf("user-%d", userID),
                    Items: []*pb.OrderItem{
                        {
                            ProductId: fmt.Sprintf("prod-%d", j),
                            Quantity: 1,
                            Price:    99.99,
                        },
                    },
                }

                _, err := client.CreateOrder(ctx, order)
                if err != nil {
                    errorChan <- err
                }
            }
        }(i)
    }

    // 等待所有goroutine完成
    wg.Wait()
    close(errorChan)

    duration := time.Since(start)
    totalOrders := concurrentUsers * ordersPerUser
    ordersPerSecond := float64(totalOrders) / duration.Seconds()

    // 统计错误
    var errorCount int
    for err := range errorChan {
        t.Errorf("Order creation error: %v", err)
        errorCount++
    }

    t.Logf("Performance Results:")
    t.Logf("Total Orders: %d", totalOrders)
    t.Logf("Total Time: %v", duration)
    t.Logf("Orders/Second: %.2f", ordersPerSecond)
    t.Logf("Error Rate: %.2f%%", float64(errorCount)/float64(totalOrders)*100)
}

// 延迟测试
func TestOrderLatency(t *testing.T) {
    conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        t.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewOrderServiceClient(conn)
    ctx := context.Background()

    var latencies []time.Duration
    testCount := 100

    for i := 0; i < testCount; i++ {
        order := &pb.CreateOrderRequest{
            UserId: "latency-test",
            Items: []*pb.OrderItem{
                {
                    ProductId: "prod-1",
                    Quantity: 1,
                    Price:    99.99,
                },
            },
        }

        start := time.Now()
        _, err := client.CreateOrder(ctx, order)
        latency := time.Since(start)
        latencies = append(latencies, latency)

        if err != nil {
            t.Errorf("CreateOrder failed: %v", err)
        }

        time.Sleep(time.Millisecond * 100) // 避免过度压力
    }

    // 计算统计信息
    var totalLatency time.Duration
    var maxLatency time.Duration
    var minLatency = latencies[0]

    for _, l := range latencies {
        totalLatency += l
        if l > maxLatency {
            maxLatency = l
        }
        if l < minLatency {
            minLatency = l
        }
    }

    avgLatency := totalLatency / time.Duration(len(latencies))

    t.Logf("Latency Results:")
    t.Logf("Average Latency: %v", avgLatency)
    t.Logf("Max Latency: %v", maxLatency)
    t.Logf("Min Latency: %v", minLatency)
}

// 负载测试监控器
type LoadTestMonitor struct {
    successCount int64
    errorCount   int64
    totalLatency time.Duration
    mu           sync.Mutex
}

func (m *LoadTestMonitor) recordSuccess(latency time.Duration) {
    m.mu.Lock()
    defer m.mu.Unlock()
    m.successCount++
    m.totalLatency += latency
}

func (m *LoadTestMonitor) recordError() {
    m.mu.Lock()
    defer m.mu.Unlock()
    m.errorCount++
}

func (m *LoadTestMonitor) getStats() (int64, int64, time.Duration) {
    m.mu.Lock()
    defer m.mu.Unlock()
    avgLatency := time.Duration(0)
    if m.successCount > 0 {
        avgLatency = m.totalLatency / time.Duration(m.successCount)
    }
    return m.successCount, m.errorCount, avgLatency
}

func TestLoadScenarios(t *testing.T) {
    conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        t.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewOrderServiceClient(conn)
    ctx := context.Background()

    scenarios := []struct {
        name           string
        concurrency   int
        duration     time.Duration
    }{
        {"Light Load", 10, 30 * time.Second},
        {"Medium Load", 50, 30 * time.Second},
        {"Heavy Load", 100, 30 * time.Second},
    }

    for _, scenario := range scenarios {
        t.Run(scenario.name, func(t *testing.T) {
            monitor := &LoadTestMonitor{}
            var wg sync.WaitGroup
            
            // 启动工作goroutine
            for i := 0; i < scenario.concurrency; i++ {
                wg.Add(1)
                go func(workerID int) {
                    defer wg.Done()
                    
                    start := time.Now()
                    for time.Since(start) < scenario.duration {
                        order := &pb.CreateOrderRequest{
                            UserId: fmt.Sprintf("load-test-%d", workerID),
                            Items: []*pb.OrderItem{
                                {
                                    ProductId: "prod-1",
                                    Quantity: 1,
                                    Price:    99.99,
                                },
                            },
                        }

                        requestStart := time.Now()
                        _, err := client.CreateOrder(ctx, order)
                        if err != nil {
                            monitor.recordError()
                        } else {
                            monitor.recordSuccess(time.Since(requestStart))
                        }

                        time.Sleep(time.Millisecond * 100) // 控制请求速率
                    }
                }(i)
            }

            wg.Wait()
            success, errors, avgLatency := monitor.getStats()
            
            t.Logf("Scenario: %s", scenario.name)
            t.Logf("Success Count: %d", success)
            t.Logf("Error Count: %d", errors)
            t.Logf("Average Latency: %v", avgLatency)
            t.Logf("Requests/Second: %.2f", float64(success+errors)/scenario.duration.Seconds())
        })
    }
}

这里我们为订单服务实现了一套完整的性能测试方案,包括:

  1. 基准测试

    • 测试单个API的性能
    • 并发测试
    • 可重复性测试
  2. 并发压力测试

    • 模拟多用户场景
    • 测试系统稳定性
    • 性能数据收集
  3. 延迟测试

    • 响应时间测试
    • 统计分析
    • 性能瓶颈定位
  4. 负载测试

    • 不同负载场景
    • 系统极限测试
    • 性能监控

五、性能优化建议

  1. 接口层面

    • 使用流式处理大数据
    • 批量处理请求
    • 合理设置超时
  2. 服务层面

    • 使用连接池
    • 优化资源使用
    • 缓存热点数据
  3. 系统层面

    • 调整系统参数
    • 监控资源使用
    • 及时处理故障

六、总结

本节课我们实现了一个完整的RPC订单服务,涵盖了:

  1. 服务设计与实现

    • 接口定义
    • 业务逻辑
    • 错误处理
  2. 性能测试与优化

    • 基准测试
    • 压力测试
    • 监控分析
  3. 最佳实践

    • 代码组织
    • 错误处理
    • 性能优化

怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

相关推荐
来恩10031 小时前
C# 类与对象详解
开发语言·c#
komo莫莫da1 小时前
寒假刷题Day19
java·开发语言
ElseWhereR1 小时前
C++ 写一个简单的加减法计算器
开发语言·c++·算法
计算机-秋大田2 小时前
基于微信小程序的电子竞技信息交流平台设计与实现(LW+源码+讲解)
spring boot·后端·微信小程序·小程序·课程设计
※DX3906※2 小时前
cpp实战项目—string类的模拟实现
开发语言·c++
wjs20242 小时前
Nginx 安装配置指南
开发语言
桦说编程3 小时前
CompletableFuture 超时功能有大坑!使用不当直接生产事故!
java·性能优化·函数式编程·并发编程
美味小鱼3 小时前
实践Rust:编写一个猜数字游戏
开发语言·游戏·rust
Dr.勿忘4 小时前
C#面试常考随笔8:using关键字有哪些用法?
开发语言·unity·面试·c#·游戏引擎
dal118网工任子仪4 小时前
92,[8] 攻防世界 web Web_php_wrong_nginx_config
开发语言·php