忙了两个月终于有时间写一写。众所周知自动化任务就是根据一些定时器时间定时任务。比如Quartz框架等。在集群中一般有两种方案实现自动化任务,第一种是单独放在一个项目中和集群独立部署。第二种就是接下来要说的,还是在集群中部署。具体如下:

  • 建立任务表以及插入任务数据
create table `vt_schedule_cluster` (
	`id` bigint (20),
	`execute` int (1),
	`version` int (11),
	`task_name` varchar (384),
	`execute_ip` varchar (96),
	`update_time` datetime 
); 
insert into `vt_schedule_cluster` (`id`, `execute`, `version`, `task_name`, `execute_ip`, `update_time`) values('1','1','0','pushInvoiceTask','127.0.0.1','2019-12-23 22:55:00');
insert into `vt_schedule_cluster` (`id`, `execute`, `version`, `task_name`, `execute_ip`, `update_time`) values('9','1','1','pushFileTask','172.0.0.1','2019-12-23 22:52:41');
alter table vt_schedule_cluster add index index_taskname ('task_name')
加索引目的:因为我们使用的阿里mysql8.0 多个集群去数据查询不加索引抢占资源会导致死锁。特别是多个任务的时候。
解释字段:
id 主键
execute 代表任务是否正在执行 1 正在执行 0 未在执行
version 后期版本多了可以使用目前可以不使用
task_name 任务名称 根据自己任务设置
execute_ip 抢到任务执行ip
update_time 更新时间
  • 核心代码实现
@Scheduled(cron = "0 */5 * * * ?")
	public void Task() {
		try {
			String ip = InetAddress.getLocalHost().getHostAddress();
			String threadName = Thread.currentThread().getName();
			if (scheduleClusterService.isValidMachine(ip,TASK_NAME)) {
				logger.info(String.format("获取任务的机器ip%s,线程名称%s", ip,threadName));
				/**
				写您的业务代码
				**/
				}
				scheduleClusterService.updateTaskInfoExeEnd(TASK_NAME);
			}else {
				System.err.println(String.format("没有获取到任务的ip%s,线程名称%s", ip,threadName));
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			logger.error("自动推送任务异常"+e.getMessage(),e);
		}
	}
@Override
	public boolean isValidMachine(String ip,String taskName) {
			boolean isValid = false;
			ScheduleCluster selectTaskInfo = selectTaskInfo(taskName);
				if (selectTaskInfo.getExecute() == 0) {
					selectTaskInfo.setExecuteIp(ip);
					selectTaskInfo.setExecute(1);
					selectTaskInfo.setUpdateTime(new Date());
					isValid = checkMachine(selectTaskInfo);
				}
			return isValid;
		}

	/**
	 * 更新
	* @Title: checkMachine 
	* @author drj 
	* @Description: TODO(更新任务状态为1代表正在执行)  
	* @param @param task
	* @param @return    参数  
	* @return boolean    返回类型  
	* @date 2020年1月1日  下午9:29:31
	 */
	private boolean checkMachine(ScheduleCluster task) {
		int a = updateTaskInfoExe(task);
		return a == 1 ? true : false;
	}
  • sql 语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vtax.mapper.ScheduleClusterMapper">
	<resultMap type="com.vtax.invoice.entity.ScheduleCluster"
		id="BaseResultMap">
		<id column="id" property="id" jdbcType="INTEGER" />
		<result column="execute" property="execute" jdbcType="INTEGER" />
		<result column="version" property="version" jdbcType="INTEGER" />
		<result column="task_name" property="taskName" jdbcType="VARCHAR" />
		<result column="execute_ip" property="executeIp" jdbcType="VARCHAR" />
		<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
	</resultMap>
	<!-- 查询任务的sql  在mysql8.0 中最好使用*。还有就是使用for update 排它锁保证数据不被其他更新 -->
	<select id="selectTaskInfo" parameterType="String" resultMap="BaseResultMap">
		select *from vt_schedule_cluster  where  task_name = #{taskName,jdbcType=VARCHAR} for update
	</select>
	<!-- 更新任务的接口 -->
	<update id="updateTaskInfoExeEnd" parameterType="String">
		update vt_schedule_cluster  set execute = 0 where task_name = #{taskName,jdbcType=VARCHAR}
	</update>
	<!-- 更新任务的接口 -->
	<update id="updateTaskInfoExe" parameterType="com.vtax.invoice.entity.ScheduleCluster">
	update vt_schedule_cluster 
	<set>
		<if test="execute != null">
			execute = #{execute,jdbcType=INTEGER},
		</if>
		<if test="executeIp != null">
			execute_ip = #{executeIp,jdbcType=VARCHAR},
		</if>
		<if test="updateTime != null">
			update_time = #{updateTime,jdbcType=TIMESTAMP},
		</if>
	</set>
		    where task_name = #{taskName,jdbcType=VARCHAR}
	</update>
</mapper>

总结:目前这个基本可以满足集群自动化任务但是当遇到执行玩任务,没有更新执行状态 会让后面任务一直抢不到资源,因为他一直是1。大概是需要根据更新时间和当前时间来实现。这个后期再详细解释。

  • 解决更新状态失败后一直不会执行任务的解决方案
private final static long maxTime  = 1000*60;//每分钟执行一次间隔时间
 /**
	    * 验证任务是否有效
	    * @return
	    */
	@Override
	@Transactional
	public boolean isValidMachine(String ip,String taskName,long maxTime) {
			boolean isValid = false;
			 long currentTimeMillis = System.currentTimeMillis();//当前时间
			ScheduleCluster selectTaskInfo = selectTaskInfo(taskName);
			long updateTime = selectTaskInfo.getUpdateTime().getTime();//上次更新时间
				if (selectTaskInfo.getExecute() == 0) {
					selectTaskInfo.setExecuteIp(ip);
					selectTaskInfo.setExecute(1);
					selectTaskInfo.setUpdateTime(new Date());
					isValid = checkMachine(selectTaskInfo);
				}else if(updateTime + maxTime -1000 < currentTimeMillis) {
				/**
				假如上次更新时间加定时任务时间间隔-1000(1000代表大约执行任务消耗的时间)具体看自己任务执行时间。还小于当前时间的话肯定是上次更新失败导致更新时间还是上上次的所以这次应该获取任务。
				**/
					isValid = checkMachine(selectTaskInfo);
				}
			System.out.println("select data is getExecute " + selectTaskInfo.getExecute());
			return isValid;
		}

正对上诉问题有疑问可以留言讨论。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