0057、解决@Scheduled定时任务被阻塞的问题


问题描述:

@Scheduled注解定义的某个定时任务,每隔30秒通过dubbo接口去统一登录系统定时拉取用户信息,在测试环境定时任务运行正常,到生产环境后,从第二天开始定时任务无法被执行;
经分析后,定位问题原因为系统存在许多@Scheduled定义的其他定时任务,且生产环境数据量较大,每个定时任务任务执行时间边长,而@Scheduled是单线程的,因此导致后续的
其他定时任务被阻塞;
解决办法:拉取用户信息的定时任务不用@Scheduled注解定义,改用自定义线程池实现,因此不会再被阻塞

1、原拉取用户信息的定时任务方法代码如下:
package com.xxx.scheduled;

import com.alibaba.dubbo.config.annotation.Reference;
import com.xxx.ApplicationService;
import com.xxx.UserDTO;
import com.xxx.entity.SystemUser;
import com.xxx.SystemUserServcie;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Objects;

@Component
@Slf4j
@EnableScheduling
public class UserPullTask {


@Value("${config.application.code}")
private String code;

@Autowired
private SystemUserServcie systemUserServcie;

@Reference(version = "1.0")
private ApplicationService applicationService;

@Value("${default.role.id}")
private String roleId;
@Value("${default.user.id}")
private String userId;
@Scheduled(fixedDelay = 30000)
public void doTask() {
DateTime dateTime = DateTime.now().minusHours(1);
List<UserDTO> userList = applicationService.getUsersByModifyTimeAndAppCode(code, dateTime.toDate());
if (CollectionUtils.isEmpty(userList)) {
log.info("本次没有查询到新增用户,执行结束");
return;
}
log.info("获取到要同步的用户,本次同步数量:{}",userList.size());
for (UserDTO userDTO : userList) {
try {
SystemUser systemUser = convertSystemUser(userDTO);
SystemUser exitUser = systemUserServcie.isExitUser(userDTO.getName());
if(Objects.isNull(exitUser)){
systemUser.setIsDelete(0);
systemUser.setLevel(3);
systemUser.setRoleId(roleId);
systemUser.setIsOnline(0);
systemUser.setCreatAt(DateTime.now().toDate());
systemUser.setAddUser(userId);
systemUserServcie.insertUser(systemUser);
}else{
systemUser.setId(exitUser.getId());
systemUserServcie.updateByPrimaryKey(systemUser);
}

} catch (Exception e) {
log.info("{}同步失败",userDTO.getName());
}
}
}

private SystemUser convertSystemUser(UserDTO userDTO) {
SystemUser systemUser = new SystemUser();
systemUser.setUserName(userDTO.getNickName());
systemUser.setAccountNum(userDTO.getName());
systemUser.setPhone(userDTO.getPhone());
//状态定义相反
if(userDTO.getStatus() == 0){
systemUser.setState(1);
}else{
systemUser.setState(0);
}
systemUser.setLastLoginAt(userDTO.getGmtLastLogin());
return systemUser;
}
}

2、将上述标红的注解注释掉,并通过自定义线程池的方式来调用该获取用户信息的方法
package com.xxx.scheduled;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class UserPullApplicationRunner implements ApplicationRunner {
//项目启动结束后会立即执行该方法
@Autowired
private UserPullTask userPullTask;
@Override
public void run(ApplicationArguments args) throws Exception {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
userPullTask.doTask();
}
}, 1, 30, TimeUnit.SECONDS);//首次延迟1秒,之后每30秒执行一次
}
}
亲测,这种方式,即使其他用@Scheduled定义的方法sleep几秒钟,该方法也不会被阻塞。





免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM