33、生鲜电商平台-定时器,定时任务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 秒)。
最终:系统架构设计图:
最后:很多人说系统功能很强大很好,但是我的一种思维方式是不一定,强大固然好,但是你需要通过这么多的系统数据中来分析出问题的关键,而不是所谓的代码堆积。
你所需要的是思考,再思考,最终思考。
转载自-- https://www.cnblogs.com/jurendage/p/9153835.html
33、生鲜电商平台-定时器,定时任务quartz的设计与架构的更多相关文章
- Java生鲜电商平台-定时器,定时任务quartz的设计与架构
Java生鲜电商平台-定时器,定时任务quartz的设计与架构 说明:任何业务有时候需要系统在某个定点的时刻执行某些任务,比如:凌晨2点统计昨天的报表,早上6点抽取用户下单的佣金. 对于Java开源生 ...
- Java开源生鲜电商平台-财务系统模块的设计与架构(源码可下载)
Java开源生鲜电商平台-财务系统模块的设计与架构(源码可下载) 前言:任何一个平台也好,系统也好,挣钱养活团队这个是无可厚非的,那么对于一个生鲜B2B平台盈利模式( 查看:http://www.cn ...
- Java生鲜电商平台-会员积分系统的设计与架构
Java生鲜电商平台-会员积分系统的设计与架构 说明:互联网平台积分体系主要用于激励和回馈用户在平台的消费行为和活动行为,一个良好的积分体系可以很好的提升用户的粘性及活跃度. 一.互联网平台积分体系设 ...
- Java生鲜电商平台-RBAC系统权限的设计与架构
Java生鲜电商平台-RBAC系统权限的设计与架构 说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上 ...
- 26、生鲜电商平台-RBAC系统权限的设计与架构
说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上分析实现的策略以及基于目前两种比较流行的权限设计思想 ...
- Java生鲜电商平台-高并发的设计与架构
Java生鲜电商平台-高并发的设计与架构 说明:源码下载Java开源生鲜电商平台以及高并发的设计与架构文档 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中 ...
- Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案
Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 说明:Java生鲜电商平台中由于采用了微服务架构进行业务的处理,买家,卖家,配送,销售,供应商等进行服务化,但是不可避免存在 ...
- Java生鲜电商平台-商城优惠券设计要点复盘与总结
Java生鲜电商平台-商城优惠券设计要点复盘与总结 Java生鲜电商平台本文将从优惠券设计用户端,需求端,业务流程全方案解析优惠券设计方案 为什么要设计优惠券 设计优惠券的核心:拉新和促活 新产品上线 ...
- Java生鲜电商平台-如何使用微服务来架构生鲜电商B2B2C平台?
Java生鲜电商平台-如何使用微服务来架构生鲜电商B2B2C平台? 说明:随着互联网的日益普及,人们通过手机下单买菜的人越来越多,生鲜这个行业有两个显著的特点,一个是刚需.(你每天都要吃饭,都要吃菜) ...
随机推荐
- [转] 在Mac上搭建React Native开发环境
原文链接: http://blog.csdn.net/xiangzhihong8/article/details/53914336 概述 前面我们介绍过在window环境下开发React Native ...
- Storm bolt重复消费问题解决
最近碰到一个storm的坑, 两个bolt都需要从kafkaSpout中获取数据进行各自的业务处理, bolt1的处理是幂等的, bolt2的处理是非幂等的, 上线后发现非幂等的bolt处理总是会处理 ...
- 仿联想商城laravel实战---5、无刷新的增删改查(动态页面更新的三种方式(html))
仿联想商城laravel实战---5.无刷新的增删改查(动态页面更新的三种方式(html)) 一.总结 一句话总结: 直接js增加删除修改html 控制器直接返回处理好的页面 用双向绑定插件比如vue ...
- 代码题(14)— 合并有序链表、数组、合并K个排序链表
1.21. 合并两个有序链表 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出 ...
- centos 静态拨号
本人系统centos6.5:虚拟机太丑,固ssh. centos的与联网相关的配置文件在 $ /etc/sysconfig/network-scripts DHCP方式-联网 打开文件 $ vim / ...
- ffmpeg第三方库编译记录
最近在研究ffmpeg的编译,之前使用的Ubuntu,需要安装虚拟机,非常麻烦,所以后来改研究在Windows平台编译. 一开始遇到很多挫折,参考了网上很多的帖子,但要么不全要么内容已过期,经过我的反 ...
- bzoj 4817: [Sdoi2017]树点涂色 LCT+树链剖分+线段树
题目: Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. Bob可能会进 ...
- debian软件安装和卸载
用root身份执行如下命令: dpkg -l |grep "^rc"|awk '{print $2}' |xargs aptitude -y purge dpkg是Debian P ...
- Erlang pool management -- Emysql pool
从这篇开始,这一系列主要分析在开源社区中,Erlang 相关pool 的管理和使用. 在开源社区,Emysql 是Erlang 较为受欢迎的一个MySQL 驱动. Emysql 对pool 的管理和使 ...
- C++ ORM ODB入门
1.ORM ORM, Object Relational Mapping, 对象关系映射,用来将基于对象的数据结构映射到SQL的数据结构中.即将基于对象的数据映射到关系表中的字段,然后我们可以通过对象 ...