Skip to content

Latest commit

 

History

History
351 lines (265 loc) · 7.02 KB

File metadata and controls

351 lines (265 loc) · 7.02 KB

SimpleScheduler 架构设计说明

🏗️ 核心设计理念

预定义服务模式 (Predefined Service Pattern)

SimpleScheduler 采用预定义服务的设计模式,这是系统安全性和可控性的核心保障。

工作流程

1. 管理员在配置文件中定义服务
   ↓
2. 服务器启动时加载并验证服务定义
   ↓
3. 客户端只能使用已定义的 serviceId
   ↓
4. 系统根据服务定义执行任务

为什么这样设计?

方面 预定义服务 (✅ 我们的设计) 动态服务 (❌ 不推荐)
安全性 只能执行预先批准的操作 客户端可以执行任意操作
可控性 管理员完全控制 难以控制和审计
审计 配置文件即审计记录 需要记录所有请求
性能 配置在启动时加载 每次都需要验证
维护 集中管理,易于维护 分散在各处,难以追踪

📋 服务定义详解

服务定义结构

interface ServiceDefinition {
  id: string              // 唯一标识符
  type: ServiceType       // WEB_SERVICE 或 LOCAL_TOOL
  description?: string    // 描述(可选)
  parameters: object      // 参数说明(文档用途)
  output: OutputType      // JSON、TEXT 或 FILE
}

示例:Web 服务

{
  "id": "github-api",
  "type": "WEB_SERVICE",
  "description": "调用 GitHub API 获取用户信息",
  "parameters": {
    "username": "string"
  },
  "output": "JSON"
}

说明

  • id: 客户端提交任务时使用的服务标识
  • type: 决定使用哪个 Executor(WebServiceExecutor)
  • parameters: 文档说明,告诉用户需要提供哪些参数
  • output: 决定如何处理返回结果

客户端使用

{
  "serviceId": "github-api",  // 必须匹配配置中的 id
  "payload": {
    "url": "https://api.github.com/users/octocat",
    "method": "GET"
  }
}

示例:本地工具

{
  "id": "video-transcoder",
  "type": "LOCAL_TOOL",
  "description": "使用 FFmpeg 转码视频",
  "parameters": {
    "inputFile": "string",
    "outputFormat": "string",
    "quality": "string"
  },
  "output": "FILE"
}

客户端使用

{
  "serviceId": "video-transcoder",
  "payload": {
    "command": "/usr/bin/ffmpeg",
    "args": ["-i", "input.mp4", "-c:v", "libx264", "output.mp4"],
    "cwd": "/media/videos"
  }
}

🔒 安全层次

SimpleScheduler 采用多层安全防护:

第一层:服务白名单

// ConfigManager.ts
if (!configManager.hasService(request.serviceId)) {
  throw new Error('Service not found')
}

防护:客户端不能调用未定义的服务

第二层:命令白名单

// TaskExecutor.ts
const safePattern = /^[a-zA-Z0-9_\-./]+$/
if (!safePattern.test(command)) {
  throw new Error('Unsafe command')
}

防护:即使是预定义的服务,也只能执行安全的命令

第三层:进程隔离

// TaskExecutor.ts
spawn(command, args, {
  shell: false,  // 禁用 shell,防止命令注入
  timeout: 300000
})

防护:不使用 shell,参数独立传递,超时保护

第四层:参数验证

// validator.ts
const validatedData = ScheduleJobRequestSchema.parse(body)

防护:所有输入经过 Zod 验证

🎯 使用场景

适合的场景

企业内部任务调度

  • 预定义的数据处理任务
  • 定期备份脚本
  • 报表生成
  • 批量数据导入/导出

API 聚合服务

  • 调用多个第三方 API
  • 数据转换和聚合
  • Webhook 触发器

DevOps 自动化

  • CI/CD 流程
  • 部署脚本
  • 健康检查
  • 日志收集

不适合的场景

动态代码执行

  • 用户提交任意代码
  • 在线代码编辑器
  • Serverless 函数即服务

完全开放的任务系统

  • 用户可以执行任意命令
  • 不可预知的工作负载

🔄 服务管理最佳实践

1. 服务定义规范

{
  "id": "service-name",           // 使用 kebab-case
  "type": "WEB_SERVICE",          // 明确类型
  "description": "清晰的描述",     // 必填,便于维护
  "parameters": {                 // 详细的参数说明
    "param1": "string",
    "param2": "number"
  },
  "output": "JSON"                // 明确输出类型
}

2. 服务版本管理

建议使用版本化的服务 ID:

[
  {
    "id": "email-v1",
    "type": "WEB_SERVICE",
    "description": "邮件服务 v1(使用 SendGrid)"
  },
  {
    "id": "email-v2",
    "type": "WEB_SERVICE",
    "description": "邮件服务 v2(使用 AWS SES)"
  }
]

这样可以:

  • 平滑升级
  • 向后兼容
  • A/B 测试

3. 服务分类

按业务域组织服务:

notification-email
notification-sms
notification-push

data-import-csv
data-import-json
data-export-excel

backup-database
backup-files

4. 配置文件管理

# 开发环境
config/services.dev.json

# 生产环境
config/services.prod.json

# 使用环境变量选择
SERVICE_CONFIG=${NODE_ENV:-dev}

📊 服务生命周期

定义 → 验证 → 加载 → 使用 → 监控 → 更新 → 废弃

1. 定义

config/services.json 中添加服务定义

2. 验证

服务器启动时自动验证(使用 Zod Schema)

3. 加载

ConfigManager 加载到内存

4. 使用

客户端通过 API 提交任务

5. 监控

查看任务执行情况、成功率、耗时

6. 更新

修改配置文件,重启服务器

7. 废弃

从配置文件中移除(注意向后兼容性)

🚀 扩展指南

添加新的服务类型

如果需要支持新的服务类型(如 DATABASE_QUERY),步骤如下:

  1. 定义枚举server/types/enums.ts):
export enum ServiceType {
  WEB_SERVICE = 'WEB_SERVICE',
  LOCAL_TOOL = 'LOCAL_TOOL',
  DATABASE_QUERY = 'DATABASE_QUERY',  // 新增
}
  1. 创建执行器server/services/DatabaseExecutor.ts):
class DatabaseExecutor {
  async execute(job: Job): Promise<void> {
    // 实现数据库查询逻辑
  }
}
  1. 注册执行器server/services/TaskExecutor.ts):
case ServiceType.DATABASE_QUERY:
  await this.databaseExecutor.execute(job)
  break
  1. 添加服务定义config/services.json):
{
  "id": "user-query",
  "type": "DATABASE_QUERY",
  "description": "查询用户数据",
  "parameters": {
    "userId": "string"
  },
  "output": "JSON"
}

📚 总结

SimpleScheduler 的预定义服务模式提供了:

安全性 - 多层防护,防止恶意操作
可控性 - 管理员完全控制
可审计 - 所有操作可追踪
高性能 - 配置预加载
易维护 - 集中管理
灵活性 - 易于扩展新服务类型

这种设计使 SimpleScheduler 特别适合企业级应用场景,在安全性和灵活性之间取得了良好的平衡。


文档版本: 1.0.0
最后更新: 2025-11-12