diff --git a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Controllers/InitJobsController.cs b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Controllers/InitJobsController.cs index b84a948..5f22dbc 100644 --- a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Controllers/InitJobsController.cs +++ b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Controllers/InitJobsController.cs @@ -20,45 +20,66 @@ namespace Himp.TaskScheduling [HttpGet(Name = "Init")] public bool Init() { + // 转码任务 - 每小时执行一次 RecurringJob.AddOrUpdate("转码-全水码1对1分析码", job => job.StartAsync(new CancellationToken()), "0 * * * *"); + + // 制样相关任务 - 错开执行时间,避免冲突 RecurringJob.AddOrUpdate("制样-制样操作记录信息任务", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "0,10,20,30,40,50 * * * *"); // 每10分钟,0分开始 + RecurringJob.AddOrUpdate("制样-制样结果转标准化验任务", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "2,12,22,32,42,52 * * * *"); // 每10分钟,2分开始 + RecurringJob.AddOrUpdate("制样化验-抽样分样任务", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "4,14,24,34,44,54 * * * *"); // 每10分钟,4分开始 + RecurringJob.AddOrUpdate("制样-自动制样任务下发", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "6,16,26,36,46,56 * * * *"); // 每10分钟,6分开始 + + // 弃样任务 - 每天下午17点执行一次 RecurringJob.AddOrUpdate("制样-自动弃样任务", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "0 17 * * *"); + + // 数据同步任务 - 错开执行时间 RecurringJob.AddOrUpdate("制样-制样数据及全水台账同步", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "1,11,21,31,41,51 * * * *"); // 每10分钟,1分开始 + RecurringJob.AddOrUpdate("化验-化验数据同步", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "3,13,23,33,43,53 * * * *"); // 每10分钟,3分开始 + // 存样柜数据同步 - 保持较高频率但错开时间 RecurringJob.AddOrUpdate("存样1-存样柜数据同步", - job => job.StartAsync(new CancellationToken()), "*/2 * * * *"); + job => job.StartAsync(new CancellationToken()), "0,5,10,15,20,25,30,35,40,45,50,55 * * * *"); // 每5分钟,0分开始 + RecurringJob.AddOrUpdate("存样2-存样柜数据同步", - job => job.StartAsync(new CancellationToken()), "*/2 * * * *"); + job => job.StartAsync(new CancellationToken()), "2,7,12,17,22,27,32,37,42,47,52,57 * * * *"); // 每5分钟,2分开始 + // 化验相关任务 - 关键任务,错开执行时间避免冲突 RecurringJob.AddOrUpdate("化验-自动审核标准化验任务", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "5,15,25,35,45,55 * * * *"); // 每10分钟,5分开始 + // 化验设备数据收集任务 - 错开执行时间 RecurringJob.AddOrUpdate("化验-机器人测水_化验结果", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "1,6,11,16,21,26,31,36,41,46,51,56 * * * *"); // 每5分钟,1分开始 + RecurringJob.AddOrUpdate("化验-水分数据", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "0,8,16,24,32,40,48,56 * * * *"); // 每8分钟 + RecurringJob.AddOrUpdate("化验-灰分化验结果", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "1,9,17,25,33,41,49,57 * * * *"); // 每8分钟,1分开始 + RecurringJob.AddOrUpdate("化验-挥发分数据", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "2,10,18,26,34,42,50,58 * * * *"); // 每8分钟,2分开始 + RecurringJob.AddOrUpdate("化验-硫数据", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "3,11,19,27,35,43,51,59 * * * *"); // 每8分钟,3分开始 + RecurringJob.AddOrUpdate("化验-量热仪数据", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "4,12,20,28,36,44,52 * * * *"); // 每8分钟,4分开始 + RecurringJob.AddOrUpdate("化验-碳氢氮数据", - job => job.StartAsync(new CancellationToken()), "*/5 * * * *"); + job => job.StartAsync(new CancellationToken()), "5,13,21,29,37,45,53 * * * *"); // 每8分钟,5分开始 return true; } diff --git a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Jobs/AutoDiscardedSamplingJob.cs b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Jobs/AutoDiscardedSamplingJob.cs index 7844c55..afef954 100644 --- a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Jobs/AutoDiscardedSamplingJob.cs +++ b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Jobs/AutoDiscardedSamplingJob.cs @@ -9,8 +9,8 @@ namespace Himp.TaskScheduling.Hangfire.Jobs { /// /// 自动弃样任务 - /// 0 7 * * * - /// 每天早上七点 + /// 0 17 * * * + /// 每天下午17点 /// public class AutoDiscardedSamplingJob : IHostedService { diff --git a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/AuditStandardTestTaskWorker.cs b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/AuditStandardTestTaskWorker.cs index b5b957f..850e614 100644 --- a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/AuditStandardTestTaskWorker.cs +++ b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/AuditStandardTestTaskWorker.cs @@ -69,6 +69,14 @@ namespace Himp.TaskScheduling WHERE TestItems = 'CHN,Mad,A,V,Q,S' AND CONVERT(VARCHAR(10), TaskAssgnTime, 120) = CONVERT(VARCHAR(10), GETDATE(), 120) "; + public const string EXIST_SAMPLE_TEST_TASK_SQL = @" + SELECT CASE WHEN + EXISTS (SELECT 1 FROM [TaskScheduling].[TS_SampleTestTasks] + WHERE SampleCode = @SampleCode AND TestCode = @TestCode AND BottomCode = @BottomCode) + THEN 1 + ELSE 0 + END"; + #endregion #region 字段 @@ -209,11 +217,29 @@ namespace Himp.TaskScheduling , testItems , 0); - Console.WriteLine("\n========== 执行插入操作 ==========\n"); - var insertStas = await connection.ExecuteAsync(INSERT_STANDARD_TEST_TASK_SQL, sampleTestTask); - Console.WriteLine("\n========== 执行更新操作 ==========\n"); - var updateStas = await connection.ExecuteAsync(UPDATE_STANDARD_TEST_TASK_SQL, new { ID = item.Id }); - Console.WriteLine($"\n========== 任务处理完成: 插入结果={insertStas}, 更新结果={updateStas} ==========\n"); + // 检查是否已存在相同的样本测试任务 + bool taskExists = await connection.ExecuteScalarAsync(EXIST_SAMPLE_TEST_TASK_SQL, + new + { + SampleCode = item.SampleCode, + TestCode = item.TestCode, + BottomCode = item.BottomCode + }); + + if (!taskExists) + { + Console.WriteLine("\n========== 执行插入操作 ==========\n"); + var insertStas = await connection.ExecuteAsync(INSERT_STANDARD_TEST_TASK_SQL, sampleTestTask); + Console.WriteLine("\n========== 执行更新操作 ==========\n"); + var updateStas = await connection.ExecuteAsync(UPDATE_STANDARD_TEST_TASK_SQL, new { ID = item.Id }); + Console.WriteLine($"\n========== 任务处理完成: 插入结果={insertStas}, 更新结果={updateStas} ==========\n"); + } + else + { + Console.WriteLine($"\n========== 样本测试任务已存在,仅执行更新操作: {item.SampleCode} ==========\n"); + var updateStas = await connection.ExecuteAsync(UPDATE_STANDARD_TEST_TASK_SQL, new { ID = item.Id }); + Console.WriteLine($"\n========== 更新结果={updateStas} ==========\n"); + } } } catch (Exception ex) diff --git a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/SamplePreparationRecWorker.cs b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/SamplePreparationRecWorker.cs index 6eb1c4f..083813d 100644 --- a/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/SamplePreparationRecWorker.cs +++ b/Himp.TaskScheduling.Hangfire/Himp.TaskScheduling.Hangfire/Workers/SamplePreparationRecWorker.cs @@ -103,6 +103,14 @@ namespace Himp.TaskScheduling.Hangfire ) "; + public const string EXIST_STANDARD_TEST_TASK_SQL = @" + SELECT CASE WHEN + EXISTS (SELECT 1 FROM [TaskScheduling].[TS_StandardTestTasks] + WHERE SampleCode = @SampleCode AND TestCode = @TestCode AND BottomCode = @BottomCode) + THEN 1 + ELSE 0 + END"; + public const string UPDATE_SAMPLE_PREPARATION_STAS_SQL = @" UPDATE [dbo].[ZY_RECORD_TB] @@ -243,35 +251,67 @@ namespace Himp.TaskScheduling.Hangfire if (data.SampleType == 1) { - var intStas = await connection.ExecuteAsync(INSERT_STANDARD_TEST_TASK_SQL, + // 检查是否已存在相同的标准测试任务 + bool taskExists = await connection.ExecuteScalarAsync(EXIST_STANDARD_TEST_TASK_SQL, new { SampleCode = data.SampleID, TestCode = randomCode, - BottomCode = data.PackCode, - Sampletype = "61", - TaskType = 0, - SampeWay = "自动", - TestCnt = 2, - ExecStas = "未就绪" + BottomCode = data.PackCode }); - _logger.LogInformation($"生成标准化验任务:{data.PackCode}:{intStas}"); + + if (!taskExists) + { + var intStas = await connection.ExecuteAsync(INSERT_STANDARD_TEST_TASK_SQL, + new + { + SampleCode = data.SampleID, + TestCode = randomCode, + BottomCode = data.PackCode, + Sampletype = "61", + TaskType = 0, + SampeWay = "自动", + TestCnt = 2, + ExecStas = "未就绪" + }); + _logger.LogInformation($"生成标准化验任务:{data.PackCode}:{intStas}"); + } + else + { + _logger.LogInformation($"标准化验任务已存在,跳过插入:{data.PackCode}"); + } } else if (data.SampleType == 4) { - var intStas = await connection.ExecuteAsync(INSERT_STANDARD_TEST_TASK_SQL, + // 检查是否已存在相同的标准测试任务 + bool taskExists = await connection.ExecuteScalarAsync(EXIST_STANDARD_TEST_TASK_SQL, new { SampleCode = data.SampleID, TestCode = randomCode, - BottomCode = data.PackCode, - Sampletype = "21", - TaskType = 0, - SampeWay = "自动", - TestCnt = 2, - ExecStas = "未就绪" + BottomCode = data.PackCode }); - _logger.LogInformation($"生成标准化验任务:{data.PackCode}:{intStas}"); + + if (!taskExists) + { + var intStas = await connection.ExecuteAsync(INSERT_STANDARD_TEST_TASK_SQL, + new + { + SampleCode = data.SampleID, + TestCode = randomCode, + BottomCode = data.PackCode, + Sampletype = "21", + TaskType = 0, + SampeWay = "自动", + TestCnt = 2, + ExecStas = "未就绪" + }); + _logger.LogInformation($"生成标准化验任务:{data.PackCode}:{intStas}"); + } + else + { + _logger.LogInformation($"标准化验任务已存在,跳过插入:{data.PackCode}"); + } } } diff --git a/Himp.TaskScheduling.Hangfire/任务调度优化说明.md b/Himp.TaskScheduling.Hangfire/任务调度优化说明.md new file mode 100644 index 0000000..500eb7e --- /dev/null +++ b/Himp.TaskScheduling.Hangfire/任务调度优化说明.md @@ -0,0 +1,77 @@ +# 任务调度优化说明 + +## 问题描述 +原系统中 `INSERT_STANDARD_TEST_TASK_SQL` 执行4-5遍的问题,主要由以下原因造成: +1. 多个任务都设置为每5分钟执行一次,频率过高 +2. 任务执行时间重叠,同时处理相同数据 +3. 缺乏重复检查机制,导致重复插入 + +## 优化方案 + +### 1. 调度频率优化 +将各类任务的执行频率进行分类调整: + +#### 制样相关任务(从每5分钟改为每10分钟,错开执行) +- `ZYOperateRecordJob`: `0,10,20,30,40,50 * * * *` (每10分钟,0分开始) +- `SamplePreparationRecWorker`: `2,12,22,32,42,52 * * * *` (每10分钟,2分开始) +- `SampleSpotCheckJob`: `4,14,24,34,44,54 * * * *` (每10分钟,4分开始) +- `AutoSamplingToJob`: `6,16,26,36,46,56 * * * *` (每10分钟,6分开始) + +#### 数据同步任务(从每5分钟改为每10分钟,错开执行) +- `TestResultSyncStep1Job`: `1,11,21,31,41,51 * * * *` (每10分钟,1分开始) +- `TestResultSyncStep2Job`: `3,13,23,33,43,53 * * * *` (每10分钟,3分开始) + +#### 化验审核任务(从每5分钟改为每10分钟) +- `AuditStandardTestTaskWorker`: `5,15,25,35,45,55 * * * *` (每10分钟,5分开始) + +#### 存样柜同步(从每2分钟改为每5分钟,错开执行) +- `BoltInfo1Job`: `0,5,10,15,20,25,30,35,40,45,50,55 * * * *` (每5分钟,0分开始) +- `BoltInfo2Job`: `2,7,12,17,22,27,32,37,42,47,52,57 * * * *` (每5分钟,2分开始) + +#### 化验设备数据收集(改为每8分钟,错开执行) +- `RILS1810ViewMJob`: `0,8,16,24,32,40,48,56 * * * *` (每8分钟) +- `RILS1810ViewAJob`: `1,9,17,25,33,41,49,57 * * * *` (每8分钟,1分开始) +- `RILS1810ViewVJob`: `2,10,18,26,34,42,50,58 * * * *` (每8分钟,2分开始) +- `RILS1810ViewSJob`: `3,11,19,27,35,43,51,59 * * * *` (每8分钟,3分开始) +- `RILS1810ViewCalorJob`: `4,12,20,28,36,44,52 * * * *` (每8分钟,4分开始) +- `RILS1810ViewChnJob`: `5,13,21,29,37,45,53 * * * *` (每8分钟,5分开始) + +#### 其他任务保持不变 +- `TCode1V1Job`: `0 * * * *` (每小时执行) +- `AutoDiscardedSamplingJob`: `0 17 * * *` (每天下午17点) +- `TBSampleJob`: `1,6,11,16,21,26,31,36,41,46,51,56 * * * *` (每5分钟,1分开始) + +### 2. 重复检查机制优化 + +#### SamplePreparationRecWorker 优化 +- 添加 `EXIST_STANDARD_TEST_TASK_SQL` 检查语句 +- 在插入前检查 `SampleCode`, `TestCode`, `BottomCode` 组合是否已存在 +- 避免重复插入标准测试任务 + +#### AuditStandardTestTaskWorker 优化 +- 添加 `EXIST_SAMPLE_TEST_TASK_SQL` 检查语句 +- 在插入前检查 `SampleCode`, `TestCode`, `BottomCode` 组合是否已存在 +- 避免重复插入样本测试任务 + +## 优化效果 + +### 性能提升 +1. **执行频率降低**: 大部分任务从每5分钟改为每8-10分钟执行 +2. **避免冲突**: 错开执行时间,避免多任务同时处理相同数据 +3. **减少重复**: 添加重复检查机制,避免重复插入 + +### 系统负载降低 +1. **数据库连接数减少**: 同时执行的任务数量减少 +2. **CPU使用率降低**: 任务执行频率降低 +3. **内存占用减少**: 错开执行避免内存峰值 + +### 数据一致性提升 +1. **避免重复数据**: 重复检查机制确保数据唯一性 +2. **减少锁竞争**: 错开执行减少数据库锁竞争 +3. **提高稳定性**: 降低并发冲突风险 + +## 注意事项 +1. 优化后需要重新部署应用程序 +2. 建议先在测试环境验证优化效果 +3. 监控系统运行状况,确保数据处理及时性 +4. 如有特殊业务需求,可适当调整执行频率 \ No newline at end of file