- 简介
在日常开发中,如果一个链接执行的时间过长,前端等不到后端返回数据会报错,或者用户一直等待任务执行,影响用户体验。
很多时候采用的方法是,前端将链接超时时间设长,但这有时并不优雅。
在这里采用redis+多线程,用户可以实时查看当前任务进度,还不必去过多等待任务执行。
- 项目流程
- 技术概要
此项目是在ruoyi架构上进行的(没有单独配置redis)
为了与其他功能解耦,将此功能单独设置成一个模块,通过用户调用,实现流程。
通过java代理对用户要执行的代码进行处理,使用户只需要处理业务代码,简化用户操作(核心)。
//给run方法添加代理
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行任务,报错
Object invoke = null;
try {
invoke = method.invoke(runnable, args);
updateTaskState(key,Entity.builder().state("200").msg(STATE_MSG_200).build());
} catch (Exception e) {
updateTaskState(key,Entity.builder().state("400").msg(STATE_MSG_400).build());
}
return invoke;
}
};
- 代码
用户调用:
通过简单的代码,就能完成对控制器的修改,并且不入侵原先的代码。如果觉得redisTaskServer.getTaskState(key)获取后判断,还是冗余,可以把这段代码放入到createTask方法中通过抛出异常实现
String key = "user:12fla:001"; //key建议使用用户id加方法的标示
Entity method = redisTaskServer.getTaskState(key);
if(method != null){
return "上一个任务没有执行完:"+method.getMsg();
}
redisTaskServer.createTask(key, new Runnable() {
@Override
public void run() {
//要执行的业务端代码
}
});
return "已添加任务";
功能:
##########对外开放接口类
public interface RedisTaskServer {
//查询任务结果
public Entity getTaskState(String key);
//删除任务结果
public int deleteTaskKey(List<String> keys);
//修改任务结果
public Entity updateTaskState(String key,Entity entity);
//简化一套 用户只要发送过来key与要执行人任务就可以。
//自动生成任务信息共cglib动态生成结果
public boolean createTask(String key,Runnable runnable);
public String createKey(String arg);
}
#################对外开放实体类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Entity {
private String state;
private String msg;
}
################接口实现类
@Service
public class RedisTaskServerImpl implements RedisTaskServer {
private final String KEY = "redisTask";
private final String STATE_MSG_0 = "任务正在运行";
private final String STATE_MSG_200 = "任务执行完毕";
private final String STATE_MSG_400 = "任务执行失败";
@Autowired
private RedisCache redisCache;
@Override
public Entity getTaskState(String key) {
String cacheMapValue = redisCache.getCacheMapValue(KEY, key);
if(!cacheMapValue.isEmpty()){
Entity entity = JSONUtil.toBean(cacheMapValue, Entity.class);
return entity;
}
return null;
}
@Override
public int deleteTaskKey(List<String> keys) {
Map<String, Object> cacheMap = redisCache.getCacheMap(KEY);
if(cacheMap == null){return 0;}
int result = 0;
for (String key : keys) {
if(cacheMap.remove(key)!=null){
result++;
}
}
return result;
}
@Override
public Entity updateTaskState(String key, Entity entity) {
redisCache.setCacheMapValue(KEY,key,JSONUtil.parse(entity).toString());
return entity;
}
@Override
public boolean createTask(String key, Runnable runnable) {
//不负责结果key冲突
updateTaskState(key,Entity.builder().state("0").msg(STATE_MSG_0).build());
//给run方法添加代理
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行任务,报错
Object invoke = null;
try {
invoke = method.invoke(runnable, args);
updateTaskState(key,Entity.builder().state("200").msg(STATE_MSG_200).build());
} catch (Exception e) {
updateTaskState(key,Entity.builder().state("400").msg(STATE_MSG_400).build());
}
return invoke;
}
};
Runnable o = (Runnable) Proxy.newProxyInstance(RedisTaskServerImpl.class.getClassLoader(),runnable.getClass().getInterfaces(),invocationHandler);
new Thread(o).start();
return true;
}
@Override
public String createKey(String arg) {
Long deptId = SecurityUtils.getLoginUser().getUser().getDeptId();
String key = "user:"+deptId+"fla:"+arg;
return key;
}
}