perf: 优化站点证书检查页面,检查增加3次重试

This commit is contained in:
xiaojunnuo
2025-01-04 20:10:00 +08:00
parent aa1da7c11a
commit e6dd7cd54a
8 changed files with 99 additions and 35 deletions
@@ -1,6 +1,6 @@
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
import { PipelineService } from '../pipeline/service/pipeline-service.js';
import { logger } from '@certd/basic';
import { logger, utils } from '@certd/basic';
import { SysSettingsService } from '@certd/lib-server';
import { SiteInfoService } from '../monitor/index.js';
import { Cron } from '../cron/cron.js';
@@ -59,13 +59,7 @@ export class AutoCRegisterCron {
break;
}
offset += records.length;
for (const record of records) {
try {
await this.siteInfoService.doCheck(record, true);
} catch (e) {
logger.error(`站点${record.name}检查出错:`, e);
}
}
await this.siteInfoService.checkList(records);
}
logger.info('站点证书检查完成');
@@ -5,7 +5,7 @@ import { Repository } from 'typeorm';
import { SiteInfoEntity } from '../entity/site-info.js';
import { siteTester } from './site-tester.js';
import dayjs from 'dayjs';
import { logger } from '@certd/basic';
import { logger, utils } from '@certd/basic';
import { PeerCertificate } from 'tls';
import { NotificationService } from '../../pipeline/service/notification-service.js';
import { isComm, isPlus } from '@certd/plus-core';
@@ -76,23 +76,35 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
* 检查站点证书过期时间
* @param site
* @param notify
* @param retryTimes
*/
async doCheck(site: SiteInfoEntity, notify = true) {
async doCheck(site: SiteInfoEntity, notify = true, retryTimes = 3) {
if (!site?.domain) {
throw new Error('站点域名不能为空');
}
try {
await this.update({
id: site.id,
checkStatus: 'checking',
lastCheckTime: dayjs,
});
const res = await siteTester.test({
host: site.domain,
port: site.httpsPort,
retryTimes,
});
const certi: PeerCertificate = res.certificate;
if (!certi) {
return;
throw new Error('没有发现证书');
}
const expires = certi.valid_to;
const domains = [certi.subject?.CN, ...certi.subjectaltname?.replaceAll('DNS:', '').split(',')];
const allDomains = certi.subjectaltname?.replaceAll('DNS:', '').split(',');
const mainDomain = certi.subject?.CN;
let domains = allDomains;
if (!allDomains.includes(mainDomain)) {
domains = [mainDomain, ...allDomains];
}
const issuer = `${certi.issuer.O}<${certi.issuer.CN}>`;
const isExpired = dayjs().valueOf() > dayjs(expires).valueOf();
const status = isExpired ? 'expired' : 'ok';
@@ -139,13 +151,14 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
* 检查,但不发邮件
* @param id
* @param notify
* @param retryTimes
*/
async check(id: number, notify = false) {
async check(id: number, notify = false, retryTimes = 3) {
const site = await this.info(id);
if (!site) {
throw new Error('站点不存在');
}
return await this.doCheck(site, notify);
return await this.doCheck(site, notify, retryTimes);
}
async sendCheckErrorNotify(site: SiteInfoEntity) {
@@ -206,15 +219,22 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
}
}
async checkAll(userId: any) {
async checkAllByUsers(userId: any) {
if (!userId) {
throw new Error('userId is required');
}
const sites = await this.repository.find({
where: { userId },
});
this.checkList(sites);
}
async checkList(sites: SiteInfoEntity[]) {
for (const site of sites) {
await this.doCheck(site);
this.doCheck(site).catch(e => {
logger.error(`检查站点证书失败,${site.domain}`, e.message);
});
await utils.sleep(200);
}
}
}
@@ -1,4 +1,4 @@
import { logger } from '@certd/basic';
import { logger, utils } from '@certd/basic';
import { merge } from 'lodash-es';
import https from 'https';
import { PeerCertificate } from 'tls';
@@ -6,6 +6,7 @@ export type SiteTestReq = {
host: string; // 只用域名部分
port?: number;
method?: string;
retryTimes?: number;
};
export type SiteTestRes = {
@@ -14,6 +15,28 @@ export type SiteTestRes = {
export class SiteTester {
async test(req: SiteTestReq): Promise<SiteTestRes> {
logger.info('测试站点:', JSON.stringify(req));
const maxRetryTimes = req.retryTimes ?? 3;
let tryCount = 0;
let result: SiteTestRes = {};
while (true) {
try {
result = await this.doTestOnce(req);
return result;
} catch (e) {
tryCount++;
if (tryCount > maxRetryTimes) {
logger.error(`测试站点出错,重试${maxRetryTimes}`, e);
throw e;
}
//指数退避
const time = 2 ** tryCount;
logger.error(`测试站点出错,${time}s后重试`, e);
await utils.sleep(time * 1000);
}
}
}
async doTestOnce(req: SiteTestReq): Promise<SiteTestRes> {
const agent = new https.Agent({ keepAlive: false });
const options: any = merge(