All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lww.sparrow.task.manager.JobScanService Maven / Gradle / Ivy

The newest version!
package com.lww.sparrow.task.manager;

import com.alibaba.fastjson.JSON;
import com.lww.sparrow.task.dal.JobInfoMapper;
import com.lww.sparrow.task.domain.bean.JobQueueBean;
import com.lww.sparrow.task.domain.entity.JobInfo;
import com.lww.sparrow.task.domain.util.OrikaBeanUtil;
import com.lww.sparrow.task.service.job.JobInfoService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

@Component("jobScanService")
public class JobScanService {
    private static final Logger logger = LoggerFactory.getLogger(JobScanService.class);

    private ReentrantLock LOCK = new ReentrantLock();
    private Condition TIME_ARRIVED = LOCK.newCondition();

    @Resource
    private JobInfoMapper jobInfoMapper;
    @Resource
    private JobInfoService jobInfoService;
    @Resource
    private JobQueue jobQueue;
    @Resource
    private OrikaBeanUtil orikaBeanUtil;

    private AtomicBoolean isRunning = new AtomicBoolean(true);
    /**
     * 时间扫描范围 记录上次扫描时间 使用 startDate < X <= now()
     */
    private AtomicLong startDate = new AtomicLong(0);

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 如果超过 2 秒那么线程就sleep
     */
    private long scanCanSleepTime = 2000;
    /**
     * 批量大小
     */
    private static final int BATCH_SIZE = 100;

    /**
     * 如果扫描没有任务那么sleep到 2028-08-01 00:00:00(1848672000) 当有新任务进来是会唤醒的
     */
    private long maxDate = 1848672000000L;
    /**
     * spring加载该bean时,自动执行该方法;
     * 目前是单线程版不考虑 多线程间重复扫描
     */
    @PostConstruct
    public void start() {
        new Thread(this::scanThreadRun).start();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            logger.info(".................. jobScan stop ..................");
            isRunning.set(false);
            LOCK.lock();
            try {
                TIME_ARRIVED.signalAll();
            } finally {
                LOCK.unlock();
            }
        }));
    }
    private void scanThreadRun() {
        logger.info(".................. jobScan start ..................");
        while (isRunning.get()) {
            Date endDate = new Date();
            while (true) {
                List jobInfos = jobInfoService.scanJobAndUpdateNextTime(new Date(startDate.get()), endDate, BATCH_SIZE);
                if (CollectionUtils.isEmpty(jobInfos)) {
                    break;
                }
                List jobQueueBeans = orikaBeanUtil.convertList(jobInfos, JobQueueBean.class);
                Date scanDate = new Date();
                jobQueueBeans.forEach(jobQueueBean -> {
                    jobQueueBean.setScanDate(scanDate);
                    this.addJob(jobQueueBean, 0);
                });
                logger.info(".......... jobScan startDate:{} endDate:{} size:{}..........", DATE_FORMAT.format(startDate), DATE_FORMAT.format(endDate), jobInfos.size());
            }
            startDate.set(endDate.getTime());
            // 获取下次执行时间 如果太长则sleep
            threadWait();
        }
    }

    private void threadWait() {
        Date nextExecuteTime = jobInfoMapper.findNextExecuteTime(new Date(startDate.get()));
        if (nextExecuteTime == null || nextExecuteTime.before(new Date(startDate.get()))) {
            logger.error("jobScan no job need execute");
            nextExecuteTime = new Date(maxDate);
        }
        threadWait(nextExecuteTime);
    }

    private boolean threadWait(Date nextExecuteTime) {
        Throwable throwable = null;
        try {
            LOCK.lockInterruptibly();
            try {
                if ((nextExecuteTime.getTime() - startDate.get()) > scanCanSleepTime) {
                    TIME_ARRIVED.awaitUntil(nextExecuteTime);
                } else {
//                    TIME_ARRIVED.await(scanCanSleepTime, TimeUnit.MILLISECONDS);
                }
            } catch (Throwable e) {
                throwable = e;
            }
        } catch (Throwable e) {
            throwable = e;
        } finally {
            LOCK.unlock();
        }
        if (throwable != null) {
            logger.error("threadWait exception:", throwable);
            return false;
        }
        return true;
    }

    /**
     * 添加 job 到队列中
     * @param jobQueueBean
     * @return
     */
    private static Integer MAX_ADD_QUEUE_RETRY_COUNT = 5;
    private boolean addJob(JobQueueBean jobQueueBean, int count) {
        jobQueueBean.setAddqueueDate(new Date());
        boolean success = jobQueue.offer(jobQueueBean);
        if (!success ) {
            if (count < MAX_ADD_QUEUE_RETRY_COUNT) {
                return addJob(jobQueueBean, count++);
            } else {
                logger.error("jobQueueBean:{} retry {} times, still can`t add in queues", JSON.toJSONString(jobQueueBean), count);
            }
        }
        return success;
    }
    void wakeup() {
        LOCK.lock();
        try {
            TIME_ARRIVED.signal();
        } finally {
            LOCK.unlock();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy