14 KiB
14 KiB
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 │ ← 作为独立进程运行
│ (可选项) │ 或通过库集成
└─────────────┘
关键点:
- ✅ Ctr 和 gRPC Server 都在 APIServer 内部
- ✅ Ctr 的 Core Client 连接到 gRPC Server
- ✅ gRPC Server 使用
core/grpc_service.go的实现 - ✅ 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)
理由
- ✅ 符合当前项目结构(Core 在
core/目录作为库) - ✅ 部署简单(用户只需运行一个命令)
- ✅ 性能更好(无网络开销)
- ✅ 调试方便(单进程日志)
📝 详细修复步骤
步骤 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: 清理未使用常量/函数
编辑以下文件,删除未使用的定义:
internal/ctr/types.go:25- 删除ErrCodeWGModeUnavailableinternal/service/network.go:208- 删除generateNetworkSecretinternal/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 服务器
- ✅
CreateEnginegRPC 调用返回成功 - ✅
StartgRPC 调用返回成功 - ✅ 日志显示完整的启动流程
代码清理
- ✅ 删除 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 - 必须立即修复