Java应用集群下的定时任务处理方案(mysql)
今天来说一个Java多机部署下定时任务的处理方案。
需求: 有两台服务器同时部署了同一套代码, 代码中写有spring自带的定时任务,但是每次执行定时任务时只需要一台机器去执行。
当拿到这个需求时我脑子中立马出现了两个简单的解决方案:
- 利用ip进行判断, 两台机器ip肯定不一样, 指定某一台机器的ip运行。
- 只在一台机器上部署定时任务的代码。
最后两个方案又都被自己否决了。 第一条,如果指定ip的机器出现了问题怎么办? 例如说宕机了, 那么该制定ip的机器上的定时任务是不是就无法运行了?如果以后该服务器迁移导致ip变化怎么办?
第二条, 同上, 还有就是要维护两套代码很不方便。
因为上面两个假设都不成立, 只能另找他法。 于是便想到利用mysql去解决, 之前了解过一点mysql的锁机制, 知道如果有同时的两个任务去写数据库中同一条记录, 只有一条会成功, 这是利用了mysql的排他锁。
下面就开始代码演示, 这里主要想给大家的是一个思路的提示, 代码还是很简单的。
首先需要单独创建一张表
CREATE TABLE `t_schedule_cluster` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '@cname:主键', `execute` int(1) NOT NULL COMMENT '@cname:执行状态', `version` int(11) NOT NULL COMMENT '@cname:版本号\r\n ', `task_name` varchar(128) NOT NULL COMMENT '@cname:任务名称\r\n ', `execute_ip` varchar(32) DEFAULT NULL COMMENT '@cname:执行ip\r\n ', `update_time` datetime DEFAULT NULL COMMENT '@cname:修改时间\r\n ', PRIMARY KEY (`id`), KEY `Index_series_id` (`execute`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='@cname:多机定时任务调度';
看一下建成后的表结构, 注释写的已经很清楚了, 初始化时需要添加一些定时任务的名称(task_name), 这个要和你代码中保持一致, 后面会提到:
- 代码
首先看下我代码中用到的spring定时任务:
代码
首先看下我代码中用到的spring定时任务:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns= xmlns:xsi=xmlns:task=xsi:schemaLocation=default-lazy-init="true"> <description>使用Spring的 Scheduled的定时任务配置</description> <!--支持annotation的方式--> <task:annotation-driven /> <task:scheduler id="springScheduler" pool-size="10"/> <task:scheduled-tasks scheduler="springScheduler"><!-- 测试使用, 项目启动后每隔一分钟执行一次 --><task:scheduled ref="listCarAction" method="listCar" cron="0 0/1 0 * * ?"/><task:scheduled ref="listCarAction" method="listCar" cron="0 0/1 0 * * ?"/> </task:scheduled-tasks> </beans>
相信大家都是用过这种定时任务的设置方法, 因为它是spring自带的, 所以使用起来很方便, 这里我指定了两个定时任务来模拟两台机器的情况, 两个定时任务都是项目启动后每隔一分钟执行一次。
然后看看这个listCar中的代码:
//定时任务的名称, 这个和数据库中的task_name是保持一致的, 保证要执行该定时任务。
public static final String LIST_CAR_TASK = "listCarTask";
private ScheduleClusterTask scheduleClusterTask;
//这个时间是根据spring-scheduler.xml中配置的定时刷新时间, 比如说我们以后要设置这个定时任务时4小时刷新一次
public static final long maxExpireTime = 4 * 3600;public void listCar() {if (scheduleClusterTask.isValidMachine(maxExpireTime, CommonConstants.ScheduleTaskName.LIST_CAR_TASK)) {//执行具体的task方法, doTask();//将execute状态更新为0scheduleClusterTask.end(LIST_CAR_TASK);}}
最后看下最核心的代码:ScheduleClusterTask.java
/*** 多机定时任务工具类* Created by WangMeng on 2017/4/12.*/
@Service
public class ScheduleClusterTask {private ScheduleClusterEntityService scheduleClusterEntityService;/*** 这里因为两台机器都有同样的定时任务, 会同时执行这个方法,只有一台机器可以执行成功,返回true。* @param maxExpireTime 最大的检查时间。* @param taskName 任务名称。* @return*/public boolean isValidMachine(long maxExpireTime, String taskName) {boolean isValid = false;try {//通过taskName去数据库中查找到该条记录, 如果大家使用的是mybatis这里需要改一下, 就是一个简单的查询操作ScheduleClusterEntity carIndexEntity = scheduleClusterEntityService.findOne(ScheduleClusterEntity.Fields.taskName.eq(taskName));int execute = carIndexEntity.getExecute();String ip = InetAddress.getLocalHost().getHostAddress();long currentTimeMillis = System.currentTimeMillis();long time = carIndexEntity.getUpdateTime().getTime();if (execute == 0 && time + maxExpireTime - 1000 < currentTimeMillis) {isValid = checkMachine(taskName, carIndexEntity, ip);} else if (time + maxExpireTime - 1000 < currentTimeMillis){//这里要判断下, 如果上一次执行出现异常导致execute没有更新为0, 那么这里要判断上一次更新时间的间隔。isValid = checkMachine(taskName, carIndexEntity, ip);}} catch (UnknownHostException e) {e.printStackTrace();}return isValid;}/*** end方法主要是将excute(是否正在执行的标志位,0:没有执行, 1:正在执行)更新为0* @param taskName* @return*/public boolean end (String taskName) {ScheduleClusterEntity carIndexEntity = scheduleClusterEntityService.findOne(ScheduleClusterEntity.Fields.taskName.eq(taskName));//将execute状态更新为0return scheduleClusterEntityService.end(carIndexEntity);}private boolean checkMachine(String taskName, ScheduleClusterEntity carIndexRefresh, String ip) {return scheduleClusterEntityService.start(taskName, carIndexRefresh.getVersion(), ip);}@Autowiredpublic void setScheduleClusterEntityService(ScheduleClusterEntityService scheduleClusterEntityService) {this.scheduleClusterEntityService = scheduleClusterEntityService;}
}
这里还有start方法, 看看怎样的操作:
@Repository
public class DefaultScheduleClusterEntityDao extends AbstractDao<ScheduleClusterEntity> implements ScheduleClusterEntityDao {@Overridepublic boolean start(String taskName, int version, String ip) {String sql = "update t_schedule_cluster set execute = 1, " +"version = ?, execute_ip = ?, update_time = ?" +" where task_name = ? and version = ?";Sql s = new Sql(sql);s.addParam(version + 1);s.addParam(ip);s.addParam(SqlTimeUtils.nowTimestamp());s.addParam(taskName);s.addParam(version);return 1 == executeUpdate(s);}
}
核心的代码到了这里就没有了, 代码确实是非常非常的简单, 有兴趣的话大家可以在本地测试一下就可以。
当然还有更多很好地解决方案, 我这里秉承的是最简单的处理方式, 如果大家对我这个方案有疑问或者做的不好的地方都希望大家能够提出来, 谢谢了,
转载于:https://blog.51cto.com/14311648/2390405
Java应用集群下的定时任务处理方案(mysql)相关推荐
- 运维企业专题(11)RHCS高可用集群下MySql数据库与共享磁盘(单点写入、多点写入)的设置
实验环境 主机名 IP 服务 server1 172.25.6.1 ricci,luci, iscsi,mysql-server server2 172.25.6.2 ricci,iscsi,mysq ...
- k8s集群下搭建数据同步工具-canal:canal-admin篇
k8s集群下搭建数据同步工具-canal:canal-admin篇 前言 容器化 canal-admin 环境准备 k8s集群创建pod canal-admin 前言 本文使用v1.1.4版本的can ...
- 大规模集群下Hadoop NameNode如何承载每秒上千次的高并发访问
目录 一.问题源起 二.HDFS优雅的解决方案 (1)分段加锁机制 + 内存双缓冲机制 (2)多线程并发吞吐量的百倍优化 (3)缓冲数据批量刷磁盘 + 网络的优化 四.总结 五.参考文章 一.问题源起 ...
- mysql集群集成springboot_springboot配置数据库包括集群下 配置
首先准备数据库的必要信息. 查询当前数据库实例名 方法一:select instance_name from v$instance; 方法二:show parameter instance 查询数据库 ...
- yarn集群下启动spark错误WARN:66 - Neither spark.yarn.jars nor spark.yarn.archive is set
yarn集群下启动spark错误如下: WARN Client:66 - Neither spark.yarn.jars nor spark.yarn.archive is set, falling ...
- Hadoop高可用集群下namenode格式化失败问题解决
Hadoop高可用集群下namenode格式化失败问题解决 输入hdfs namenode -format报如下错误 解决方法: 1.在zookeeper目录下执行./bin/zkServer.sh ...
- DatabaseMetaData查询集群下的库名
网上很少有查询集群下的库名的方法.一直在想是getschema()方法里能查到库名还是getcatlogs()方法里能查到库名.最后在databasemetadata的官方文档看了下发现getsche ...
- 【云原生之kubernetes实战】在k8s集群下部署Weave Scope监控平台
[云原生之kubernetes实战]在k8s集群下部署Weave Scope监控平台 一.Weave Scope介绍 1.Weave Scope简介 2.Weave Scope的特点 3.Weave ...
- 集群定义以及高性能计算环境方案
[精彩] 集群定义以及高性能计算环境方案. http://www.chinaunix.net 作者:gongshi 发表于:2007-11-19 15:55:07 [发表评论] [查看原文] [服务 ...
最新文章
- spring boot hello world 搭建
- Java实现无向图的邻接列表表示,深度遍历及广度遍历
- 解决Windows Installer的错误
- Refuses to install for WTP10
- 微信小程序定义全局变量_微信小程序第二天学习内容分享
- 功放音量调节原理_汽车音响知识关于功放和低音喇叭的匹配
- 中国程序员最应该感谢的几家公司
- 全球AI挑战-场景分类的比赛源码(多模型融合)
- 三款主流智能车机横评 吉利博越GKUI实至名归
- 浅谈工业级物联网项目架构设计及实施
- 计算机文件自动备份到移动硬盘,1个让移动硬盘自动备份的简单方法!
- 嵌入式Linux开发的编程语言选择
- 查看windows电脑凭证密码 -Invoke-WCMDump
- java 使用POI 导出 Excel 画斜线
- Android 蓝牙HID协议(基于BR蓝牙)连接流程分析--framework-jni-btif-bta-btm-hci -- 全网最详细(一)
- 强化学习(RL)原理以及数学模型
- word2010公式编辑器 格式设置
- paperwhite3翻页_Kindle vs. Paperwhite vs. Voyage vs. Oasis:您应该购买哪种Kindle?
- TheTechBehindDx11UnrealEngineSamaritanDemo
- 我发现智能无人机课程里面讲了无人机建模这方面的理论知识
热门文章
- python 随机字符串_python生成随机数、随机字符串
- php 简易 blog,PHP实现简易blog的制作
- wdcp mysql密码_wdcp默认的mysql密码是多少?
- c#数据库訪问返回值类型为SqlDataReader时使用using时注意的问题
- 初学JSP+Servlet常见的错误
- 虚拟主机TOMCAT配置
- 恩智浦NXP I.MX6ULL芯片介绍下载官网资料
- 机械秒表的使用方法_让console.log()不再是你的唯一选项js日志输出6种方法
- mysql 客户服务号获取_《MySQL排错指南》——1.4 获取查询信息-阿里云开发者社区...
- 修改java启动参数_如何修改jvm启动参数