Commit f6609703 by Ren Ping

feat:改造初始化容量自动任务为分布式

1 parent 8e5f9f79
......@@ -142,6 +142,12 @@
<version>3.17</version>
</dependency>
<!-- quartz依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......
package com.dituhui.pea.order.quartz;
import com.dituhui.pea.order.common.DateUtils;
import com.dituhui.pea.order.scheduler.InitEngineerCapacityScheduler;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
/**
* 自动派工任务
*
* @author RenPing
* @date 2023/11/02
*/
@Component
@Slf4j
public class InitEngineerCapacityJob extends QuartzJobBean {
public static final String INIT_ENGINEER_JOB_PREFIX = "BOXI_INIT_ENGINEER_CAPACITY_";
@Value("${scheduler.init-engineer-capacity.day-offset-begin}")
private int dayOffsetBegin;
@Value("${scheduler.init-engineer-capacity.day-offset-end}")
private int dayOffsetEnd;
@Autowired
private InitEngineerCapacityScheduler initEngineerCapacityScheduler;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
String name = jobKey.getName();
String engineerCode = name.substring(INIT_ENGINEER_JOB_PREFIX.length());
long start = System.currentTimeMillis();
log.info(">>> 初始化开始,工程师(engineerCode:{})的容量将根据日历表的记录进行计算设置", engineerCode);
String bdate = DateUtils.formatDate(LocalDate.now().plusDays(dayOffsetBegin));
String edate = DateUtils.formatDate(LocalDate.now().plusDays(dayOffsetEnd));
initEngineerCapacityScheduler.initOneEngineerByDays(bdate, edate, engineerCode);
long end = System.currentTimeMillis();
log.info(">>> 初始化结束,工程师(engineerCode:{})的容量,耗时:{}", engineerCode, end - start);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
\ No newline at end of file
package com.dituhui.pea.order.quartz;
import cn.hutool.core.collection.CollectionUtil;
import com.dituhui.pea.order.dao.EngineerInfoDao;
import com.dituhui.pea.order.entity.EngineerInfoEntity;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
/**
* 手动触发定时任务
*
* @author RenPing
* @date 2023/11/01
*/
@Component
@Slf4j
public class InitEngineerCapacityListener implements ApplicationListener<ApplicationStartedEvent> {
@Resource
private Scheduler scheduler;
@Autowired
private EngineerInfoDao engineerInfoDao;
@Value("${scheduler.init-engineer-capacity.cron-expr}")
private String cronExpr;
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
List<String> allEngineerCodes = engineerInfoDao.findAll().stream().map(EngineerInfoEntity::getEngineerCode).collect(Collectors.toList());
allEngineerCodes.forEach(engineerCode -> {
String jobName = InitEngineerCapacityJob.INIT_ENGINEER_JOB_PREFIX + engineerCode;
JobDetail jobDetail = JobBuilder.newJob(InitEngineerCapacityJob.class)
.withIdentity(jobName, jobName)
.storeDurably()
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(jobName, jobName)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpr))
.build();
try {
scheduler.scheduleJob(jobDetail, CollectionUtil.newHashSet(trigger), true);
} catch (SchedulerException e) {
//log.error(e.getMessage(), e);
}
});
}
}
\ No newline at end of file
package com.dituhui.pea.order.quartz;
import org.quartz.Job;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* 自定义JobFactory,从Spring容器中拿单例Job
*
* @author RenPing
* @date 2023/11/02
*/
@Component
public class MyQuartzJobFactory extends AdaptableJobFactory implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Job job = applicationContext.getBean(bundle.getJobDetail().getJobClass());
return job;
}
}
package com.dituhui.pea.order.quartz;
import org.quartz.spi.JobFactory;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
@Configuration
public class QuartzConfig {
@Resource
private JobFactory jobFactory;
@Resource
private QuartzProperties quartzProperties;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
Properties properties = new Properties();
for (String key : quartzProperties.getProperties().keySet()) {
properties.put(key, quartzProperties.getProperties().get(key));
}
factory.setQuartzProperties(properties);
factory.setJobFactory(jobFactory);
return factory;
}
}
......@@ -227,6 +227,17 @@ public class InitEngineerCapacityScheduler {
log.info("====== 处理完毕 ======");
}
public void initOneEngineerByDays(String bdate, String edate, String engineerCode) {
log.info("==== initAllEngineerByDays, bdate[{}] edate[{}]", bdate, edate);
LocalDate currentDate = DateUtils.localDateFromStr(bdate);
LocalDate endDate = DateUtils.localDateFromStr(edate);
List<String> allEngineerCodes = engineerInfoDao.findAll().stream().map(EngineerInfoEntity::getEngineerCode).collect(Collectors.toList());
while (!currentDate.isAfter(endDate)) {
initOneEngineer(DateUtils.formatDate(currentDate), engineerCode);
currentDate = currentDate.plusDays(1);
}
}
private void initAllEngineerByDays(String bdate, String edate) {
log.info("==== initAllEngineerByDays, bdate[{}] edate[{}]", bdate, edate);
LocalDate currentDate = DateUtils.localDateFromStr(bdate);
......@@ -240,7 +251,7 @@ public class InitEngineerCapacityScheduler {
}
}
@Scheduled(cron = "${scheduler.init-engineer-capacity.cron-expr}")
//@Scheduled(cron = "${scheduler.init-engineer-capacity.cron-expr}")
public void run() {
log.info("开始初始化,所有工程师的容量将根据日历表的记录进行计算设置");
String bdate = DateUtils.formatDate(LocalDate.now().plusDays(dayOffsetBegin));
......
server:
port: 8013
spring:
application:
name: project-order
jackson:
default-property-inclusion: NON_NULL
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
cloud:
nacos:
discovery:
server-addr: nacos-server:8848
group: project
config:
server-addr: nacos-server:8848
group: project
file-extension: yaml
import-check:
# no config file
enabled: false
config:
import:
- optional:nacos:redis-config.yaml?group=project&refreshEnabled=true
- optional:nacos:bean-kafka-config.yaml?group=project&refreshEnabled=true
#config:
# import:
# - optional:nacos:project-order.yaml
# - optional:nacos:datasource-config.yaml
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
#url: jdbc:mysql://10.10.0.116:32306/saas_aftersale_test?serverTimezone=Asia/Shanghai
url: jdbc:mysql://localhost:32306/saas_aftersale_test?serverTimezone=Asia/Shanghai
username: boxi
password: boxi_dev_0725
type: com.alibaba.druid.pool.DruidDataSource
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
seata:
application-id: ${spring.application.name}
tx-service-group: ${spring.application.name}-group
service:
vgroup-mapping:
project-order-group: default
grouplist:
default: seata-server:8091
mybatis-plus:
global-config:
db-config:
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
sentry:
dsn: http://85293a9310fc4a8187422a7a257fc1ba@sentry.zjhuixinyun.com/8
# Set traces_sample_rate to 1.0 to capture 100%
# of transactions for performance monitoring.
# We recommend adjusting this value in production.
tracesSampleRate: 1.0
exception-resolver-order: -2147483647
SaaS:
url: https://pea-test.bshg.com.cn
ak: 64e1cde3f9144bfb850b7d37c51af559
scheduler:
init-engineer-capacity:
# 每天22点1次
#cron-expr: 0 0 22 * * ?
cron-expr: 0 32 * * * ?
day-offset-begin: 0
day-offset-end: 14
rewrite-force: true
calc-engineer-capacity:
# 8-20点,每30分钟1次
cron-expr: 0 */30 1-23 * * ?
day-offset-begin: 0
day-offset-end: 14
calc-org-capacity:
# 8-20点,每小时1次
cron-expr: 0 0 1-23 * * ?
day-offset-begin: 0
day-offset-end: 14
\ No newline at end of file
......@@ -34,6 +34,50 @@ spring:
username: boxi
password: boxi_dev_0725
type: com.alibaba.druid.pool.DruidDataSource
# quartz 配置
quartz:
# 应用关闭时,是否等待定时任务执行完成。默认为 false,建议设置为 true
wait-for-jobs-to-complete-on-shutdown: true
# 是否覆盖已有 Job 的配置,注意为false时,修改已存在的任务调度cron,周期不生效
overwrite-existing-jobs: true
#相关属性配置
properties:
org:
quartz:
scheduler:
#调度标识名 集群中每一个实例都必须使用相同的名称
instanceName: ProjectOrderScheduler
#ID设置为自动获取 每一个必须不同
instanceId: AUTO
makeSchedulerThreadDaemon: false
jobStore:
#class: org.quartz.impl.jdbcjobstore.JobStoreTX
#spring-boot-starter-quartz V2.5.7及以上
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#表的前缀,默认QRTZ_
tablePrefix: QRTZ_
#是否加入集群
isClustered: true
#调度实例失效的检查时间间隔
clusterCheckinInterval: 10000
useProperties: false
#设置调度引擎对触发器超时的忍耐时间 (单位毫秒),20分钟
misfireThreshold: 1200000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
# 指定在线程池里面创建的线程是否是守护线程
makeThreadsDaemons: false
#指定线程数,至少为1(无默认值,一般设置为1-100)
threadCount: 10
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#数据库方式
job-store-type: jdbc
#初始化表结构,初始化:always,再次重启改为 embedded
jdbc:
initialize-schema: embedded
seata:
application-id: ${spring.application.name}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!