diff --git a/packages/libs/lib-server/src/system/basic/index.ts b/packages/libs/lib-server/src/system/basic/index.ts index 03a9533c5..e05d3be82 100644 --- a/packages/libs/lib-server/src/system/basic/index.ts +++ b/packages/libs/lib-server/src/system/basic/index.ts @@ -2,3 +2,4 @@ export * from './service/plus-service.js'; export * from './service/file-service.js'; export * from './service/encryptor.js'; export * from './service/ocr-service.js'; +export * from './service/executor-queue.js'; \ No newline at end of file diff --git a/packages/libs/lib-server/src/system/basic/service/executor-queue.ts b/packages/libs/lib-server/src/system/basic/service/executor-queue.ts new file mode 100644 index 000000000..ca82b7eec --- /dev/null +++ b/packages/libs/lib-server/src/system/basic/service/executor-queue.ts @@ -0,0 +1,79 @@ +import { logger } from "@certd/basic"; + +export type TaskItem = { + task: ()=>Promise; +} + +export class UserTaskQueue{ + userId: number; + pendingQueue: TaskItem[] = []; + runningQueue: TaskItem[] = []; + getMaxRunningCount: ()=>number ; + + constructor(req: { userId: number ,getMaxRunningCount: ()=>number }) { + this.userId = req.userId; + this.getMaxRunningCount = req.getMaxRunningCount ; + } + + addTask(task: TaskItem) { + this.pendingQueue.push(task); + this.runTask(); + } + + runTask() { + logger.info(`[user_${this.userId}]当前运行队列:${this.runningQueue.length}, 等待队列:${this.pendingQueue.length},最大运行队列:${this.getMaxRunningCount()}`); + if (this.runningQueue.length >= this.getMaxRunningCount()) { + return; + } + if (this.pendingQueue.length === 0) { + return; + } + const task = this.pendingQueue.shift(); + if (!task) { + return; + } + // 执行任务 + this.runningQueue.push(task); + const call = async ()=>{ + try{ + await task.task(); + }finally{ + // 任务执行完成,从运行队列中移除 + const index = this.runningQueue.indexOf(task); + if (index > -1) { + this.runningQueue.splice(index, 1); + } + // 继续执行下一个任务 + this.runTask(); + } + } + logger.info(`[user_${this.userId}]执行任务,当前运行队列:${this.runningQueue.length}, 等待队列:${this.pendingQueue.length}`); + call() + } +} + +export class ExecutorQueue{ + queues: Record = {}; + maxRunningCount: number = 8; + + + setMaxRunningCount(count: number) { + this.maxRunningCount = count; + } + + getUserQueue(userId: number) { + const userQueue = this.queues[userId]; + if (!userQueue) { + this.queues[userId] = new UserTaskQueue({ userId, getMaxRunningCount: ()=>this.maxRunningCount }); + } + return this.queues[userId]; + } + + addTask(userId: number, task: TaskItem) { + const userQueue = this.getUserQueue(userId); + userQueue.addTask(task); + } + +} + +export const executorQueue = new ExecutorQueue(); \ No newline at end of file diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts index 5df3952f7..699eb1f72 100644 --- a/packages/libs/lib-server/src/system/settings/service/models.ts +++ b/packages/libs/lib-server/src/system/settings/service/models.ts @@ -43,12 +43,16 @@ export class SysPublicSettings extends BaseSettings { //流水线是否启用有效期 pipelineValidTimeEnabled?: boolean = false; + //证书域名添加到监控 certDomainAddToMonitorEnabled?: boolean = false; // 固定证书有效期天数,0表示不固定 fixedCertExpireDays?: number; + //默认到期前更新天数 + defaultCertRenewDays?: number; + // 第三方OAuth配置 oauthEnabled?: boolean = false; oauthProviders: Record { if (bean.dnsResultOrder) { dns.setDefaultResultOrder(bean.dnsResultOrder as any); } + + if (bean.pipelineMaxRunningCount){ + executorQueue.setMaxRunningCount(bean.pipelineMaxRunningCount); + } } async updateByKey(key: string, setting: any) { diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index 6f39a9159..41e7587a0 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -759,6 +759,14 @@ export default { certDomainAddToMonitorEnabled: "证书域名添加到证书监控", certDomainAddToMonitorEnabledHelper: "创建证书流水线时是否可以选择将域名添加到证书监控", + defaultCertRenewDays: "默认到期前更新天数", + defaultCertRenewDaysHelper: "创建证书流水线时,默认的证书到期前更新天数", + defaultCertRenewDaysRecommend: "默认值15", + + pipelineMaxRunningCount: "同时最大运行流水线数量", + pipelineMaxRunningCountHelper: "同一个用户同时运行的最大流水线数量,避免同时触发太多导致ACME账户被限制", + pipelineMaxRunningCountRecommend: "推荐5-10", + fixedCertExpireDays: "固定证书有效期天数", fixedCertExpireDaysHelper: "固定证书有效期天数,有助于列表进度条整齐显示", fixedCertExpireDaysRecommend: "推荐90", diff --git a/packages/ui/certd-client/src/store/settings/api.basic.ts b/packages/ui/certd-client/src/store/settings/api.basic.ts index b22a70bfa..91ded0fad 100644 --- a/packages/ui/certd-client/src/store/settings/api.basic.ts +++ b/packages/ui/certd-client/src/store/settings/api.basic.ts @@ -54,6 +54,9 @@ export type SysPublicSetting = { //流水线是否启用有效期 pipelineValidTimeEnabled?: boolean; + // 默认到期前更新天数 + defaultCertRenewDays?: number; + //证书域名添加到监控 certDomainAddToMonitorEnabled?: boolean; @@ -86,6 +89,9 @@ export type SysPrivateSetting = { httpsProxy?: string; dnsResultOrder?: string; commonCnameEnabled?: boolean; + // 同一个用户同时最大运行流水线数量 + pipelineMaxRunningCount?: number; + sms?: { type?: string; config?: any; diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue index 2c5e6f70d..ea3aaf77b 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue @@ -26,12 +26,26 @@
- +
{{ t("certd.sys.setting.fixedCertExpireDaysHelper") }}
+ +
+ +
+
{{ t("certd.sys.setting.defaultCertRenewDaysHelper") }}
+
+ + +
+ +
+
{{ t("certd.sys.setting.pipelineMaxRunningCountHelper") }}
+
+ {{ t("certd.saveButton") }} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/executor-queue.ts b/packages/ui/certd-server/src/modules/pipeline/service/executor-queue.ts deleted file mode 100644 index 18fdd4bf4..000000000 --- a/packages/ui/certd-server/src/modules/pipeline/service/executor-queue.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { logger } from "@certd/basic"; - -export type TaskItem = { - task: ()=>Promise; -} - -export class ExecutorQueue{ - pendingQueue: TaskItem[] = []; - runningQueue: TaskItem[] = []; - maxRunningCount: number = 10; - - - setMaxRunningCount(count: number) { - this.maxRunningCount = count; - } - - addTask(task: TaskItem) { - this.pendingQueue.push(task); - this.runTask(); - } - - runTask() { - logger.info(`当前运行队列:${this.runningQueue.length}, 等待队列:${this.pendingQueue.length}`); - if (this.runningQueue.length >= this.maxRunningCount) { - logger.info(`当前运行队列已满,等待队列:${this.pendingQueue.length}`); - return; - } - if (this.pendingQueue.length === 0) { - return; - } - const task = this.pendingQueue.shift(); - if (!task) { - return; - } - // 执行任务 - this.runningQueue.push(task); - const call = async ()=>{ - try{ - await task.task(); - }finally{ - // 任务执行完成,从运行队列中移除 - const index = this.runningQueue.indexOf(task); - if (index > -1) { - this.runningQueue.splice(index, 1); - } - // 继续执行下一个任务 - this.runTask(); - } - } - call() - } -} - -export const executorQueue = new ExecutorQueue(); \ No newline at end of file diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts index b48becb51..d273d6620 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts @@ -47,7 +47,7 @@ import { CertInfoService } from "../../monitor/service/cert-info-service.js"; import { TaskServiceBuilder } from "./getter/task-service-getter.js"; import { nanoid } from "nanoid"; import { set } from "lodash-es"; -import { executorQueue } from "./executor-queue.js"; +import { executorQueue } from "@certd/lib-server"; const runningTasks: Map = new Map(); @@ -373,7 +373,7 @@ export class PipelineService extends BaseService { return; } for (const trigger of pipeline.triggers) { - this.registerCron(pipeline.id, trigger); + this.registerCron(pipeline.id, pipeline.userId, trigger); } if (immediateTriggerOnce) { @@ -461,7 +461,7 @@ export class PipelineService extends BaseService { logger.info("当前定时器数量:", this.cron.getTaskSize()); } - registerCron(pipelineId, trigger) { + registerCron(pipelineId: number, userId: number, trigger) { if (pipelineId == null) { logger.warn("pipelineId为空,无法注册定时任务"); return; @@ -491,7 +491,7 @@ export class PipelineService extends BaseService { return; } //加入执行队列 - executorQueue.addTask({ + executorQueue.addTask(userId, { task: async () => { try { await this.run(pipelineId, triggerId); @@ -678,7 +678,7 @@ export class PipelineService extends BaseService { this.cron.remove(key); triggerType = null; } else { - logger.info(`timer trigger:${key}, ${found.title}, ${found.props}`); + logger.info(`timer trigger:${key}, ${found.title}, ${JSON.stringify(found.props)}`); triggerType = "timer"; } }