Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第三章:系统架构

本章详细介绍 VSDB 的双进程架构设计、分层结构和各模块职责。

3.1 整体架构

VSDB 采用双进程架构,将数据库操作隔离在独立的 Worker 进程中。

graph TD
    subgraph "VSCode Extension Host (主进程)"
        A["extension.ts<br/>入口与命令注册"]
        B["核心管理层"]
        C["UI 层"]
        
        subgraph "核心管理层"
            B1["ConnectionManager"]
            B2["IpcManager"]
            B3["Storage"]
            B4["ScannerEngine"]
            B5["HistoryManager"]
            B6["BookmarkManager"]
            B7["SearchEngine"]
        end
        
        subgraph "UI 层"
            C1["TreeView<br/>(连接树)"]
            C2["Webview Panels<br/>(React 应用)"]
        end
    end
    
    subgraph "Worker Process (子进程)"
        D["worker.ts<br/>Worker 入口"]
        E["QueryQueue<br/>查询队列"]
        
        subgraph "数据库驱动层"
            F1["MySqlDriver"]
            F2["PostgreSqlDriver"]
        end
        
        subgraph "Schema Inspector"
            G1["MySqlSchema"]
            G2["PgSchema"]
        end
    end
    
    A --> B
    B --> C
    B2 -->|"IPC (child_process)"| D
    D --> E
    E --> F1
    E --> F2
    F1 --> G1
    F2 --> G2

3.2 双进程架构原理

为什么选择双进程

graph TD
    subgraph "单进程架构的问题"
        A1["数据库操作"] --> A2["阻塞 Extension Host"]
        A2 --> A3["VSCode 卡顿"]
        A4["驱动异常"] --> A5["扩展崩溃"]
        A5 --> A6["需要重启 VSCode"]
    end
    
    subgraph "双进程架构的优势"
        B1["数据库操作"] --> B2["Worker 进程"]
        B2 --> B3["Extension Host 不受影响"]
        B4["Worker 崩溃"] --> B5["自动重启恢复"]
        B5 --> B6["用户体验连续"]
    end
问题单进程双进程
复杂查询阻塞阻塞 VSCodeWorker 执行,UI 正常
驱动异常扩展崩溃Worker 崩溃自动恢复
内存泄漏影响 VSCodeWorker 可重启清理
长时间操作UI 无响应异步 IPC 通信

进程通信方式

VSDB 使用 Node.js child_process.fork() 创建 Worker:

// IpcManager.ts
this.worker = fork(this.workerPath, [], {
  stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
});

IPC 通信特点:

  • 原生支持:无需额外依赖
  • JSON 序列化:消息自动序列化/反序列化
  • 双向通信:主进程和 Worker 都可发送消息

3.3 分层架构

graph TB
    subgraph "UI 层"
        UI1["TreeView"]
        UI2["Webview Panel"]
    end
    
    subgraph "管理层"
        M1["ConnectionManager"]
        M2["IpcManager"]
        M3["ScannerEngine"]
        M4["HistoryManager"]
        M5["BookmarkManager"]
        M6["SearchEngine"]
    end
    
    subgraph "Worker 层"
        W1["QueryQueue"]
        W2["Driver"]
        W3["SchemaInspector"]
    end
    
    subgraph "存储层"
        S1["JSON Storage"]
        S2["SecretStorage"]
        S3["Schema Cache"]
    end
    
    UI1 --> M1
    UI1 --> M2
    UI2 --> M2
    
    M1 --> S1
    M1 --> S2
    M3 --> S1
    
    M2 -->|"IPC"| W1
    W1 --> W2
    W2 --> W3
    
    M6 --> S3

各层职责

层级模块职责
UI 层TreeView连接树展示、节点交互
WebviewSQL 编辑器、数据网格、表单
管理层ConnectionManager连接 CRUD、范围管理
IpcManagerWorker 生命周期、消息路由
ScannerEngine协调配置扫描
HistoryManager查询历史存储
BookmarkManager书签管理
SearchEngine全局搜索协调
Worker 层QueryQueue并发控制、超时管理
Driver数据库连接、查询执行
SchemaInspector元信息查询
存储层JSON Storage连接配置持久化
SecretStorage密码加密存储
Schema Cache搜索缓存

3.4 Extension Host(主进程)

入口模块

extension.ts 是扩展入口,负责:

