This commit is contained in:
dengqichen 2025-12-17 20:07:35 +08:00
parent d16b1d59ed
commit f619654b1a
3 changed files with 56 additions and 38 deletions

View File

@ -134,28 +134,45 @@ public class ThreadPoolConfig {
}
/**
* 审计事件处理线程池 - 使用虚拟线程Java 21+
* 审计事件处理线程池 - 使用传统线程池更稳定可靠
*
* 为什么使用虚拟线程
* 1. 审计事件处理是**I/O密集型**任务写日志写数据库
* 2. 虚拟线程在I/O阻塞时不占用OS线程资源消耗极低
* 3. 审计事件量可能很大虚拟线程支持高并发处理
* 4. 独立线程池避免与业务线程池竞争资源
* 为什么不使用虚拟线程
* 1. 审计任务量不大不需要虚拟线程的高并发能力
* 2. 审计日志是关键数据传统线程池的异常处理更成熟可靠
* 3. 传统线程池有队列缓冲可以防止突发流量
* 4. 传统线程池的监控和调试工具更完善
* 5. 虚拟线程在异常处理方面还不够成熟可能导致异常被吞掉
*
* 💡 场景
* - 异步记录用户操作审计日志
* - 写入审计数据库
* - 发送审计事件到消息队列
*
* 🎯 解决问题
* - 修复 "More than one TaskExecutor bean found" 警告
* - 确保审计事件处理不受其他业务线程池影响
* 🎯 线程池配置
* - 核心线程数2审计任务通常不多
* - 最大线程数4足够处理突发流量
* - 队列容量1000缓冲突发的审计事件
* - 拒绝策略CallerRunsPolicy确保审计日志不丢失
* - 优雅关闭等待60秒让审计任务完成
*/
@Bean("auditTaskExecutor")
public SimpleAsyncTaskExecutor auditTaskExecutor() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("audit-virtual-");
executor.setVirtualThreads(true);
executor.setConcurrencyLimit(-1); // 无限制支持大量并发审计事件
public AsyncTaskExecutor auditTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2); // 审计任务不多2个核心线程足够
executor.setMaxPoolSize(4); // 最大4个线程处理突发流量
executor.setQueueCapacity(1000); // 较大的队列缓冲突发审计事件
executor.setThreadNamePrefix("audit-");
executor.setKeepAliveSeconds(60);
// 关键使用CallerRunsPolicy确保审计日志不丢失
// 如果线程池满了由调用线程执行保证审计日志一定被记录
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 优雅关闭等待审计任务完成
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}

View File

@ -13,6 +13,7 @@ public class AuditEventListener {
@Async("auditTaskExecutor")
@EventListener
public void handleAuditEvent(AuditEvent event) {
try {
// 这里可以将审计信息保存到数据库或发送到日志系统
log.info("Audit: {} performed {} on {} (ID: {}) at {}, detail: {}",
event.getMetadata().getOperator(),
@ -22,5 +23,14 @@ public class AuditEventListener {
event.getMetadata().getTimestamp(),
event.getMetadata().getDetail()
);
} catch (Exception e) {
// 关键修复捕获并记录所有异常防止异常被@Async吞掉
log.error("Failed to handle audit event: operator={}, action={}, entityType={}, entityId={}",
event.getMetadata().getOperator(),
event.getMetadata().getAction(),
event.getMetadata().getEntityType(),
event.getMetadata().getEntityId(),
e);
}
}
}

View File

@ -12,24 +12,15 @@ INSERT INTO system_release (
)
VALUES (
'system', NOW(), 'system', NOW(), 1, 0,
1.41, 'ALL', NOW(),
1.42, 'ALL', NOW(),
'【后端】
- ServerLogStreamStrategy: tail -f命令重复问题logQueryCommand已包含完整命令-n
- K8sLogStreamStrategy: sessionId命名规范
- DockerLogStreamStrategy: sessionId命名规范
- AbstractLogStreamWebSocketHandler: sendLogLine(), sendStatus(), sendError()synchronized(session)
- synchronized(session)session对象session互不影响
- Strategy实现中统一使用logStreamSessionId参数名
- webSocketIdIDlogStreamSessionIdID
- cleanupSession()Shell输入输出流强制中断阻塞的read操作
- readSSHOutput()readSSHError()线
- Monaco Editor替换XTerm.js
- - LogViewerWindow中间层
- LogViewerWindow违反单一职责原则
- LOG/STATUS/ERROR消息类型
- K8S应用重复连接
-
-
- types.tsk8sNamespaceId: number和k8sDeploymentId: number改为k8sNamespaceName: string和k8sDeploymentName: string
- K8sRuntimeConfig组件name进行选择和匹配namespaceName查找对应的namespace ID来加载Deployment列表
- RuntimeConfigSection和TeamApplicationDialog组件的props传递和表单数据结构使name字段进行数据绑定和提交
- TeamApplicationManageDialog的保存逻辑name字段而非ID
',
0, NULL, NULL, 0
);