490 lines
14 KiB
Markdown
490 lines
14 KiB
Markdown
# MeshRay gRPC 架构真相与最终修复方案
|
||
|
||
**发现时间**: 2026-03-24
|
||
**状态**: 🔴 **确认问题存在** - 需要修复
|
||
**关键发现**: Ctr 只有 Core 客户端,没有 gRPC 服务器
|
||
|
||
---
|
||
|
||
## 🚨 最终确认:gRPC 服务器确实未启动
|
||
|
||
### **代码证据**
|
||
|
||
```go
|
||
// 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 服务器
|
||
}
|
||
```
|
||
|
||
**搜索结果**:
|
||
```bash
|
||
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 进程中
|
||
|
||
```go
|
||
// 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 作为独立进程运行
|
||
|
||
```bash
|
||
# 启动 Core 服务
|
||
./meshray-core --grpc-port=50051 &
|
||
|
||
# 启动 API 服务器
|
||
./meshray-api serve
|
||
```
|
||
|
||
**优点**:
|
||
- ✅ 完全解耦
|
||
- ✅ 可以独立扩展
|
||
- ✅ 符合微服务架构
|
||
|
||
**缺点**:
|
||
- ❌ 部署复杂(需要管理两个进程)
|
||
- ❌ 需要额外的进程监控
|
||
|
||
---
|
||
|
||
## 🎯 推荐方案:进程内集成(方案 A)
|
||
|
||
### **理由**
|
||
1. ✅ 符合当前项目结构(Core 在 `core/` 目录作为库)
|
||
2. ✅ 部署简单(用户只需运行一个命令)
|
||
3. ✅ 性能更好(无网络开销)
|
||
4. ✅ 调试方便(单进程日志)
|
||
|
||
---
|
||
|
||
## 📝 详细修复步骤
|
||
|
||
### **步骤 1: 修改 internal/api/server.go**
|
||
|
||
```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: 验证修复**
|
||
|
||
```bash
|
||
# 编译项目
|
||
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: 删除未使用文件**
|
||
|
||
```bash
|
||
# 删除未使用的文件
|
||
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**
|
||
|
||
```go
|
||
// 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 - 必须立即修复**
|