// 主要职责
export function activate(context: vscode.ExtensionContext) {
  // 1. 初始化管理器
  const storage = new Storage(context.globalState, context.secrets);
  const connectionManager = new ConnectionManager(storage, projectRoot);
  const ipcManager = new IpcManager({ ... });
  
  // 2. 初始化扫描引擎
  const scannerEngine = new ScannerEngine();
  
  // 3. 初始化辅助管理器
  const historyManager = new HistoryManager(context.globalState);
  const bookmarkManager = new BookmarkManager(context.globalState);
  const searchEngine = new SearchEngine(ipcManager, connectionManager);
  
  // 4. 初始化 TreeView
  const treeProvider = new TreeProvider(connectionManager, ipcManager, ...);
  const treeView = vscode.window.createTreeView(VIEWS.CONNECTIONS, {
    treeDataProvider: treeProvider,
  });
  
  // 5. 注册命令
  registerCommands(context);
  
  // 6. 自动扫描(如果有工作区)
  autoScanProject(workspaceRoot);
}

命令注册

VSDB 注册两类命令:

graph TD
    subgraph "全局命令"
        C1["vsdb.addConnection"]
        C2["vsdb.scanProject"]
        C3["vsdb.newQuery"]
        C4["vsdb.search"]
    end
    
    subgraph "树节点命令"
        T1["vsdb.tree.connect"]
        T2["vsdb.tree.disconnect"]
        T3["vsdb.tree.newQuery"]
        T4["vsdb.viewTableStructure"]
        T5["vsdb.exportData"]
    end
    
    C1 --> F1["打开连接表单"]
    C2 --> F2["扫描项目配置"]
    C3 --> F3["打开 SQL 编辑器"]
    C4 --> F4["搜索表/列"]

3.5 Worker Process(子进程)

Worker 入口

worker.ts 是 Worker 进程入口:

// Worker 生命周期
process.on('message', (request: WorkerRequest) => {
  handleMessage(request);  // 处理请求
});

process.send?.({ type: 'ready' });  // 通知主进程就绪

process.on('disconnect', () => {
  shutdown();  // 父进程断开时清理
});

process.on('SIGTERM', () => {
  shutdown();  // 信号终止时清理
});

请求处理流程

sequenceDiagram
    participant Host as Extension Host
    participant Worker as Worker Process
    participant Queue as QueryQueue
    participant Driver as DatabaseDriver
    
    Host->>Worker: WorkerRequest (type=query)
    Worker->>Queue: enqueue(request)
    Queue->>Driver: execute(sql)
    Driver-->>Queue: QueryResult
    Queue-->>Worker: WorkerResponse
    Worker-->>Host: WorkerResponse

查询队列

QueryQueue 实现并发控制和超时管理:

class QueryQueue {
  private queue = new Map<string, PendingQuery>();
  private maxConcurrency = 5;
  private defaultTimeout = 30000;
  
  enqueue(request: WorkerRequest, handler: () => Promise<WorkerResponse>) {
    // 1. 检查并发限制
    // 2. 设置超时定时器
    // 3. 执行查询
    // 4. 返回结果或超时错误
  }
  
  cancel(requestId: string) {
    // 取消正在执行的查询
  }
}

3.6 数据流分析

连接建立流程

sequenceDiagram
    participant User as 用户
    participant Tree as TreeView
    participant CM as ConnectionManager
    participant IPC as IpcManager
    participant Worker as Worker Process
    participant Driver as MySqlDriver
    
    User->>Tree: 点击连接节点
    Tree->>IPC: sendRequest(connect)
    IPC->>Worker: WorkerRequest(type=connect)
    Worker->>CM: 检查连接配置
    Worker->>Driver: driver.connect(config)
    Driver-->>Worker: 连接成功
    Worker-->>IPC: WorkerResponse(type=result)
    IPC-->>Tree: 更新连接状态
    Tree-->>User: 显示连接图标

查询执行流程

sequenceDiagram
    participant User as 用户
    participant Webview as SQL Editor
    participant IPC as IpcManager
    participant Worker as Worker Process
    participant Queue as QueryQueue
    participant Driver as Driver
    
    User->>Webview: 输入 SQL,点击执行
    Webview->>IPC: postMessage(executeQuery)
    IPC->>Worker: WorkerRequest(type=query)
    Worker->>Queue: enqueue
    Queue->>Driver: driver.query(sql)
    Driver-->>Queue: QueryResult
    Queue-->>Worker: WorkerResponse
    Worker-->>IPC: WorkerResponse
    IPC-->>Webview: WorkerResponse
    Webview-->>User: 显示结果表格

