使用场景
最近开发中遇到一个问题,一个老系统的页面打开非常慢,因为一个接口中处理了各种逻辑的统计,给前端返回一个结果,会导致查询速度比较慢,在不考虑拆分接口(这样前端也需要调整)做速度的优化,多线程是一种比较好的选择
Controller 层
@GetMapping("/listNumber")
public AjaxResult listNumber(FollowUpVO fuVO) {
SysUser user = SecurityUtils.getLoginUser().getUser();
Set<String> permissions = permissionService.getMenuPermission(user);
return AjaxResult.success(followUpService.listNumberNew(fuVO,permissions));
}
ServiceImpl层
@Override
public FollowUpStatistics listNumberNew(FollowUpVO fuVO,Set<String> permissions) throws BusinessException {
FollowUpStatistics fus = new FollowUpStatistics();
String numberShow = sysConfigService.selectConfigByKey("doctor_number_show");
if(StringUtils.isNotEmpty(numberShow)&&"2".equals(numberShow)){
return fus;
}
if (fuVO.getDeptIds() == null || fuVO.getDeptIds().size() == 0) {
List<Long> list = new ArrayList<>();
list.add(SecurityUtils.getLoginUser().getUser().getDeptId());
fuVO.setDeptIds(list);
}
//由于登录人可以右上角切换人员角色,因此这里需要重新查询一下
SysUser LoginUser = SecurityUtils.getLoginUser().getUser();
SysUser userInfo = sysUserMapper.selectUserById(LoginUser.getUserId());
String userType = userInfo.getUserType();
fuVO.setUserType(userType);
String userId ="";
String deptId ="";
if(userInfo.getUserId()!=null){
userId = userInfo.getUserId().toString();
}
if(userInfo.getDeptId()!=null){
deptId = userInfo.getDeptId().toString();
}
fuVO.setDeptId(Long.valueOf(deptId));
fuVO.getDeptIds().add(Long.valueOf(deptId));
fuVO.setCreateBy(SecurityUtils.getLoginUser().getUser().getUserId().toString());
followUpMapper.listWaitAssessNum(fuVO);
//使用多线程来查询组装数据
Future<Integer> waidtFollowUpNum = doctorWorkNumberService.listWaitFollowUpNum(fuVO,permissions);
Future<Integer> evaluationResult = doctorWorkNumberService.queryEvaluationResultCount(fuVO,userId,userType,deptId,permissions);
Future<Integer> listWaitGradedNum = doctorWorkNumberService.listWaitGradedNum(fuVO,permissions);
Future<Integer> waitGuideBaseNum = doctorWorkNumberService.listWaitGuideBaseNum(fuVO,permissions);
Future<Integer> waitAssessNum = doctorWorkNumberService.listWaitAssessNum(fuVO,permissions);
Future<Integer> notManageNum = doctorWorkNumberService.listNotManageNum(fuVO,permissions);
Future<Integer> healthNotManageNum = doctorWorkNumberService.listHealthNotManageNum(fuVO,permissions);
Future<Integer> reManageNum = doctorWorkNumberService.listReManageNum(fuVO,permissions);
Future<Integer> attentionNum = doctorWorkNumberService.listAttentionNum(fuVO,permissions);
Future<Integer> workDoneNum = doctorWorkNumberService.listWorkDoneNum(fuVO,permissions);
Future<Integer> waitSubsequentVisitNum = doctorWorkNumberService.listWaitSubsequentVisitNum(fuVO,permissions);
while ( !waidtFollowUpNum.isDone()
||!evaluationResult.isDone()
||!listWaitGradedNum.isDone()
||!waitGuideBaseNum.isDone()
||!waitAssessNum.isDone()
||!notManageNum.isDone()
||!healthNotManageNum.isDone()
||!reManageNum.isDone()
||!workDoneNum.isDone()
||!attentionNum.isDone()
||!waitSubsequentVisitNum.isDone()
){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
fus.setWaitFollowUpNum(waidtFollowUpNum.get());
fus.setFollowUpResultNum(evaluationResult.get());
fus.setWaitGradedNum(listWaitGradedNum.get());
fus.setWaitGuideBaseNum(waitGuideBaseNum.get());
fus.setWaitAssessNum(waitAssessNum.get());
fus.setNotManageNum(notManageNum.get());
fus.setHealthNotManageNum(healthNotManageNum.get());
fus.setReManageNum(reManageNum.get());
fus.setAttentionNum(attentionNum.get());
fus.setWaitSubsequentVisitNum(waitSubsequentVisitNum.get());
fus.setWorkDoneNum(workDoneNum.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return fus;
}
单独为里面每个小任务单独定义一个DoctorWorkNumberService 其中 @Async(“threadPoolTaskExecutor”)等注解 标明用哪个线程池配置
package com.xikang.doctorWorkstation.service.impl;
import com.xikang.common.enums.Permissions;
import com.xikang.common.utils.DateUtils;
import com.xikang.doctorWorkstation.domain.vo.FollowUpVO;
import com.xikang.doctorWorkstation.mapper.FollowUpMapper;
import com.xikang.doctorWorkstation.mapper.WorkDoneMapper;
import com.xikang.doctorWorkstation.mapper.WorkstationAttentionMapper;
import com.xikang.hypertension.domain.vo.AutoEvaluationPo;
import com.xikang.hypertension.mapper.GxyEvaluationBaseMapper;
import com.xikang.hypertension.mapper.SubsequentVisitMapper;
import com.xikang.system.mapper.DiseaseDepartmentRelationMapper;
import com.xikang.system.mapper.SysDeptMapper;
import com.xikang.system.mapper.SysUserMapper;
import com.xikang.system.service.ISysConfigService;
import com.xikang.visitInformation.mapper.VisitInformationMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.Future;
import static com.xikang.common.utils.DateUtils.getDateAgoOrAfterDays;
@Service
public class DoctorWorkNumberService {
@Autowired
private FollowUpMapper followUpMapper;
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private SysDeptMapper deptMapper;
@Autowired
private WorkstationAttentionMapper workstationAttentionMapper;
@Autowired
private WorkDoneMapper workDoneMapper;
@Resource
private SubsequentVisitMapper subsequentVisitMapper;
@Resource
private ISysConfigService sysConfigService;
@Resource
private VisitInformationMapper visitInformationMapper;
@Resource
private DiseaseDepartmentRelationMapper relationMapper;
@Resource
private GxyEvaluationBaseMapper gxyEvaluationBaseMapper;
public String PrintDate(Long startTime, Long endTime, String note){
Long tempTime = (endTime - startTime);
String result = note + (((tempTime/86400000)>0)?((tempTime/86400000)+"d"):"")+
((((tempTime/86400000)>0)||((tempTime%86400000/3600000)>0))?((tempTime%86400000/3600000)+"h"):(""))+
((((tempTime/3600000)>0)||((tempTime%3600000/60000)>0))?((tempTime%3600000/60000)+"m"):(""))+
((((tempTime/60000)>0)||((tempTime%60000/1000)>0))?((tempTime%60000/1000)+"s"):(""))+
((tempTime%1000)+"ms");
System.out.println(result);
return result;
}
/**
* @Description:待随访数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listWaitFollowUpNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOFOLLOWUP.getCode())){
result = followUpMapper.listWaitFollowUpNum(fuVO);
}
PrintDate(startTime, System.currentTimeMillis(), "待随访数量");
return new AsyncResult<>(result);
}
/**
* @Description:自动随访结果数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> queryEvaluationResultCount(FollowUpVO fuVO,String userId,String userType,String deptId,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.AUTOEVALUATIONRESULT.getCode())) {
AutoEvaluationPo autoEvaluationPo = new AutoEvaluationPo();
autoEvaluationPo.setEvaluationType("不良反应,控制不满意");
autoEvaluationPo.setEndEvaluationDate(DateUtils.getDate());
autoEvaluationPo.setStartEvaluationDate(DateUtils.parseDateToStr("yyyy-MM-dd",getDateAgoOrAfterDays(DateUtils.getNowDate(),-7)));
//健康管理师
if("1".equals(userType)){
autoEvaluationPo.setManageHealthId(userId);
//管理医生
}else if("2".equals(userType)){
autoEvaluationPo.setManageDoctorId(userId);
}
autoEvaluationPo.setManageDeptId(deptId);
result = gxyEvaluationBaseMapper.queryEvaluationResultCount(autoEvaluationPo);
}
PrintDate(startTime, System.currentTimeMillis(), "自动随访结果数量");
return new AsyncResult<>(result);
}
/**
* @Description:待分级数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listWaitGradedNum(FollowUpVO fuVO,Set<String> permissions){
Integer result = null;
Long startTime = System.currentTimeMillis();
if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOGRADED.getCode())) {
String mustGradeFlag = "是";
//随访之后必须分级参数
String mustGrade = sysConfigService.selectConfigByKey("must_grade");
if (mustGrade.equals(mustGradeFlag)) {
//待分级数量
result = followUpMapper.listWaitGradedNumAndEvaluation(fuVO);
} else {
//待分级数量
result = followUpMapper.listWaitGradedNum(fuVO);
}
}
PrintDate(startTime, System.currentTimeMillis(), "待分级数量");
return new AsyncResult<>(result);
}
/**
* @Description:待制定管理方案数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listWaitGuideBaseNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
String mustGuideFlag = "是";
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOMANAGEMENTPLAN.getCode())) {
//随访之后必须指定管理方案
String mustGuide = sysConfigService.selectConfigByKey("must_guide");
if (mustGuide.equals(mustGuideFlag)) {
//待制定管理方案数量
result = followUpMapper.listWaitGuideBaseNumAndEvaluation(fuVO);
} else {
//待制定管理方案数量
result = followUpMapper.listWaitGuideBaseNum(fuVO);
}
}
PrintDate(startTime, System.currentTimeMillis(), "待制定管理方案数量");
return new AsyncResult<>(result);
}
/**
* @Description:待管理评估数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listWaitAssessNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOMANAGEDASSESSMENT.getCode())) {
result = followUpMapper.listWaitAssessNum(fuVO);
}
PrintDate(startTime, System.currentTimeMillis(), "待管理评估数量");
return new AsyncResult<>(result);
}
/**
* @Description:未管理数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listNotManageNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.UNMANAGED.getCode())) {
result = followUpMapper.listNotManageNum(fuVO);
}
PrintDate(startTime, System.currentTimeMillis(), "未管理数量");
return new AsyncResult<>(result);
}
/**
* @Description:未管理数量-健康管理师
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listHealthNotManageNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.HEALTHUNMANAGED.getCode())){
result = followUpMapper.listHealthNotManageNum(fuVO);
}
PrintDate(startTime, System.currentTimeMillis(), "未管理数量-健康管理师");
return new AsyncResult<>(result);
}
/**
* @Description:失管复诊数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listReManageNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.REMANAGE.getCode())){
result = followUpMapper.listReManageNum(fuVO);
}
PrintDate(startTime, System.currentTimeMillis(), "失管复诊数量");
return new AsyncResult<>(result);
}
/**
* @Description:收藏数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listAttentionNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = workstationAttentionMapper.listNumber(fuVO);
PrintDate(startTime, System.currentTimeMillis(), "收藏数量");
return new AsyncResult<>(result);
}
/**
* @Description:已办数量
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listWorkDoneNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = workDoneMapper.listNumber(fuVO);
PrintDate(startTime, System.currentTimeMillis(), "已办数量");
return new AsyncResult<>(result);
}
/**
* @Description:待复诊数量 无健康管理师
*
* @author zhangzhiqing
* @date 2022/11/4 15:34
*/
@Async("threadPoolTaskExecutor")
public Future<Integer> listWaitSubsequentVisitNum(FollowUpVO fuVO,Set<String> permissions){
Long startTime = System.currentTimeMillis();
Integer result = null;
if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOFOLLOWUPVISIT.getCode())){
//待复诊数量 无健康管理师
fuVO.setManageHealth(null);
result = followUpMapper.listWaitSubsequentVisitNum(fuVO);
}
PrintDate(startTime, System.currentTimeMillis(), "待复诊数量");
return new AsyncResult<>(result);
}
}
线程池配置 ThreadPoolConfig
package com.xikang.framework.config;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.xikang.common.utils.Threads;
/**
* 线程池配置
*
* @author xikang
**/
@Configuration
public class ThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = 50;
// 最大可创建的线程数
private int maxPoolSize = 200;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
{
@Override
protected void afterExecute(Runnable r, Throwable t)
{
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
@Bean
public TaskExecutor customTaskExecutor() {
return buildSequentialExecutor("customTaskExecutor", 50000);
}
/**
* 创建一个可以顺序执行的执行器,通过设置线程池只有一个线程和队列的方式来保证顺序执行
* @param executorName
* @param queueSize
* @return
*/
private ThreadPoolTaskExecutor buildSequentialExecutor(String executorName, int queueSize) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(queueSize);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("spring-async-".concat(executorName));
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
return executor;
}
}
总结
经过优化以后,接口从超时变成 8s 也不快总之不报错了。也不用前端配合拆接口,很nice