2019-07-17 15:40

@阿龙 通过改写 Utf8Encoder 即可, 相当于是做一个新的 Utf8mb4Encoder,这部分接口是开放的,你也可以自己写一个, 然后自己通过我前面回复中的方法让其生效

jfinal 中绝大部分功能都是开放可扩展的

2019-07-17 15:38

@hxu 单步跟踪一下,找到原因

2019-07-17 15:33

@邓小杰 我前面强调过 "阻塞", 就是要提醒你用 new Thread 的方式

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:49

你先在浏览器中输入 reqUrl ,看看返回的是什么值, 或许服务端本身就反回了被截断的 JSP 页面

用好排除法

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:51

freemarker 貌似没有这个功能

2019-07-17 10:10

放在 afterJFianlStart() 或者 onStart() 中启动,新版本支持了 onStart() 方法回调

注意检查 netty 启动时是不是线程被阻塞了,造成 jfinal 启动过程止步于 onStart() 或 afterJFianlStart()

2019-07-17 10:08

@ThreeX 这个需求很多人都自己通过写一个 #define where() 函数来解决了,目前感觉需求并不是很强烈,先入备忘列表, 感谢你的反馈

2019-07-17 10:07

代码十分简洁,项目很有价值,对于整合常用第三方认证登录能节省大量时间,已 fork + star + 点赞 + 收藏