流式查询流程(大数据)

sequenceDiagram
    participant User as 用户
    participant Webview as SQL Editor
    participant IPC as IpcManager
    participant Worker as Worker Process
    participant Driver as Driver
    
    User->>Webview: 执行大查询
    Webview->>IPC: sendRequest(streamQuery)
    IPC->>Worker: WorkerRequest(type=streamQuery)
    Worker->>Driver: driver.streamQuery(sql)
    
    loop 每 1000 行
        Driver-->>Worker: chunk
        Worker-->>IPC: StreamChunk
        IPC-->>Webview: StreamChunk
        Webview-->>User: 渲染数据块
    end
    
    Driver-->>Worker: end
    Worker-->>IPC: streamEnd
    IPC-->>Webview: streamEnd
    Webview-->>User: 完成

3.7 错误处理架构

Worker 崩溃恢复

stateDiagram-v2
    [*] --> Running
    Running --> Crashed: 进程退出
    Crashed --> Restarting: 重启尝试 < max
    Restarting --> Running: 重启成功
    Restarting --> Failed: 重试次数超限
    Failed --> [*]
    
    Running --> Shutdown: 正常关闭
    Shutdown --> [*]

恢复策略:

// IpcManager.ts
worker.on('exit', (code, signal) => {
  if (!isShuttingDown && restartOnCrash && restartAttempts < maxRestartAttempts) {
    restartAttempts++;
    const delay = Math.min(500 * Math.pow(2, restartAttempts - 1), 5000);
    
    setTimeout(() => {
      this.start();                  // 重启 Worker
      this.reconnectActiveConnections();  // 恢复连接
    }, delay);
  }
});

错误分类与处理

// Worker 错误分类
type QueryErrorClass = 'connection' | 'syntax' | 'timeout' | 'permission' | 'unknown';

function classifyQueryError(error: unknown): ClassifiedError {
  const message = error instanceof Error ? error.message : String(error);
  
  if (message.includes('ECONNREFUSED')) {
    return { errorClass: 'connection', retryable: true };
  }
  if (message.includes('syntax error')) {
    return { errorClass: 'syntax', retryable: false };
  }
  if (message.includes('timeout')) {
    return { errorClass: 'timeout', retryable: true };
  }
  // ...
}
错误类典型原因retryable用户提示
connection网络问题、服务未启动"连接失败,请检查网络"
syntaxSQL 语法错误"SQL 语法错误"
timeout查询超时"查询超时,可重试"
permission权限不足"权限不足"
unknown其他错误"未知错误"

3.8 性能考量

IPC 消息大小限制

IPC 消息通过 JSON 序列化传输,大消息会影响性能:

// 流式传输策略
const chunkSize = 1000;  // 每块最多 1000 行

for await (const chunk of driver.streamQuery(sql)) {
  send({
    requestId,
    chunkIndex,
    rows: chunk.rows,  // 限制在 chunkSize
  });
}

内存管理

// Worker 内存监控
const memoryLimitMb = 512;

setInterval(() => {
  const usage = process.memoryUsage();
  const heapUsedMb = usage.heapUsed / (1024 * 1024);
  
  if (heapUsedMb > memoryLimitMb) {
    send({ type: 'error', error: { code: 'WORKER_MEMORY_EXCEEDED' } });
  }
}, 10000);

心跳机制

// 防止僵尸进程
const heartbeatInterval = 15000;  // 15秒发送 ping
const maxMissedHeartbeats = 2;     // 2次未响应视为崩溃

setInterval(() => {
  worker.send({ type: 'ping' });
  missedHeartbeats++;
  
  if (missedHeartbeats >= maxMissedHeartbeats) {
    worker.kill('SIGKILL');  // 强制终止
  }
}, heartbeatInterval);

3.9 小结

本章介绍了 VSDB 的系统架构:

  • 双进程架构:Extension Host + Worker Process
  • 分层设计:UI 层 → 管理层 → Worker 层 → 存储层
  • IPC 通信:JSON 序列化,请求/响应匹配
  • 错误处理:崩溃恢复,错误分类
  • 性能优化:流式传输,内存监控,心跳机制

下一章将深入 IPC 通信机制的实现细节。