2019-07-17 15:32
@邓小杰 为啥不在 afterJFianlStart() 或 onStart() 中也同样用 new 一个新线程启动???
2019-07-17 12:18
@think-takn 升级一定要按文档来,跨多个版本升级,主要就是改点类名、方法名:
https://www.jfinal.com/doc/14-1
2019-07-17 11:45
@10000 status 的取值是 0 和 1,高并发的时候,不知道是谁将这个状态置为 1 的
虽然在事务中也可以保障这个值只能被某个线程抢到,但多一个 UUID 作为 locker 更加安全,因为事务依赖不少的条件,例如必须是 InnoDB 引擎,必须要开启事务
上面的方案用到 UUID 的 locker 相当于是双保险
2019-07-17 11:36
我给的方案,再补充完善一点点:
1:所有任务存放在一个中心的数据库中
2:任务的表名为 task,主要字段有: task(id, locker)
3:创建一个 TaskService 业务类
4:TaskService 中创建一个抢占 Task 的方法:
public class TaskService {
private static final Task dao = new Task().dao();
public Task getTask() {
String UUID = StrKit.getRandomUUID();
// 该 sql 只更新 locker 字段,也就是先只去抢占锁,而不能做其它任何事情
String sql = "update task set locker = ? where locker is NULL";
int n = Db.update(sql, UUID);
if ( n <= 0) {
return null;
};
// n 大于 0 表示 update 成功,但不能保证是当前线程抢到的该 task
// 通过前面生成的 UUID 去查询,查到了才能证明真的是当前线程抢占到了该 task
Task task = dao.findFirst("select * from task where locker = ?", UUID);
if (task != null) {
return task;
} else {
// 如果没有抢占到 task ,可以放弃则 return null, 也可以重试几次
return null;
}
}
}
2019-07-17 11:28
代码十分简洁,为了进一步提升可靠性与稳固性,有几个建议:
1:isRun()、lock()、unlock() 都用 Db.tx(...) 开启事务
2:lock() 方法的抢占需要引入一个 locker 字段,用于标识是否是当前线程抢到了,具体办法如下:
a:添加一个字段名叫 locker,默认值为 null
b:locker 字段的值通过 StrKit.getRandomUUID() 生成一个 UUID 值,由于 UUID 是全球唯一的,所以可以确保不可能重复。而当前你的 lock() 方法在并发高的时候,多个线程很可能都返回 true
c:改进 lock() 内部代码逻辑,大致如下:
String UUID = StrKit.getRandomUUID();
// 该 sql 只更新 locker 字段,也就是先只去抢占锁,而不能做其它任何事情
String sql = "update task locker = ? where locker is NULL";
int n = Db.update(sql, UUID);
// n 大于 0 表示 update 成功,但不能保证是当前线程抢到的该 task
if (n > 0) {
// 通过前面生成的 UUID 去查询,查到了才能证明真的是当前线程抢占到了该 task
Record task = Db.findFirst("select * from task where locker = ?", UUID);
if (task != null) {
这里再对 task 进行处理,例如更新 task 表的其它字段
return true; // lock() 方法不建议返回 boolean 值,而是返回 task 对象,便于上层调用者使用,用完以后还可以方便置回状态
}
}
上面的改进思路主要是用一个 locker 字段让多线程先抢占锁,抢到以后再进行后续的操作
注意,上述示例代码用到了 task 表以及 locker 字段,与你给出来的代码中用的 xx_job_status 并不相同
2019-07-17 10:56
@sdhery 将 spring 的代码拿过来用一用,配置点:
Configuration config = FreeMarkerRender.getConfiguration();
通过上面代码拿到 freemarker 的 config 对象以后,再进行各种配置即可
2019-07-17 10:08
@ThreeX 这个需求很多人都自己通过写一个 #define where() 函数来解决了,目前感觉需求并不是很强烈,先入备忘列表, 感谢你的反馈