Files
Meshray-Manager/docs/gRPC 架构最终修复方案.md
2026-06-30 15:14:37 +08:00

14 KiB
Raw Permalink Blame History

MeshRay gRPC 架构真相与最终修复方案

发现时间: 2026-03-24
状态: 🔴 确认问题存在 - 需要修复
关键发现: Ctr 只有 Core 客户端,没有 gRPC 服务器


🚨 最终确认:gRPC 服务器确实未启动

代码证据

// internal/ctr/ctr.go:33-46
func NewCtr(name string, networkID uint64, config *CtrConfig, logger *zap.Logger) (*Ctr, error) {
    ctr := &Ctr{
        name:        name,
        networkID:   networkID,
        config:      config,
        logger:      logger,
        coreClients: make(map[string]*CoreClient), // ← 只有客户端
    }
    
    // 初始化 WireGuard 管理器
    ctr.wgManager = NewWGManager(logger)
    
    return ctr, nil  // ← 没有创建 gRPC 服务器
}

搜索结果:

grep -r "grpc.NewServer" internal/ctr/     # ❌ 无结果
grep -r "RegisterCoreService" internal/ctr/ # ❌ 无结果
grep -r "Serve(lis)" internal/ctr/          # ❌ 无结果

结论:

  • gRPC 服务器确实未启动 - 审查报告正确
  • core/grpc_service.go 的实现无处可用
  • core/client/core_client.go 会连接失败

🏗️ 架构分析:谁应该在 gRPC 两端?

当前架构(错误的)

