Java生鲜电商平台-定时器,定时任务quartz的设计与架构

说明:任何业务有时候需要系统在某个定点的时刻执行某些任务,比如:凌晨2点统计昨天的报表,早上6点抽取用户下单的佣金。

对于Java开源生鲜电商平台而言,有定时推送客户备货,定时计算卖家今日的收益,定时提醒每日的提现金额等等

          对于Java定时器而言,我们采用spring+quartz来进行技术解决方案:

对于业务而言,需要满足以下几个方面:

1.  对定时任务需要可以手动启动,手动停止,手动删除等。

2. 对定时任务的结果需要记录,什么时候执行,执行的结果如何,是否存在异常,是否有异常的记录。

3. 对定时器的错误,如果是级别很重要,是否有短信提醒来让运营人员或者技术人员手工处理呢?

类似这样:

为了满足业务的需求,根据quartz的技术选型,设计一下基础架构:

1. 任务记录信息表:

CREATE TABLE `t_job` (
`JOB_ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务id',
`BEAN_NAME` varchar(100) NOT NULL COMMENT 'spring bean名称',
`METHOD_NAME` varchar(100) NOT NULL COMMENT '方法名',
`PARAMS` varchar(200) DEFAULT NULL COMMENT '参数',
`CRON_EXPRESSION` varchar(100) NOT NULL COMMENT 'cron表达式',
`STATUS` char(2) NOT NULL COMMENT '任务状态 0:正常 1:暂停',
`REMARK` varchar(200) DEFAULT NULL COMMENT '备注',
`CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`JOB_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

2. 任务操作日志记录表:

CREATE TABLE `t_job_log` (
`LOG_ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务日志id',
`JOB_ID` bigint(20) NOT NULL COMMENT '任务id',
`BEAN_NAME` varchar(100) NOT NULL COMMENT 'spring bean名称',
`METHOD_NAME` varchar(100) NOT NULL COMMENT '方法名',
`PARAMS` varchar(200) DEFAULT NULL COMMENT '参数',
`STATUS` char(2) NOT NULL COMMENT '任务状态 0:成功 1:失败',
`ERROR` text COMMENT '失败信息',
`TIMES` decimal(11,0) DEFAULT NULL COMMENT '耗时(单位:毫秒)',
`CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`LOG_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2476 DEFAULT CHARSET=utf8;

说明:整个业务很简单,整个表设计与架构也很简单。

最终运营截图如下:

相关的系统设置与配置说明:

相关的核心代码如下:(贴些核心的,不算很核心的,大家可以去我github下面下载即可)

/**
* 定时任务
*
* @author Administrator
*
*/
public class ScheduleJob extends QuartzJobBean {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private ExecutorService service = Executors.newSingleThreadExecutor(); @Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
Job scheduleJob = (Job) context.getMergedJobDataMap().get(Job.JOB_PARAM_KEY); // 获取spring bean
JobLogService scheduleJobLogService = (JobLogService) SpringContextUtils.getBean("JobLogService"); JobLog log = new JobLog();
log.setJobId(scheduleJob.getJobId());
log.setBeanName(scheduleJob.getBeanName());
log.setMethodName(scheduleJob.getMethodName());
log.setParams(scheduleJob.getParams());
log.setCreateTime(new Date()); long startTime = System.currentTimeMillis(); try {
// 执行任务
logger.info("任务准备执行,任务ID:" + scheduleJob.getJobId());
ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(),
scheduleJob.getParams());
Future<?> future = service.submit(task);
future.get();
long times = System.currentTimeMillis() - startTime;
log.setTimes(times);
// 任务状态 0:成功 1:失败
log.setStatus("0"); logger.info("任务执行完毕,任务ID:" + scheduleJob.getJobId() + " 总共耗时:" + times + "毫秒");
} catch (Exception e) {
logger.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
long times = System.currentTimeMillis() - startTime;
log.setTimes(times);
// 任务状态 0:成功 1:失败
log.setStatus("1");
log.setError(StringUtils.substring(e.toString(), 0, 2000));
} finally {
scheduleJobLogService.saveJobLog(log);
}
}
}
/**
* 执行定时任务
*
* @author Administrator
*
*/
public class ScheduleRunnable implements Runnable {
private Object target;
private Method method;
private String params; public ScheduleRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = SpringContextUtils.getBean(beanName);
this.params = params; if (StringUtils.isNotBlank(params)) {
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
this.method = target.getClass().getDeclaredMethod(methodName);
}
} @Override
public void run() {
try {
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotBlank(params)) {
method.invoke(target, params);
} else {
method.invoke(target);
}
} catch (Exception e) {
e.printStackTrace();
}
} }
/**
* 执行定时任务
*
* @author Administrator
*
*/
public class ScheduleRunnable implements Runnable {
private Object target;
private Method method;
private String params; public ScheduleRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = SpringContextUtils.getBean(beanName);
this.params = params; if (StringUtils.isNotBlank(params)) {
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
this.method = target.getClass().getDeclaredMethod(methodName);
}
} @Override
public void run() {
try {
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotBlank(params)) {
method.invoke(target, params);
} else {
method.invoke(target);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

POM文件

<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>

org.quartz.scheduler.instanceName属性可为任何值,用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同。

org.quartz.scheduler.instanceId 属性为 AUTO即可,基于主机名和时间戳来产生实例 ID。

org.quartz.jobStore.class属性为 JobStoreTX,将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。

org.quartz.jobStore.isClustered 属性为 true,你就告诉了 Scheduler 实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。

org.quartz.jobStore.clusterCheckinInterval 属性定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。

最终:系统架构设计图:

最后:很多人说系统功能很强大很好,但是我的一种思维方式是不一定,强大固然好,但是你需要通过这么多的系统数据中来分析出问题的关键,而不是所谓的代码堆积。

           你所需要的是思考,再思考,最终思考。

Java生鲜电商平台-定时器,定时任务quartz的设计与架构的更多相关文章

  1. 33、生鲜电商平台-定时器,定时任务quartz的设计与架构

    说明:任何业务有时候需要系统在某个定点的时刻执行某些任务,比如:凌晨2点统计昨天的报表,早上6点抽取用户下单的佣金. 对于Java开源生鲜电商平台而言,有定时推送客户备货,定时计算卖家今日的收益,定时 ...

  2. Java生鲜电商平台-会员积分系统的设计与架构

    Java生鲜电商平台-会员积分系统的设计与架构 说明:互联网平台积分体系主要用于激励和回馈用户在平台的消费行为和活动行为,一个良好的积分体系可以很好的提升用户的粘性及活跃度. 一.互联网平台积分体系设 ...

  3. Java生鲜电商平台-RBAC系统权限的设计与架构

    Java生鲜电商平台-RBAC系统权限的设计与架构 说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上 ...

  4. Java开源生鲜电商平台-财务系统模块的设计与架构(源码可下载)

    Java开源生鲜电商平台-财务系统模块的设计与架构(源码可下载) 前言:任何一个平台也好,系统也好,挣钱养活团队这个是无可厚非的,那么对于一个生鲜B2B平台盈利模式( 查看:http://www.cn ...

  5. 26、生鲜电商平台-RBAC系统权限的设计与架构

    说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上分析实现的策略以及基于目前两种比较流行的权限设计思想 ...

  6. Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案

    Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 说明:Java生鲜电商平台中由于采用了微服务架构进行业务的处理,买家,卖家,配送,销售,供应商等进行服务化,但是不可避免存在 ...

  7. Java生鲜电商平台-商城优惠券设计要点复盘与总结

    Java生鲜电商平台-商城优惠券设计要点复盘与总结 Java生鲜电商平台本文将从优惠券设计用户端,需求端,业务流程全方案解析优惠券设计方案 为什么要设计优惠券 设计优惠券的核心:拉新和促活 新产品上线 ...

  8. Java生鲜电商平台-高并发的设计与架构

    Java生鲜电商平台-高并发的设计与架构 说明:源码下载Java开源生鲜电商平台以及高并发的设计与架构文档 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中 ...

  9. Java生鲜电商平台-生鲜系统中微服务架构设计与分析实战

    Java生鲜电商平台-生鲜系统中微服务架构设计与分析实战 说明: Java生鲜系统中微服务的拆分应该如何架构设计与分析呢?以下是我的实战中的设计与经验分析. 目录 1. 微服务简介2. 当前现状3. ...

随机推荐

  1. Java 构造结构私有化

    Java 构造结构私有化 单例设计模式:(Singleton) 在一般情况下,一个类只有通过产生对象之后才可以操作这个类. class Singleton { public void print() ...

  2. Android多module下重复jar包问题

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/166 Android多module下重复jar包问题 An ...

  3. MQTT linux centOS7 部署

    系统版本centos7 X64 1.设置保存安装包路径 # cd /usr/local/src 2.开始下载源包 官网资源: https://mosquitto.org/files/source/ # ...

  4. 自动化测试中执行JS脚本方法封装

    执行JS脚本方法封装: class JavaScript(Base): def execute_javascript(self, js): """执行 JavaScrip ...

  5. 用背包问题思路解决 322. Coin Change(完全背包)

    首先需要明白 0-1 背包问题中的放置表格,见 “玩转算法面试 从真题到思维全面提升算法思维” 9-5 节,本题思路类似表格纵向为:只考虑第 [0 …,… index] 种硬币(物品)表格横向为:需要 ...

  6. Java代码精进

    一.代码命名规范 驼峰命名法(CamelCase) Google 定义了以下的转换规则: 从正常的表达形式开始,把短语转换成 ASCII 码,并且移除单引号. 例如,“Müller’s algorit ...

  7. java加密类

    java.security.KeyStore KeyStore ks = KeyStore.getInstance(type); KeyStore ks = KeyStore.getInstance( ...

  8. CF1225C p-binary

    CF1225C p-binary 洛谷评测传送门 题目描述 Vasya will fancy any number as long as it is an integer power of two. ...

  9. babel速览

    babel babel初衷 在es6出现之后,由于此版本的巨大改变,给人们带来了很多革命性的技术支持,但是当时很多浏览器对es6支持有限,babel就是为了达到写最新的语法,可以在任意浏览器运行而出现 ...

  10. 小白专场-是否同一颗二叉搜索树-c语言实现

    目录 一.题意理解 二.求解思路 三.搜索树表示 程序框架搭建 3.1 如何建搜索树 3.2 如何判别 3.3 清空树 更新.更全的<数据结构与算法>的更新网站,更有python.go.人 ...