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
}{
"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 函数即服务
❌ 完全开放的任务系统
- 用户可以执行任意命令
- 不可预知的工作负载
{
"id": "service-name", // 使用 kebab-case
"type": "WEB_SERVICE", // 明确类型
"description": "清晰的描述", // 必填,便于维护
"parameters": { // 详细的参数说明
"param1": "string",
"param2": "number"
},
"output": "JSON" // 明确输出类型
}建议使用版本化的服务 ID:
[
{
"id": "email-v1",
"type": "WEB_SERVICE",
"description": "邮件服务 v1(使用 SendGrid)"
},
{
"id": "email-v2",
"type": "WEB_SERVICE",
"description": "邮件服务 v2(使用 AWS SES)"
}
]这样可以:
- 平滑升级
- 向后兼容
- A/B 测试
按业务域组织服务:
notification-email
notification-sms
notification-push
data-import-csv
data-import-json
data-export-excel
backup-database
backup-files
# 开发环境
config/services.dev.json
# 生产环境
config/services.prod.json
# 使用环境变量选择
SERVICE_CONFIG=${NODE_ENV:-dev}定义 → 验证 → 加载 → 使用 → 监控 → 更新 → 废弃
在 config/services.json 中添加服务定义
服务器启动时自动验证(使用 Zod Schema)
ConfigManager 加载到内存
客户端通过 API 提交任务
查看任务执行情况、成功率、耗时
修改配置文件,重启服务器
从配置文件中移除(注意向后兼容性)
如果需要支持新的服务类型(如 DATABASE_QUERY),步骤如下:
- 定义枚举(
server/types/enums.ts):
export enum ServiceType {
WEB_SERVICE = 'WEB_SERVICE',
LOCAL_TOOL = 'LOCAL_TOOL',
DATABASE_QUERY = 'DATABASE_QUERY', // 新增
}- 创建执行器(
server/services/DatabaseExecutor.ts):
class DatabaseExecutor {
async execute(job: Job): Promise<void> {
// 实现数据库查询逻辑
}
}- 注册执行器(
server/services/TaskExecutor.ts):
case ServiceType.DATABASE_QUERY:
await this.databaseExecutor.execute(job)
break- 添加服务定义(
config/services.json):
{
"id": "user-query",
"type": "DATABASE_QUERY",
"description": "查询用户数据",
"parameters": {
"userId": "string"
},
"output": "JSON"
}SimpleScheduler 的预定义服务模式提供了:
✅ 安全性 - 多层防护,防止恶意操作
✅ 可控性 - 管理员完全控制
✅ 可审计 - 所有操作可追踪
✅ 高性能 - 配置预加载
✅ 易维护 - 集中管理
✅ 灵活性 - 易于扩展新服务类型
这种设计使 SimpleScheduler 特别适合企业级应用场景,在安全性和灵活性之间取得了良好的平衡。
文档版本: 1.0.0
最后更新: 2025-11-12