┌─────────────┐
│   Core      │  ← gRPC 服务端(core/grpc_service.go
│   (库)      │     但没有人启动它!
└─────────────┘
       ↑
       │ gRPC 调用(127.0.0.1:50051
       │
┌─────────────┐
│    Ctr      │  ← gRPC 客户端(core/client/core_client.go
│ (调度中心)  │     尝试连接但会失败
└─────────────┘

正确的架构

┌─────────────────────────────────┐
│         APIServer               │
│  (internal/api/server.go)       │
│                                 │
│  ┌───────────┐  ┌────────────┐ │
│  │   Ctr     │  │ GRPCServer │ │
│  │(调度中心) │  │  (服务端)  │ │
│  │           │  │            │ │
│  │ ┌───────┐ │  │ ┌────────┐ │ │
│  │ │Core   │ │  │ │Core    │ │ │
│  │ │Client │ │  │ │Service │ │ │
│  │ │(客户端)│ │  │ │(服务端)│ │ │
│  │ └───────┘ │  │ └────────┘ │ │
│  └───────────┘  └────────────┘ │
└─────────────────────────────────┘
       ↑                ↑
       │                │
       └──────┬─────────┘
              │
       127.0.0.1:50051
              ↓
       ┌─────────────┐
       │    Core     │  ← 作为独立进程运行
       │  (可选项)    │     或通过库集成
       └─────────────┘

关键点:

  1. Ctr 和 gRPC Server 都在 APIServer 内部
  2. Ctr 的 Core Client 连接到 gRPC Server
  3. gRPC Server 使用 core/grpc_service.go 的实现
  4. Core 可以作为库(集成到进程内)或独立进程

💡 两种可行的架构方案

方案 A: 进程内集成(推荐)

特点: Core 作为库集成到 meshray 进程中

// internal/api/server.go
type APIServer struct {
    engine  *gin.Engine
    config  *config.Config
    logger  *zap.Logger
    
    // Ctr 层
    ctrClient *ctr.Ctr
    
    // gRPC 服务器
    grpcServer *grpc.Server
    coreInst   *core.Core  // Core 实例(库)
}

func (s *APIServer) Start() error {
    // 1. 创建 Core 实例(作为库)
    s.coreInst = core.NewCore(s.logger)
    
    // 2. 创建 gRPC 服务器
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        panic(fmt.Sprintf("无法监听 50051 端口:%v", err))
    }
    
    s.grpcServer = grpc.NewServer()
    
    // 3. 注册 Core 服务(使用 core/grpc_service.go
    coreService := core.NewCoreServiceServer(s.coreInst, s.logger)
    core.RegisterCoreService(s.grpcServer, coreService)
    
    // 4. 启动 gRPC 服务器
    go func() {
        s.logger.Info("启动 gRPC 服务器", zap.Int("port", 50051))
        if err := s.grpcServer.Serve(lis); err != nil {
            s.logger.Error("gRPC 服务器错误", zap.Error(err))
        }
    }()
    
    // 5. 创建 Ctr(会自动连接 127.0.0.1:50051
    s.ctrClient, err = ctr.NewCtr("default", 1, &ctr.CtrConfig{GRPCPort: 50051}, s.logger)
    if err != nil {
        panic(fmt.Sprintf("初始化 ctr 失败:%v", err))
    }
    
    // 6. 启动 HTTP 服务器
    return s.engine.Run(":8080")
}

优点:

  • 简单直接,无需额外进程
  • 部署方便(单个二进制文件)
  • 通信高效(进程内调用)

缺点:

  • Core 和 API 耦合在同一进程

方案 B: 独立 Core 进程(微服务)

特点: Core 作为独立进程运行

# 启动 Core 服务
./meshray-core --grpc-port=50051 &

# 启动 API 服务器
./meshray-api serve

优点:

  • 完全解耦
  • 可以独立扩展
  • 符合微服务架构

缺点:

  • 部署复杂(需要管理两个进程)
  • 需要额外的进程监控

🎯 推荐方案:进程内集成(方案 A

理由

  1. 符合当前项目结构(Core 在 core/ 目录作为库)
  2. 部署简单(用户只需运行一个命令)
  3. 性能更好(无网络开销)
  4. 调试方便(单进程日志)

📝 详细修复步骤

步骤 1: 修改 internal/api/server.go

package api

import (
    "context"
    "fmt"
    "net"
    "time"
    
    "git.zkcoi.com/zkcoi/meshray/core"
    "git.zkcoi.com/zkcoi/meshray/internal/ctr"
    "github.com/gin-gonic/gin"
    "go.uber.org/zap"
    "google.golang.org/grpc"
)

type APIServer struct {
    engine  *gin.Engine
    config  *config.Config
    logger  *zap.Logger
    store   *store.Store
    
    // Ctr 层
    ctrClient *ctr.Ctr
    
    // gRPC 服务器
    grpcServer *grpc.Server
    coreInst   *core.Core
}

func NewAPIServer(cfg *config.Config) (*APIServer, error) {
    // ... 现有代码 ...
    
    return &APIServer{
        engine:  engine,
        config:  cfg,
        logger:  logger,
        store:   store,
        // ctrClient 和 grpcServer 在 Start 中初始化
    }, nil
}

func (s *APIServer) Start() error {
    s.logger.Info("启动 MeshRay 服务器",
        zap.String("mode", s.config.Server.Mode),
        zap.Int("http_port", s.config.Server.Port))
    
    // ========== P1: 启动 gRPC 服务器(为 Core 提供服务)==========
    if err := s.startGRPCServer(); err != nil {
        return fmt.Errorf("启动 gRPC 服务器失败:%w", err)
    }
    
    // ========== P2: 初始化 Ctr 并注入到 Service 层 ==========
    var err error
    s.ctrClient, err = ctr.NewCtr("default", 1, &ctr.CtrConfig{GRPCPort: 50051}, s.logger)
    if err != nil {
        s.logger.Error("初始化 meshray-ctr 失败", zap.Error(err))
        panic(fmt.Sprintf("初始化 ctr 失败:%v", err))
    }
    
    // ... 后续 Service 层初始化 ...
    
    // ========== P3: 启动 HTTP 服务器 ==========
    addr := fmt.Sprintf(":%d", s.config.Server.Port)
    if err := s.engine.Run(addr); err != nil {
        return fmt.Errorf("启动 HTTP 服务器失败:%w", err)
    }
    
    return nil
}

// startGRPCServer 启动 gRPC 服务器(为 Core 提供服务)
func (s *APIServer) startGRPCServer() error {
    s.logger.Info("正在启动 gRPC 服务器",
        zap.Int("port", 50051))
    
    // 1. 创建 Core 实例(作为库)
    s.coreInst = core.NewCore(s.logger)
    
    // 2. 创建 gRPC 服务器
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        return fmt.Errorf("无法监听 50051 端口:%w", err)
    }
    
    s.grpcServer = grpc.NewServer()
    
    // 3. 注册 Core 服务(使用 core/grpc_service.go
    coreService := core.NewCoreServiceServer(s.coreInst, s.logger)
    core.RegisterCoreService(s.grpcServer, coreService)
    
    // 4. 在后台启动 gRPC 服务器
    go func() {
        s.logger.Info("gRPC 服务器已启动",
            zap.Int("port", 50051),
            zap.String("service", "CoreService"))
        
        if err := s.grpcServer.Serve(lis); err != nil {
            s.logger.Error("gRPC 服务器错误", zap.Error(err))
        }
    }()
    
    // 5. 等待 gRPC 服务器就绪(最多 3 秒)
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    conn, err := grpc.DialContext(ctx, "127.0.0.1:50051", grpc.WithInsecure())
    if err != nil {
        return fmt.Errorf("gRPC 服务器未就绪:%w", err)
    }
    conn.Close()
    
    s.logger.Info("gRPC 服务器验证成功")
    return nil
}

// Stop 停止服务器
func (s *APIServer) Stop() error {
    s.logger.Info("正在停止 MeshRay 服务器...")
    
    // 1. 停止 Ctr
    if s.ctrClient != nil {
        if err := s.ctrClient.Stop(); err != nil {
            s.logger.Error("停止 Ctr 失败", zap.Error(err))
        }
    }
    
    // 2. 停止 gRPC 服务器
    if s.grpcServer != nil {
        s.grpcServer.GracefulStop()
    }
    
    // 3. 停止 Core 实例
    if s.coreInst != nil {
        // TODO: Core 实例清理
    }
    
    s.logger.Info("MeshRay 服务器已停止")
    return nil
}

步骤 2: 验证修复

# 编译项目
cd e:\Project\MeshRay
go build -o meshray.exe

# 运行测试
./meshray.exe serve

# 预期输出:
# [INFO] 正在启动 gRPC 服务器 port=50051
# [INFO] gRPC 服务器已启动 port=50051 service=CoreService
# [INFO] gRPC 服务器验证成功
# [INFO] 启动 MeshRay 服务器 mode=release http_port=8080

步骤 3: 删除未使用文件

# 删除未使用的文件
rm internal/ctr/wg_manager.go
rm internal/ctr/wg_go_process.go
rm core/pool/connpool.go
rm pkg/meshseed/meshseed.go

# 保留 watchdog.go(标记为未来功能)
# 在 internal/ctr/watchdog.go 顶部添加注释:
// ⏳ TODO: 启用 Watchdog 监控功能

步骤 4: 注入 SystemConfigService

// internal/api/server.go:178 后添加
// 初始化 SystemConfigService
s.systemConfigService = service.NewSystemConfigService(s.store, s.logger)

步骤 5: 清理未使用常量/函数

编辑以下文件,删除未使用的定义:

  1. internal/ctr/types.go:25 - 删除 ErrCodeWGModeUnavailable
  2. internal/service/network.go:208 - 删除 generateNetworkSecret
  3. internal/ctr/wg_detect.go:154 - 删除 GetRecommendedWGMode

📊 修复后的架构

┌─────────────────────────────────────┐
│         APIServer                   │
│                                     │
│  ┌──────────────────────────────┐  │
│  │  gRPC Server (:50051)        │  │
│  │  ┌────────────────────────┐  │  │
│  │  │ CoreServiceServer      │  │  │
│  │  │ - CreateEngine         │  │  │
│  │  │ - Start                │  │  │
│  │  │ - Stop                 │  │  │
│  │  │ - GetStatus            │  │  │
│  │  └────────────────────────┘  │  │
│  └──────────────────────────────┘  │
│           ↑                        │
│           │ gRPC 调用              │
│           ↓                        │
│  ┌──────────────────────────────┐  │
│  │  Ctr (调度中心)              │  │
│  │  ┌────────────────────────┐  │  │
│  │  │ CoreClient             │  │  │
│  │  │ - 连接到 127.0.0.1:50051│  │  │
│  │  └────────────────────────┘  │  │
│  └──────────────────────────────┘  │
│                                     │
│  ┌──────────────────────────────┐  │
│  │  WGManager                   │  │
│  │  - tunDevice 引用            │  │
│  │  - wgDevice 引用             │  │
│  │  - 跨平台支持                 │  │
│  └──────────────────────────────┘  │
└─────────────────────────────────────┘
        ↑
        │ HTTP/REST API
        ↓
┌─────────────────────────────────────┐
│         Web UI (Vue 3)              │
└─────────────────────────────────────┘

📋 验收清单

核心功能

  • gRPC 服务器成功启动在 50051 端口
  • CoreClient 成功连接到 gRPC 服务器
  • CreateEngine gRPC 调用返回成功
  • Start gRPC 调用返回成功
  • 日志显示完整的启动流程

代码清理

  • 删除 4 个未使用文件
  • 删除 4 个未使用常量/函数
  • SystemConfigService 正确注入

文档完善

  • 5 个预留模型都有注释
  • 架构文档反映真实实现

🎯 总结

问题根源

  • Ctr 只有 Core 客户端,没有服务端
  • core/grpc_service.go 的实现无处可用
  • CoreClient 连接 127.0.0.1:50051 会失败

修复方案

  • 在 APIServer 中启动 gRPC 服务器
  • 使用 core/grpc_service.go 的实现
  • Core 作为库集成到进程中

技术价值

  • 解决了核心架构问题
  • 使 Core 层功能真正可用
  • 统一了进程内通信
  • 简化了部署流程

修复完成时间: 预计 2-3 小时
状态: 📋 等待实施
优先级: 🔴 P0 - 必须立即修复