diff --git a/src/main/java/net/geedge/asw/common/config/job/JobConfig.java b/src/main/java/net/geedge/asw/common/config/job/JobConfig.java new file mode 100644 index 0000000..daf9101 --- /dev/null +++ b/src/main/java/net/geedge/asw/common/config/job/JobConfig.java @@ -0,0 +1,83 @@ +package net.geedge.asw.common.config.job; + +import cn.hutool.log.Log; +import jakarta.annotation.PostConstruct; +import net.geedge.asw.common.util.T; +import net.geedge.asw.module.environment.job.JobEnvironmentStatusChecker; +import net.geedge.asw.module.sys.service.ISysConfigService; +import org.quartz.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import java.util.TimeZone; + +@Configuration +public class JobConfig { + + private static final Log log = Log.get(); + + private static final String JOB_NAME_PREFIX = "ASW_JOB"; + private static final String JOB_DEFAULT_GROUP = "SYSTEM"; + + /** + * get job key + * job_name=ASW_JOB_{name} + * group_name=SYSTEM + */ + private static JobKey getJobKey(String name) { + String jobName = T.StrUtil.concat(true, JOB_NAME_PREFIX, "_", name); + return new JobKey(jobName, JOB_DEFAULT_GROUP); + } + + @Autowired + private Scheduler scheduler; + + @Autowired + private Environment environment; + + @Autowired + private ISysConfigService sysConfigService; + + @Bean + public JobDetail JobEnvironmentStatusChecker() { + return JobBuilder.newJob(JobEnvironmentStatusChecker.class) + .withIdentity(getJobKey(JobEnvironmentStatusChecker.class.getSimpleName())) + .storeDurably() + .build(); + } + + @PostConstruct + public void init() throws SchedulerException { + // JobEnvironmentStatusChecker + createCronScheduleJob(JobEnvironmentStatusChecker(), environment.getProperty("asw.cron.JobEnvironmentStatusChecker", "0 0/1 * * * ? *")); + } + + /** + * create cron schedule job + * 先删后增 + */ + private void createCronScheduleJob(JobDetail jobDetail, String cronExpression) throws SchedulerException { + JobKey key = jobDetail.getKey(); + + boolean jobExists = scheduler.checkExists(key); + if (log.isDebugEnabled()) { + log.debug("[createCronScheduleJob] [key: {}] [exists: {}]", key.toString(), jobExists); + } + if (jobExists) { + scheduler.deleteJob(key); + log.debug("[createCronScheduleJob] [key: {}] [deleted]", key.toString()); + } + + String timezone = sysConfigService.getValue("timezone"); + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).inTimeZone(TimeZone.getTimeZone(timezone)); + + CronTrigger cronTrigger = TriggerBuilder.newTrigger() + .forJob(jobDetail) + .withSchedule(cronScheduleBuilder) + .build(); + scheduler.scheduleJob(jobDetail, cronTrigger); + } + +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/module/environment/job/JobEnvironmentStatusChecker.java b/src/main/java/net/geedge/asw/module/environment/job/JobEnvironmentStatusChecker.java new file mode 100644 index 0000000..f697837 --- /dev/null +++ b/src/main/java/net/geedge/asw/module/environment/job/JobEnvironmentStatusChecker.java @@ -0,0 +1,124 @@ +package net.geedge.asw.module.environment.job; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONObject; +import cn.hutool.log.Log; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import net.geedge.asw.common.util.RCode; +import net.geedge.asw.common.util.T; +import net.geedge.asw.module.environment.entity.EnvironmentEntity; +import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity; +import net.geedge.asw.module.environment.service.IEnvironmentService; +import net.geedge.asw.module.environment.service.IEnvironmentSessionService; +import org.apache.commons.lang3.time.StopWatch; +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@DisallowConcurrentExecution +public class JobEnvironmentStatusChecker extends QuartzJobBean { + + private static final Log log = Log.get(); + + @Autowired + private IEnvironmentService envService; + + @Autowired + private IEnvironmentSessionService envSessionService; + + @Override + protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + Thread.currentThread().setName("JobEnvironmentStatusChecker"); + + log.info("[JobEnvironmentStatusChecker] [begin]"); + StopWatch sw = new StopWatch(); + sw.start(); + try { + this.environmentStatusChecker(); + } catch (Exception e) { + log.error(e, "[JobEnvironmentStatusChecker] [error]"); + } finally { + sw.stop(); + } + log.info("[JobEnvironmentStatusChecker] [finshed] [Run Time: {}]", sw.toString()); + } + + /** + * environment status checker + *

+ * 1. update entity status、lastHealthCheck + * 2. close the offline env session + */ + @Transactional(rollbackFor = Exception.class) + public void environmentStatusChecker() { + List list = envService.list(); + for (EnvironmentEntity entity : list) { + Thread.ofVirtual().start(() -> { + String result = null; + try { + JSONObject paramJSONObject = entity.getParamJSONObject(); + String url = paramJSONObject.getStr("url"); + String token = paramJSONObject.getStr("token"); + + HttpRequest request = T.HttpUtil.createGet(String.format("%s/api/v1/env/status", url)); + request.header("Authorization", token); + + HttpResponse response = request.execute(); + log.info("[environmentStatusChecker] [env: {}] [status: {}]", entity.getId(), response.getStatus()); + + if (response.isOk()) { + result = response.body(); + } + } catch (RuntimeException e) { + log.error(e, "[environmentStatusChecker] [request api error] [env: {}]", entity.getId()); + } + + if (log.isDebugEnabled()) { + log.debug("[environmentStatusChecker] [env: {}] [result: {}]", entity.getId(), result); + } + + entity.setStatus(0); + entity.setLastHealthCheck(System.currentTimeMillis()); + + if (T.StrUtil.isNotEmpty(result)) { + try { + JSONObject jsonObject = T.JSONUtil.parseObj(result); + if (T.ObjectUtil.equal(RCode.SUCCESS.getCode(), jsonObject.getInt("code"))) { + JSONObject data = jsonObject.getJSONObject("data"); + String status = data.getStr("status"); + if (T.StrUtil.equals("online", status)) { + entity.setStatus(1); + } + } + } catch (Exception e) { + log.error(e, "[environmentStatusChecker] [parse result error] [env: {}]", entity.getId()); + } + } + + // update entity status、lastHealthCheck + envService.update(new LambdaUpdateWrapper() + .set(EnvironmentEntity::getStatus, entity.getStatus()) + .set(EnvironmentEntity::getLastHealthCheck, entity.getLastHealthCheck()) + .eq(EnvironmentEntity::getId, entity.getId()) + ); + + // close the offline env session + if (0 == entity.getStatus()) { + envSessionService.update(new LambdaUpdateWrapper() + .set(EnvironmentSessionEntity::getStatus, 2) + .set(EnvironmentSessionEntity::getEndTimestamp, System.currentTimeMillis()) + .eq(EnvironmentSessionEntity::getStatus, 1) + .eq(EnvironmentSessionEntity::getEnvId, entity.getId()) + ); + } + }); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java b/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java index a9241fe..64f6f03 100644 --- a/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java +++ b/src/main/java/net/geedge/asw/module/sys/service/ISysConfigService.java @@ -6,4 +6,8 @@ import net.geedge.asw.module.sys.entity.SysConfigEntity; public interface ISysConfigService extends IService { + String getValue(String key); + + String getValueOrDefault(String key, String defaultValue); + } diff --git a/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java b/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java index cd7a39d..75a5ea2 100644 --- a/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java +++ b/src/main/java/net/geedge/asw/module/sys/service/impl/SysConfigServiceImpl.java @@ -1,14 +1,26 @@ package net.geedge.asw.module.sys.service.impl; -import org.springframework.stereotype.Service; - +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; - +import net.geedge.asw.common.util.T; import net.geedge.asw.module.sys.dao.SysConfigDao; import net.geedge.asw.module.sys.entity.SysConfigEntity; import net.geedge.asw.module.sys.service.ISysConfigService; +import org.springframework.stereotype.Service; @Service public class SysConfigServiceImpl extends ServiceImpl implements ISysConfigService { + @Override + public String getValue(String key) { + SysConfigEntity config = this.getOne(new LambdaQueryWrapper().eq(SysConfigEntity::getParamKey, key)); + return config == null ? "" : config.getParamValue(); + } + + @Override + public String getValueOrDefault(String key, String defaultValue) { + String value = this.getValue(key); + return T.StrUtil.isEmpty(value) ? defaultValue : value; + } + }