ai-engineering-init 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/skill-forced-eval.js +46 -62
- package/.claude/settings.json +10 -1
- package/.claude/skills/api-development/SKILL.md +179 -130
- package/.claude/skills/architecture-design/SKILL.md +102 -212
- package/.claude/skills/backend-annotations/SKILL.md +166 -220
- package/.claude/skills/bug-detective/SKILL.md +225 -186
- package/.claude/skills/code-patterns/SKILL.md +127 -244
- package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
- package/.claude/skills/crud-development/SKILL.md +226 -307
- package/.claude/skills/data-permission/SKILL.md +131 -202
- package/.claude/skills/database-ops/SKILL.md +158 -355
- package/.claude/skills/error-handler/SKILL.md +224 -285
- package/.claude/skills/file-oss-management/SKILL.md +174 -169
- package/.claude/skills/git-workflow/SKILL.md +123 -341
- package/.claude/skills/json-serialization/SKILL.md +121 -137
- package/.claude/skills/performance-doctor/SKILL.md +83 -89
- package/.claude/skills/redis-cache/SKILL.md +134 -185
- package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
- package/.claude/skills/security-guard/SKILL.md +168 -276
- package/.claude/skills/sms-mail/SKILL.md +266 -228
- package/.claude/skills/social-login/SKILL.md +257 -195
- package/.claude/skills/tenant-management/SKILL.md +172 -188
- package/.claude/skills/utils-toolkit/SKILL.md +214 -222
- package/.claude/skills/websocket-sse/SKILL.md +251 -172
- package/.claude/skills/workflow-engine/SKILL.md +178 -250
- package/.codex/skills/api-development/SKILL.md +179 -130
- package/.codex/skills/architecture-design/SKILL.md +102 -212
- package/.codex/skills/backend-annotations/SKILL.md +166 -220
- package/.codex/skills/bug-detective/SKILL.md +225 -186
- package/.codex/skills/code-patterns/SKILL.md +127 -244
- package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
- package/.codex/skills/crud-development/SKILL.md +226 -307
- package/.codex/skills/data-permission/SKILL.md +131 -202
- package/.codex/skills/database-ops/SKILL.md +158 -355
- package/.codex/skills/error-handler/SKILL.md +224 -285
- package/.codex/skills/file-oss-management/SKILL.md +174 -169
- package/.codex/skills/git-workflow/SKILL.md +123 -341
- package/.codex/skills/json-serialization/SKILL.md +121 -137
- package/.codex/skills/performance-doctor/SKILL.md +83 -89
- package/.codex/skills/redis-cache/SKILL.md +134 -185
- package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
- package/.codex/skills/security-guard/SKILL.md +168 -276
- package/.codex/skills/sms-mail/SKILL.md +266 -228
- package/.codex/skills/social-login/SKILL.md +257 -195
- package/.codex/skills/tenant-management/SKILL.md +172 -188
- package/.codex/skills/utils-toolkit/SKILL.md +214 -222
- package/.codex/skills/websocket-sse/SKILL.md +251 -172
- package/.codex/skills/workflow-engine/SKILL.md +178 -250
- package/.cursor/hooks/cursor-skill-eval.js +66 -6
- package/.cursor/skills/api-development/SKILL.md +179 -130
- package/.cursor/skills/architecture-design/SKILL.md +102 -212
- package/.cursor/skills/backend-annotations/SKILL.md +166 -220
- package/.cursor/skills/bug-detective/SKILL.md +225 -186
- package/.cursor/skills/code-patterns/SKILL.md +127 -244
- package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
- package/.cursor/skills/crud-development/SKILL.md +226 -307
- package/.cursor/skills/data-permission/SKILL.md +131 -202
- package/.cursor/skills/database-ops/SKILL.md +158 -355
- package/.cursor/skills/error-handler/SKILL.md +224 -285
- package/.cursor/skills/file-oss-management/SKILL.md +174 -169
- package/.cursor/skills/git-workflow/SKILL.md +123 -341
- package/.cursor/skills/json-serialization/SKILL.md +121 -137
- package/.cursor/skills/performance-doctor/SKILL.md +83 -89
- package/.cursor/skills/redis-cache/SKILL.md +134 -185
- package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
- package/.cursor/skills/security-guard/SKILL.md +168 -276
- package/.cursor/skills/sms-mail/SKILL.md +266 -228
- package/.cursor/skills/social-login/SKILL.md +257 -195
- package/.cursor/skills/tenant-management/SKILL.md +172 -188
- package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
- package/.cursor/skills/websocket-sse/SKILL.md +251 -172
- package/.cursor/skills/workflow-engine/SKILL.md +178 -250
- package/AGENTS.md +49 -540
- package/CLAUDE.md +73 -119
- package/README.md +37 -6
- package/bin/index.js +5 -1
- package/package.json +1 -1
- package/src/skills/api-development/SKILL.md +179 -130
- package/src/skills/architecture-design/SKILL.md +102 -212
- package/src/skills/backend-annotations/SKILL.md +166 -220
- package/src/skills/bug-detective/SKILL.md +225 -186
- package/src/skills/code-patterns/SKILL.md +127 -244
- package/src/skills/collaborating-with-codex/SKILL.md +96 -113
- package/src/skills/crud-development/SKILL.md +226 -307
- package/src/skills/data-permission/SKILL.md +131 -202
- package/src/skills/database-ops/SKILL.md +158 -355
- package/src/skills/error-handler/SKILL.md +224 -285
- package/src/skills/file-oss-management/SKILL.md +174 -169
- package/src/skills/git-workflow/SKILL.md +123 -341
- package/src/skills/json-serialization/SKILL.md +121 -137
- package/src/skills/performance-doctor/SKILL.md +83 -89
- package/src/skills/redis-cache/SKILL.md +134 -185
- package/src/skills/scheduled-jobs/SKILL.md +187 -224
- package/src/skills/security-guard/SKILL.md +168 -276
- package/src/skills/sms-mail/SKILL.md +266 -228
- package/src/skills/social-login/SKILL.md +257 -195
- package/src/skills/tenant-management/SKILL.md +172 -188
- package/src/skills/utils-toolkit/SKILL.md +214 -222
- package/src/skills/websocket-sse/SKILL.md +251 -172
- package/src/skills/workflow-engine/SKILL.md +178 -250
- package/.claude/skills/skill-creator/LICENSE.txt +0 -202
- package/.claude/skills/skill-creator/SKILL.md +0 -479
- package/.claude/skills/skill-creator/agents/analyzer.md +0 -274
- package/.claude/skills/skill-creator/agents/comparator.md +0 -202
- package/.claude/skills/skill-creator/agents/grader.md +0 -223
- package/.claude/skills/skill-creator/assets/eval_review.html +0 -146
- package/.claude/skills/skill-creator/eval-viewer/generate_review.py +0 -471
- package/.claude/skills/skill-creator/eval-viewer/viewer.html +0 -1325
- package/.claude/skills/skill-creator/references/schemas.md +0 -430
- package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
- package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
- package/.claude/skills/skill-creator/scripts/generate_report.py +0 -326
- package/.claude/skills/skill-creator/scripts/improve_description.py +0 -248
- package/.claude/skills/skill-creator/scripts/package_skill.py +0 -136
- package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -103
- package/.claude/skills/skill-creator/scripts/run_eval.py +0 -310
- package/.claude/skills/skill-creator/scripts/run_loop.py +0 -332
- package/.claude/skills/skill-creator/scripts/utils.py +0 -47
|
@@ -1,224 +1,176 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scheduled-jobs
|
|
3
3
|
description: |
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
通用定时任务开发指南。涵盖 @Scheduled、Quartz、XXL-Job 等方案的概念对比与实现模式。
|
|
6
5
|
触发场景:
|
|
7
6
|
- 每日数据汇总、定期清理等周期性任务
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
核心特性:
|
|
15
|
-
- @Scheduled:简单周期任务、框架内置
|
|
16
|
-
- SnailJob:分布式集群、可视化管理、失败重试、工作流编排
|
|
7
|
+
- 分布式任务调度、失败重试
|
|
8
|
+
- 任务分片、广播任务
|
|
9
|
+
- 可视化任务管理和监控
|
|
10
|
+
触发词:定时任务、任务调度、@Scheduled、Quartz、XXL-Job、Cron、重试、分片任务、广播任务、分布式任务
|
|
11
|
+
注意:如果项目有专属技能(如 `leniu-java-task`),优先使用专属版本。
|
|
17
12
|
---
|
|
18
13
|
|
|
19
14
|
# 定时任务开发指南
|
|
20
15
|
|
|
21
|
-
>
|
|
16
|
+
> 通用模板。如果项目有专属技能(如 `leniu-java-task`),优先使用。
|
|
22
17
|
|
|
23
|
-
##
|
|
18
|
+
## 设计原则
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
| 广播(所有节点执行) | **SnailJob** | 支持广播模式 |
|
|
30
|
-
| 海量数据分片 | **SnailJob** | 静态分片/Map/MapReduce |
|
|
20
|
+
1. **幂等性**:任务重复执行不会产生副作用。考虑任务被重试、多次调度的情况。
|
|
21
|
+
2. **可观测**:任务执行结果必须可追踪(日志、监控、告警)。
|
|
22
|
+
3. **故障隔离**:单个任务失败不应影响其他任务的调度。
|
|
23
|
+
4. **超时控制**:设置合理的执行超时时间,避免任务无限阻塞。
|
|
31
24
|
|
|
32
25
|
---
|
|
33
26
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
```yaml
|
|
37
|
-
# application-dev.yml
|
|
38
|
-
snail-job:
|
|
39
|
-
enabled: ${SNAIL_JOB_ENABLED:false}
|
|
40
|
-
group: ${app.id}
|
|
41
|
-
token: ${SNAIL_JOB_TOKEN:SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT}
|
|
42
|
-
server:
|
|
43
|
-
host: ${SNAIL_JOB_HOST:127.0.0.1}
|
|
44
|
-
port: ${SNAIL_JOB_PORT:17888}
|
|
45
|
-
namespace: ${spring.profiles.active}
|
|
46
|
-
port: 2${server.port}
|
|
47
|
-
```
|
|
27
|
+
## 方案对比
|
|
48
28
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
29
|
+
| 维度 | @Scheduled | Quartz | XXL-Job | 其他(SnailJob 等) |
|
|
30
|
+
|------|-----------|--------|---------|-------------------|
|
|
31
|
+
| 学习成本 | 极低 | 中等 | 低 | 低-中 |
|
|
32
|
+
| 分布式支持 | 无 | 支持(数据库锁) | 原生支持 | 原生支持 |
|
|
33
|
+
| 可视化管理 | 无 | 无(需自建) | Web 控制台 | Web 控制台 |
|
|
34
|
+
| 失败重试 | 无 | 支持 | 支持 | 支持 |
|
|
35
|
+
| 任务分片 | 不支持 | 不支持 | 支持 | 支持 |
|
|
36
|
+
| 广播模式 | 不支持 | 不支持 | 支持 | 支持 |
|
|
37
|
+
| 工作流编排 | 不支持 | 不支持 | 子任务依赖 | 部分支持 |
|
|
38
|
+
| 依赖 | Spring 内置 | quartz jar | 独立服务 | 独立服务 |
|
|
39
|
+
| 适用场景 | 简单周期任务 | 中等复杂度 | 生产级分布式 | 生产级分布式 |
|
|
40
|
+
|
|
41
|
+
### 选型决策树
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
需要分布式调度?
|
|
45
|
+
├── 否 → @Scheduled(简单定时)
|
|
46
|
+
└── 是 → 需要可视化管理?
|
|
47
|
+
├── 否 → Quartz + 数据库(中等规模)
|
|
48
|
+
└── 是 → XXL-Job / SnailJob / PowerJob(生产推荐)
|
|
56
49
|
```
|
|
57
50
|
|
|
58
51
|
---
|
|
59
52
|
|
|
60
|
-
##
|
|
53
|
+
## 实现模式
|
|
61
54
|
|
|
62
|
-
###
|
|
63
|
-
|
|
64
|
-
> 源码:`ruoyi-job/.../snailjob/TestAnnoJobExecutor.java`
|
|
55
|
+
### 方案一:@Scheduled(Spring 内置)
|
|
65
56
|
|
|
66
57
|
```java
|
|
67
|
-
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
|
|
68
|
-
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
|
|
69
|
-
import com.aizuda.snailjob.model.dto.ExecuteResult;
|
|
70
|
-
import com.aizuda.snailjob.common.log.SnailJobLog;
|
|
71
|
-
import com.aizuda.snailjob.common.core.util.JsonUtil;
|
|
72
|
-
|
|
73
58
|
@Component
|
|
74
|
-
@
|
|
75
|
-
public class
|
|
59
|
+
@EnableScheduling
|
|
60
|
+
public class SimpleScheduledTasks {
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return ExecuteResult.success("执行成功");
|
|
62
|
+
// 固定频率:每30秒执行一次
|
|
63
|
+
@Scheduled(fixedRate = 30000)
|
|
64
|
+
public void heartbeat() {
|
|
65
|
+
log.info("心跳检测");
|
|
82
66
|
}
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 广播任务
|
|
87
67
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
68
|
+
// 固定延迟:上次执行完成后等10秒再执行
|
|
69
|
+
@Scheduled(fixedDelay = 10000)
|
|
70
|
+
public void cleanTempFiles() {
|
|
71
|
+
log.info("清理临时文件");
|
|
72
|
+
}
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
@
|
|
94
|
-
|
|
95
|
-
|
|
74
|
+
// Cron 表达式:每天凌晨2点执行
|
|
75
|
+
@Scheduled(cron = "0 0 2 * * ?")
|
|
76
|
+
public void dailyReport() {
|
|
77
|
+
log.info("生成日报");
|
|
78
|
+
}
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
80
|
+
// 使用配置文件中的 Cron
|
|
81
|
+
@Scheduled(cron = "${task.cleanup.cron:0 0 3 * * ?}")
|
|
82
|
+
public void configuredTask() {
|
|
83
|
+
log.info("可配置的定时任务");
|
|
101
84
|
}
|
|
102
85
|
}
|
|
103
86
|
```
|
|
104
87
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
按固定规则分片,每个节点处理不同数据范围。
|
|
88
|
+
**注意事项**:
|
|
89
|
+
- 默认单线程执行,需配置线程池避免任务阻塞
|
|
90
|
+
- 多实例部署时每个实例都会执行,需分布式锁控制
|
|
110
91
|
|
|
111
92
|
```java
|
|
112
|
-
@
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
public ExecuteResult jobExecute(JobArgs jobArgs) {
|
|
118
|
-
String[] split = jobArgs.getJobParams().split(",");
|
|
119
|
-
Long fromId = Long.parseLong(split[0]);
|
|
120
|
-
Long toId = Long.parseLong(split[1]);
|
|
121
|
-
SnailJobLog.REMOTE.info("处理 ID 范围: {} - {}", fromId, toId);
|
|
122
|
-
// 处理该范围的数据
|
|
123
|
-
return ExecuteResult.success("分片任务完成");
|
|
93
|
+
@Configuration
|
|
94
|
+
public class SchedulingConfig implements SchedulingConfigurer {
|
|
95
|
+
@Override
|
|
96
|
+
public void configureTasks(ScheduledTaskRegistrar registrar) {
|
|
97
|
+
registrar.setScheduler(Executors.newScheduledThreadPool(5));
|
|
124
98
|
}
|
|
125
99
|
}
|
|
126
100
|
```
|
|
127
101
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
### Map 任务(动态分片)
|
|
131
|
-
|
|
132
|
-
> 源码:`ruoyi-job/.../snailjob/TestMapJobAnnotation.java`
|
|
133
|
-
|
|
134
|
-
运行时动态拆分数据,并行执行。
|
|
102
|
+
### 方案二:Quartz
|
|
135
103
|
|
|
136
104
|
```java
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
105
|
+
// 1. 定义 Job
|
|
106
|
+
public class OrderCleanupJob implements Job {
|
|
107
|
+
@Override
|
|
108
|
+
public void execute(JobExecutionContext context) throws JobExecutionException {
|
|
109
|
+
JobDataMap dataMap = context.getMergedJobDataMap();
|
|
110
|
+
int days = dataMap.getInt("retentionDays");
|
|
111
|
+
// 业务逻辑...
|
|
112
|
+
}
|
|
113
|
+
}
|
|
140
114
|
|
|
141
|
-
|
|
142
|
-
@
|
|
143
|
-
public class
|
|
144
|
-
|
|
145
|
-
@
|
|
146
|
-
public
|
|
147
|
-
|
|
148
|
-
.
|
|
149
|
-
.
|
|
150
|
-
.
|
|
151
|
-
|
|
115
|
+
// 2. 配置调度
|
|
116
|
+
@Configuration
|
|
117
|
+
public class QuartzConfig {
|
|
118
|
+
|
|
119
|
+
@Bean
|
|
120
|
+
public JobDetail orderCleanupJobDetail() {
|
|
121
|
+
return JobBuilder.newJob(OrderCleanupJob.class)
|
|
122
|
+
.withIdentity("orderCleanup", "maintenance")
|
|
123
|
+
.usingJobData("retentionDays", 30)
|
|
124
|
+
.storeDurably()
|
|
125
|
+
.build();
|
|
152
126
|
}
|
|
153
127
|
|
|
154
|
-
@
|
|
155
|
-
public
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
128
|
+
@Bean
|
|
129
|
+
public Trigger orderCleanupTrigger() {
|
|
130
|
+
return TriggerBuilder.newTrigger()
|
|
131
|
+
.forJob(orderCleanupJobDetail())
|
|
132
|
+
.withIdentity("orderCleanupTrigger", "maintenance")
|
|
133
|
+
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 * * ?"))
|
|
134
|
+
.build();
|
|
159
135
|
}
|
|
160
136
|
}
|
|
161
137
|
```
|
|
162
138
|
|
|
163
|
-
###
|
|
164
|
-
|
|
165
|
-
> 源码:`ruoyi-job/.../snailjob/TestMapReduceAnnotation1.java`
|
|
166
|
-
|
|
167
|
-
在 Map 基础上增加 Reduce 汇总。
|
|
139
|
+
### 方案三:分布式任务调度平台(XXL-Job 等)
|
|
168
140
|
|
|
169
141
|
```java
|
|
170
|
-
|
|
171
|
-
import com.aizuda.snailjob.client.job.core.dto.ReduceArgs;
|
|
172
|
-
|
|
142
|
+
// 通用模式:注解标记任务处理器
|
|
173
143
|
@Component
|
|
174
|
-
|
|
175
|
-
public class TestMapReduceAnnotation1 {
|
|
176
|
-
|
|
177
|
-
@MapExecutor
|
|
178
|
-
public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
|
|
179
|
-
// 拆分数据(同 Map 任务)
|
|
180
|
-
return mapHandler.doMap(partition, "doCalc");
|
|
181
|
-
}
|
|
144
|
+
public class OrderCleanupJobHandler {
|
|
182
145
|
|
|
183
|
-
@
|
|
184
|
-
|
|
185
|
-
List<Integer> sourceList = (List<Integer>) mapArgs.getMapResult();
|
|
186
|
-
return ExecuteResult.success(sourceList.stream().mapToInt(i -> i).sum());
|
|
187
|
-
}
|
|
146
|
+
@Autowired
|
|
147
|
+
private OrderService orderService;
|
|
188
148
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
149
|
+
// 具体注解取决于所选平台
|
|
150
|
+
// XXL-Job: @XxlJob("orderCleanupHandler")
|
|
151
|
+
// SnailJob: @JobExecutor(name = "orderCleanupJob")
|
|
152
|
+
public void execute(String params) {
|
|
153
|
+
log.info("开始清理过期订单");
|
|
154
|
+
try {
|
|
155
|
+
int days = StringUtils.isBlank(params) ? 30 : Integer.parseInt(params);
|
|
156
|
+
int count = orderService.cleanupExpiredOrders(days);
|
|
157
|
+
log.info("清理完成,删除 {} 条", count);
|
|
158
|
+
} catch (Exception e) {
|
|
159
|
+
log.error("清理失败: {}", e.getMessage());
|
|
160
|
+
throw e; // 抛出异常触发平台重试
|
|
161
|
+
}
|
|
196
162
|
}
|
|
197
163
|
}
|
|
198
164
|
```
|
|
199
165
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
## 执行模式对比
|
|
166
|
+
### 执行模式
|
|
203
167
|
|
|
204
168
|
| 模式 | 特点 | 适用场景 |
|
|
205
169
|
|------|------|---------|
|
|
206
|
-
|
|
|
207
|
-
| **广播** | 所有节点都执行 |
|
|
208
|
-
|
|
|
209
|
-
| **
|
|
210
|
-
| **MapReduce** | 动态分片 + 结果汇总 | 分布式计算(求和、统计) |
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## 日志工具
|
|
215
|
-
|
|
216
|
-
```java
|
|
217
|
-
import com.aizuda.snailjob.common.log.SnailJobLog;
|
|
218
|
-
|
|
219
|
-
SnailJobLog.LOCAL.info("本地日志: {}", msg); // 输出到控制台/日志文件
|
|
220
|
-
SnailJobLog.REMOTE.info("远程日志: {}", msg); // 上报到 SnailJob 控制台
|
|
221
|
-
```
|
|
170
|
+
| **集群/随机** | 多节点竞争,只有一个执行 | 订单处理、数据汇总 |
|
|
171
|
+
| **广播** | 所有节点都执行 | 清理本地缓存、刷新配置 |
|
|
172
|
+
| **分片** | 按规则分片,每节点处理部分数据 | 大数据量批处理 |
|
|
173
|
+
| **MapReduce** | 动态分片 + 结果汇总 | 分布式计算、统计 |
|
|
222
174
|
|
|
223
175
|
---
|
|
224
176
|
|
|
@@ -228,87 +180,98 @@ SnailJobLog.REMOTE.info("远程日志: {}", msg); // 上报到 SnailJob 控制
|
|
|
228
180
|
|
|
229
181
|
```java
|
|
230
182
|
@Component
|
|
231
|
-
|
|
232
|
-
|
|
183
|
+
public class [你的任务类] {
|
|
184
|
+
|
|
185
|
+
private static final Logger log = LoggerFactory.getLogger([你的任务类].class);
|
|
233
186
|
|
|
234
187
|
@Autowired
|
|
235
|
-
private
|
|
188
|
+
private [你的业务Service] bizService;
|
|
189
|
+
|
|
190
|
+
public void execute(String params) {
|
|
191
|
+
long startTime = System.currentTimeMillis();
|
|
192
|
+
log.info("[任务开始] 参数: {}", params);
|
|
236
193
|
|
|
237
|
-
public ExecuteResult jobExecute(JobArgs jobArgs) {
|
|
238
|
-
SnailJobLog.REMOTE.info("开始清理过期订单");
|
|
239
194
|
try {
|
|
240
|
-
|
|
241
|
-
int days =
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
195
|
+
// 1. 解析参数
|
|
196
|
+
int days = parseParams(params);
|
|
197
|
+
|
|
198
|
+
// 2. 幂等检查
|
|
199
|
+
if (bizService.isAlreadyProcessed(today())) {
|
|
200
|
+
log.info("[任务跳过] 今日已执行");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 3. 执行业务逻辑
|
|
205
|
+
int count = bizService.process(days);
|
|
206
|
+
|
|
207
|
+
// 4. 记录结果
|
|
208
|
+
long cost = System.currentTimeMillis() - startTime;
|
|
209
|
+
log.info("[任务完成] 处理 {} 条,耗时 {} ms", count, cost);
|
|
210
|
+
|
|
245
211
|
} catch (Exception e) {
|
|
246
|
-
|
|
212
|
+
log.error("[任务失败] {}", e.getMessage(), e);
|
|
247
213
|
throw e; // 抛出异常触发重试
|
|
248
214
|
}
|
|
249
215
|
}
|
|
250
216
|
}
|
|
251
217
|
```
|
|
252
218
|
|
|
253
|
-
###
|
|
254
|
-
|
|
255
|
-
```java
|
|
256
|
-
public ExecuteResult jobExecute(JobArgs jobArgs) {
|
|
257
|
-
String orderId = jobArgs.getJobParams();
|
|
258
|
-
if (paymentService.isSynced(orderId)) {
|
|
259
|
-
return ExecuteResult.success("已同步,跳过");
|
|
260
|
-
}
|
|
261
|
-
paymentService.sync(orderId);
|
|
262
|
-
return ExecuteResult.success("同步成功");
|
|
263
|
-
}
|
|
264
|
-
```
|
|
219
|
+
### 重试策略对比
|
|
265
220
|
|
|
266
|
-
|
|
221
|
+
| 策略 | 说明 | 适用场景 |
|
|
222
|
+
|------|------|---------|
|
|
223
|
+
| 固定间隔 | 每次间隔相同(如 30s) | 网络抖动、临时故障 |
|
|
224
|
+
| 指数退避 | 间隔逐倍增加(1s, 2s, 4s, 8s...) | 服务恢复中 |
|
|
225
|
+
| Cron 重试 | 按 Cron 表达式重试 | 定点重试 |
|
|
226
|
+
| 最大次数 | 限制最大重试次数 | 防止无限重试 |
|
|
267
227
|
|
|
268
|
-
|
|
269
|
-
// 抛出异常 -> SnailJob 自动重试
|
|
270
|
-
throw e;
|
|
228
|
+
### Cron 表达式速查
|
|
271
229
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
230
|
+
| 表达式 | 含义 |
|
|
231
|
+
|--------|------|
|
|
232
|
+
| `0 0 2 * * ?` | 每天凌晨 2:00 |
|
|
233
|
+
| `0 0/30 * * * ?` | 每 30 分钟 |
|
|
234
|
+
| `0 0 9-18 * * MON-FRI` | 工作日 9-18 点每小时 |
|
|
235
|
+
| `0 0 0 1 * ?` | 每月 1 号零点 |
|
|
236
|
+
| `0 0 0 L * ?` | 每月最后一天零点 |
|
|
275
237
|
|
|
276
238
|
---
|
|
277
239
|
|
|
278
|
-
##
|
|
279
|
-
|
|
280
|
-
1. **任务管理** -> **新增任务**
|
|
281
|
-
2. 任务名称与 `@JobExecutor(name)` 一致
|
|
282
|
-
3. 任务类型:集群/广播/静态分片/Map/MapReduce
|
|
283
|
-
4. 触发类型:CRON / 固定频率
|
|
284
|
-
|
|
285
|
-
### 重试策略
|
|
286
|
-
|
|
287
|
-
| 策略 | 说明 | 场景 |
|
|
288
|
-
|------|------|------|
|
|
289
|
-
| 固定间隔 | 每次间隔相同 | 网络抖动 |
|
|
290
|
-
| 指数退避 | 间隔逐倍增加 | 服务恢复中 |
|
|
291
|
-
| CRON | 按表达式重试 | 定点重试 |
|
|
240
|
+
## 常见错误
|
|
292
241
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
242
|
+
```java
|
|
243
|
+
// 1. @Scheduled 任务中抛出异常但不处理
|
|
244
|
+
@Scheduled(fixedRate = 60000)
|
|
245
|
+
public void task() {
|
|
246
|
+
service.process(); // 异常后任务停止调度!
|
|
247
|
+
}
|
|
248
|
+
// 应捕获异常
|
|
249
|
+
@Scheduled(fixedRate = 60000)
|
|
250
|
+
public void task() {
|
|
251
|
+
try {
|
|
252
|
+
service.process();
|
|
253
|
+
} catch (Exception e) {
|
|
254
|
+
log.error("任务执行失败", e);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
296
257
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
| 任务不执行 | 1.检查 `snail-job.enabled: true` 2.`@JobExecutor(name)` 与控制台一致 3.SnailJob 服务是否启动 |
|
|
300
|
-
| @Scheduled vs SnailJob | 简单/<100个任务 -> @Scheduled;需重试/监控/分布式 -> SnailJob |
|
|
301
|
-
| 查看任务日志 | 本地 -> 应用日志;远程 -> SnailJob 控制台 -> 任务实例 -> 查看日志 |
|
|
258
|
+
// 2. @Scheduled 默认单线程,长任务阻塞其他任务
|
|
259
|
+
// 应配置线程池(见上文 SchedulingConfig)
|
|
302
260
|
|
|
303
|
-
|
|
261
|
+
// 3. 多实例部署未做互斥
|
|
262
|
+
// @Scheduled 每个实例都执行 -> 数据重复处理
|
|
263
|
+
// 应使用分布式锁(Redis / 数据库)或分布式调度平台
|
|
304
264
|
|
|
305
|
-
|
|
265
|
+
// 4. 任务非幂等
|
|
266
|
+
public void syncData() {
|
|
267
|
+
insertAll(fetchData()); // 重复执行会插入重复数据
|
|
268
|
+
}
|
|
269
|
+
// 应先检查是否已处理
|
|
306
270
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
| MapReduce | `ruoyi-modules/ruoyi-job/.../snailjob/TestMapReduceAnnotation1.java` |
|
|
271
|
+
// 5. 任务内开启大事务
|
|
272
|
+
@Transactional
|
|
273
|
+
public void processAllOrders() {
|
|
274
|
+
// 处理百万条数据在一个事务中 -> 内存溢出、锁超时
|
|
275
|
+
}
|
|
276
|
+
// 应分批处理,每批独立事务
|
|
277
|
+
```
|