前言
两个月前,领导丢给我一个需求:做一个定时任务管理系统。
我当时的反应就两个字:不会。
这个技术栈我之前完全没碰过。按老路子,我得先花一周看文档、啃教程,然后写代码,然后翻车,然后重写……
但我没时间。项目排期就给了三周。
后来我用了一套“偷懒”的方法:让AI帮我跑通入门项目,我再迁移到正式代码里。最后一周交付。
这篇就是复盘我是怎么干的。如果你也遇到“新技术+短工期”的困境,可以参考。上篇文章:不用XXL-JOB!我花2个月自研了一套分布式定时任务系统
搞个图,AI提效实战流程:
一、核心策略:先做入门项目
当接到不熟悉的技术需求时,不要直接在公司项目上动手。正确的做法是:
先创建一个独立的入门项目,只实现核心需求,跑通为止。
这个道理我是吃过亏才懂的。之前有个需求,我直接在公司项目里试,改了一周发现方向错了,代码已经乱得没法回滚,最后加班两周重写。
为什么这样做?
- 降低试错成本:入门项目可以随意修改、删除,不用担心影响现有代码
- 快速理解原理:通过实际操作,弄清楚技术实现的基本原理和底层逻辑
- 积累可复用经验:入门项目中的实现思路可以直接迁移到正式项目
二、AI辅助:让DeepSeek帮你生成高质量提示词
在入门项目阶段,可以这样利用AI:
第一步:和DeepSeek对话,说明核心需求
例如,我最近遇到一个需求:实现一个定时任务管理模块。我完全不熟悉这个技术栈,我一开始跟DeepSeek说的其实很乱,大概是:“我想做个定时任务的东西,能增删改查那种,最好可以动态调……” DeepSeek反问我好几个问题,我才慢慢把需求理清楚。 最后整理出来的提示词是下面这样的:
我需要实现一个定时任务管理系统,核心功能包括:动态添加任务、暂停任务、恢复任务、修改任务执行时间。请帮我生成一套可以直接交给Claude Code生成代码的提示词。
第二步:让DeepSeek输出结构化的提示词
DeepSeek会帮你整理出一套完整的提示词,包括:
- 技术栈要求
- 核心功能列表
- 代码结构设计
- 关键实现细节
DeepSeek生成的提示词示例如下(篇幅有限省略了部分提示词,关注公众号获取完整提示词):
请使用 SpringBoot 3 + Java 17 + PostgreSQL 创建一个简单的定时任务调度系统。
项目结构:
- scheduler-center(调度中心,端口8080)
- biz-module(业务模块,端口8081)
调度中心功能:
- 使用 ThreadPoolTaskScheduler + CronTrigger 实现 cron 定时调度
- 使用 RestTemplate 按 cron 触发执行业务模块的任务
- 任务执行后回调结果给业务模块的 /api/biz/callback 接口
- 任务信息持久化到 PostgreSQL(task_info 表),启动时从数据库加载所有启用任务
技术要求:
- 每个服务有独立的 application.yml
- 使用 JdbcTemplate 或 Spring Data JPA 操作 PostgreSQL
第三步:将提示词交给Claude Code生成代码
这样得到的代码通常是可运行的、结构清晰的。你只需要专注于:
- 理解代码逻辑
- 运行测试
- 调试修复问题
Claude Code生成的代码结构:
三、实战案例:定时任务管理项目的迁移
下面分享我的一个实际案例:将AI生成的定时任务管理项目顺利迁移到公司项目上。
入门项目阶段
我用上述方法跑通了一个定时任务管理项目。这个项目的核心实现包括:
- 业务模块启动 → 扫描注解 → 注册到调度中心
- 调度中心定时扫描 → 找到到期任务 → 调用业务模块
- 业务模块反射执行 → 返回结果
- 调度中心更新记录 → 计算下次执行时间
跑通后,我理解了关键点:
- 调度器和业务模块的交互流程
- 业务模块如何通过反射执行任务的?
- 定时任务的数据库如何设计的?
ps:跑通的过程没那么顺利。我卡得最久的一个问题是:业务模块怎么把自己的任务注册到调度中心?
Claude Code生成的代码用的是“启动时扫描注解+自动注册”,但我没理解这个机制,以为要手动写注册代码。折腾了两个小时,最后发现是我自己看漏了——调度中心启动时会主动拉取业务模块的任务列表。
但正是这个“卡住→排查→理解”的过程,让我把交互流程彻底搞懂了。如果直接抄代码,我到现在可能还不知道它是怎么工作的。
迁移到公司项目
有了入门项目的基础,迁移过程非常顺利:
- 复用核心代码:将调度器管理类直接复制过来;形成定时任务组件包,供公司业务模块进行接入
- 适配现有框架:将数据库操作适配到公司现有的ORM
- 添加业务逻辑:将公司的业务模块,接入定时任务组件包中
整个过程只用了一周时间,如果从零开始,可能需要一两个月。
四、进阶需求:分布式调度的简单实现
上面说的方案,调度中心只有一个实例。如果部署多个实例,会出问题:同一个任务到点时,可能两个调度中心都会触发,导致重复执行。
我当时也想到这个坑了。解决方案其实很简单:用数据库行锁。 这也就是粉丝留言提到的:“调度中心单点问题怎么解决?
但是,当调度中心模块需要支持分布式部署时,会遇到一个经典问题:如何保证同一个任务不会被多个调度中心同时调度?
问题分析
假设有两个调度中心节点,一个业务模块节点,如果不加控制,一个定时任务到点时,可能两个调度中心都会执行任务扫描,造成任务的重复执行。所以需要添加一个分布式锁方法,确保在多个调度中心节点中只有一个节点能执行任务扫描。
当有多个调度中心时,调度中心分布式部署架构图:
分布式部署架构
```
┌──────────────────────────────────────────────────────────────┐
│ 负载均衡器 │
│ (Nginx/HAProxy) │
└──────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ 调度中心集群 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Scheduler-1 │ │ Scheduler-2 │ │ Scheduler-3 │ │
│ │ :8080 │ │ :8080 │ │ :8080 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ PostgreSQL主从集群 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Master │◄──────────────────►│ Slave-1 │ │
│ │ :5432 │ 同步复制 │ :5433 │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 业务模块集群 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Business-1 │ │ Business-2 │ │ Business-3 │ │
│ │ :8081 │ │ :8081 │ │ :8081 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
```
简单可靠的解决方案:数据库行锁
不需要引入Redis、ZooKeeper等中间件,仅用数据库就能实现。
核心思路:
- 新增任务锁表scheduler_lock,里面只有一个字段:
lock_name(锁名称) - 调度器执行任务前,先尝试通过锁"抢占"任务
- 抢占成功的实例执行任务,失败的跳过
CREATE TABLE public.scheduler_lock (
lock_name varchar(50) NOT NULL,
CONSTRAINT xxl_job_lock_pkey PRIMARY KEY (lock_name)
);
INSERT INTO public.scheduler_lock (lock_name) VALUES('schedule_lock');
实现代码示例:
@Scheduled(fixedDelay = 1000)
public void scanAndExecuteTasks() {
Connection conn = acquireDistributedLock();
if (conn == null) {
logger.debug("获取锁失败,跳过本次扫描");
return;
}
try {
// 原有扫描逻辑
doScanAndExecuteTasks();
conn.commit();
} catch (Exception e) {
rollback(conn);
} finally {
close(conn);
}
}
// 添加一个分布式锁方法,确保在多个调度中心实例中只有一个实例能执行任务扫描
private Connection acquireDistributedLock() {
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
// 设置锁超时时间(毫秒)
try (Statement stmt = conn.createStatement()) {
stmt.execute(String.format("SET LOCAL lock_timeout = '%dms'", lockTimeout));
}
// 尝试获取锁并验证记录存在
try (PreparedStatement pstmt = conn.prepareStatement(
"SELECT lock_name FROM scheduler_lock WHERE lock_name = 'schedule_lock' FOR UPDATE NOWAIT"
)) {
ResultSet rs = pstmt.executeQuery();
if (!rs.next()) {
logger.error("锁记录不存在,请执行: INSERT INTO scheduler_lock (lock_name) VALUES ('schedule_lock')");
conn.rollback();
return null;
}
}
logger.debug("成功获取分布式锁");
return conn;
} catch (SQLException e) {
if (conn != null) {
try { conn.rollback(); } catch (SQLException ignored) {}
try { conn.close(); } catch (SQLException ignored) {}
}
logger.error("获取分布式锁异常", e);
return null;
}
}
核心要点:
- 使用数据库的行级锁,确保在多个调度中心实例中只有一个实例能执行任务扫描
- 只有抢占成功的实例才执行任务
这个方案的优点:
- 无额外依赖:不需要引入中间件
- 实现简单:一条UPDATE语句搞定
- 足够可靠:数据库的行锁机制保证了原子性
五、总结
总结就一句:先让AI帮你跑通最小可行性,再自己动手迁移到正式项目。。搞个图总结一下:
flowchart LR
A[接到新技术需求] --> B[和DeepSeek对话]
B --> C[生成Claude Code提示词]
C --> D[跑通入门项目]
D --> E[理解核心原理]
E --> F[迁移到公司项目]
F --> G[完成需求]
这个方法我试了三次,都管用。你可以拿你手头最头疼的那个需求试试——跟DeepSeek聊十分钟,看它能给你什么。
(完整的调度中心高可用代码,公众号回复「定时任务」拿)
📍 关于我
我是麻雀,6年央国企实战派,专注 AI提效实战 + 架构设计。每周一篇硬核技术文。
公众号/B站:麻雀聊技术(关注领配套资料)
如果觉得有用,欢迎点赞、评论、转发。有问题评论区见。