feat(platform): Implement platform infrastructure with cloud-native support
- Add storage service (LocalAdapter + OSSAdapter stub) - Add database connection pool with graceful shutdown - Add logging system with winston (JSON format) - Add environment config management - Add async job queue (MemoryQueue + DatabaseQueue stub) - Add cache service (MemoryCache + RedisCache stub) - Add health check endpoints for SAE - Add monitoring metrics for DB, memory, API Key Features: - Zero-code switching between local and cloud environments - Adapter pattern for multi-environment support - Backward compatible with legacy modules - Ready for Aliyun Serverless deployment Related: Platform Infrastructure Planning (docs/09-鏋舵瀯瀹炴柦/04-骞冲彴鍩虹璁炬柦瑙勫垝.md)
This commit is contained in:
204
backend/src/common/jobs/MemoryQueue.ts
Normal file
204
backend/src/common/jobs/MemoryQueue.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import { Job, JobQueue, JobHandler } from './types.js'
|
||||
import { randomUUID } from 'crypto'
|
||||
|
||||
/**
|
||||
* 内存队列实现
|
||||
*
|
||||
* 适用场景:
|
||||
* - 本地开发环境
|
||||
* - 单实例部署
|
||||
* - 非关键任务(重启会丢失)
|
||||
*
|
||||
* 特点:
|
||||
* - ✅ 简单易用,无需外部依赖
|
||||
* - ✅ 性能高效
|
||||
* - ⚠️ 进程重启后任务丢失
|
||||
* - ⚠️ 不支持多实例共享
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const queue = new MemoryQueue()
|
||||
*
|
||||
* // 注册处理函数
|
||||
* queue.process('email:send', async (job) => {
|
||||
* await sendEmail(job.data.to, job.data.subject)
|
||||
* })
|
||||
*
|
||||
* // 创建任务
|
||||
* const job = await queue.push('email:send', { to: 'user@example.com', subject: 'Hello' })
|
||||
*
|
||||
* // 查询任务
|
||||
* const status = await queue.getJob(job.id)
|
||||
* ```
|
||||
*/
|
||||
export class MemoryQueue implements JobQueue {
|
||||
private jobs: Map<string, Job> = new Map()
|
||||
private handlers: Map<string, JobHandler> = new Map()
|
||||
private processing: boolean = false
|
||||
|
||||
/**
|
||||
* 添加任务到队列
|
||||
*/
|
||||
async push<T>(type: string, data: T): Promise<Job<T>> {
|
||||
const job: Job<T> = {
|
||||
id: randomUUID(),
|
||||
type,
|
||||
data,
|
||||
status: 'pending',
|
||||
progress: 0,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}
|
||||
|
||||
this.jobs.set(job.id, job)
|
||||
|
||||
// 触发任务处理
|
||||
this.processNextJob()
|
||||
|
||||
return job
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册任务处理函数
|
||||
*/
|
||||
process<T>(type: string, handler: JobHandler<T>): void {
|
||||
this.handlers.set(type, handler)
|
||||
console.log(`[MemoryQueue] Registered handler for job type: ${type}`)
|
||||
|
||||
// 开始处理队列中的待处理任务
|
||||
this.processNextJob()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务信息
|
||||
*/
|
||||
async getJob(id: string): Promise<Job | null> {
|
||||
return this.jobs.get(id) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
*/
|
||||
async updateProgress(id: string, progress: number): Promise<void> {
|
||||
const job = this.jobs.get(id)
|
||||
if (job) {
|
||||
job.progress = Math.min(100, Math.max(0, progress))
|
||||
job.updatedAt = new Date()
|
||||
this.jobs.set(id, job)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记任务为完成
|
||||
*/
|
||||
async completeJob(id: string, result: any): Promise<void> {
|
||||
const job = this.jobs.get(id)
|
||||
if (job) {
|
||||
job.status = 'completed'
|
||||
job.progress = 100
|
||||
job.result = result
|
||||
job.completedAt = new Date()
|
||||
job.updatedAt = new Date()
|
||||
this.jobs.set(id, job)
|
||||
|
||||
console.log(`[MemoryQueue] Job completed: ${id} (type: ${job.type})`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记任务为失败
|
||||
*/
|
||||
async failJob(id: string, error: string): Promise<void> {
|
||||
const job = this.jobs.get(id)
|
||||
if (job) {
|
||||
job.status = 'failed'
|
||||
job.error = error
|
||||
job.completedAt = new Date()
|
||||
job.updatedAt = new Date()
|
||||
this.jobs.set(id, job)
|
||||
|
||||
console.error(`[MemoryQueue] Job failed: ${id} (type: ${job.type})`, error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理下一个待处理任务
|
||||
*/
|
||||
private async processNextJob(): Promise<void> {
|
||||
if (this.processing) return
|
||||
|
||||
// 查找第一个待处理的任务
|
||||
const pendingJob = Array.from(this.jobs.values()).find(
|
||||
job => job.status === 'pending'
|
||||
)
|
||||
|
||||
if (!pendingJob) return
|
||||
|
||||
// 获取对应的处理函数
|
||||
const handler = this.handlers.get(pendingJob.type)
|
||||
if (!handler) {
|
||||
// 没有注册处理函数,跳过
|
||||
return
|
||||
}
|
||||
|
||||
// 标记为处理中
|
||||
this.processing = true
|
||||
pendingJob.status = 'processing'
|
||||
pendingJob.startedAt = new Date()
|
||||
pendingJob.updatedAt = new Date()
|
||||
this.jobs.set(pendingJob.id, pendingJob)
|
||||
|
||||
console.log(`[MemoryQueue] Processing job: ${pendingJob.id} (type: ${pendingJob.type})`)
|
||||
|
||||
try {
|
||||
// 执行处理函数
|
||||
const result = await handler(pendingJob)
|
||||
|
||||
// 标记为完成
|
||||
await this.completeJob(pendingJob.id, result)
|
||||
} catch (error: any) {
|
||||
// 标记为失败
|
||||
await this.failJob(pendingJob.id, error.message)
|
||||
} finally {
|
||||
this.processing = false
|
||||
|
||||
// 继续处理下一个任务
|
||||
setImmediate(() => this.processNextJob())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列统计信息
|
||||
*/
|
||||
getStats() {
|
||||
const jobs = Array.from(this.jobs.values())
|
||||
return {
|
||||
total: jobs.length,
|
||||
pending: jobs.filter(j => j.status === 'pending').length,
|
||||
processing: jobs.filter(j => j.status === 'processing').length,
|
||||
completed: jobs.filter(j => j.status === 'completed').length,
|
||||
failed: jobs.filter(j => j.status === 'failed').length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理已完成的任务(避免内存泄漏)
|
||||
* 建议定期调用
|
||||
*/
|
||||
cleanup(olderThan: Date = new Date(Date.now() - 24 * 60 * 60 * 1000)) {
|
||||
let removed = 0
|
||||
for (const [id, job] of this.jobs) {
|
||||
if (
|
||||
(job.status === 'completed' || job.status === 'failed') &&
|
||||
job.completedAt &&
|
||||
job.completedAt < olderThan
|
||||
) {
|
||||
this.jobs.delete(id)
|
||||
removed++
|
||||
}
|
||||
}
|
||||
console.log(`[MemoryQueue] Cleanup: removed ${removed} old jobs`)
|
||||
return removed
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user