关于定时任务:定时提醒再也不用担心忘记啦

定时揭示再也不必放心遗记啦~~~    好忘性不如烂笔头,烂笔头不如靠谱的自动化~~~正在寻找自动化流程编排工具或定时工作工具的敌人们连忙看过去,为你介绍一款简略配置即可实现自动化定时揭示的平台阿里云计算巢AppFLow。    利用它能够通过简略的几步配置实现自动化定时工作和各类其余自动化流程。例如,以下这些场景: 定时填报工时定时告诉写周报/月报揭示定期数据备份与更新:对于IT人员或数据库管理员来说,定期备份零碎数据或更新应用程序版本是至关重要的,定时揭示能确保这些操作按计划执行。衰弱与劳动提醒:长时间工作后,设置短暂劳动或眼保健操的揭示,以保障身体健康和工作效率。客户关心与跟进:销售和客服团队依据客户关系保护策略,为要害客户或潜在客户的跟进设置个性化揭示。利用AppFlow定时工作自动化执行下面这些告诉或揭示,让咱们从这些反复工作中解放出来,再也不必放心忘了“定时工作”。目前,AppFlow反对钉钉群、飞书群、企业微信群、华为云WeLink的自动化告诉。上面以飞书为例,为大家演示整体配置应用流程,仅需简略几步,无妨一起来试试吧~ 配置办法一、配置飞书机器人关上电脑版飞书,找到须要设计自动化定时工作揭示的群聊,点击右上角设置按钮,而后点击群机器人。 点击“增加机器人”,抉择“自定义机器人”,设置完头像和名称后点击“增加”。 增加胜利后,您将取得一个Webhook地址,请复制保留该地址,后续Appflow配置中将会应用到。平安设置能够抉择自定义关键词或签名校验,也能够临时疏忽先实现流程配置。至此,飞书机器人的配置就全副实现啦,接下来仅需到AppFlow中创立连贯流即可。 二、AppFlow配置(应用举荐模版创立)如果在下面的机器人平安配置中抉择的是自定义关键词或没有进行平安设置,则应用上面的举荐模版配置即可;否则请参考前面的自定义配置模式。 关上AppFlow首页,找到举荐模版中的“定时告诉飞书群”,点击“立刻应用按钮” 接下来进入到连接器配置环节。首先配置触发条件,此处即为填写定时工作的“cron表达式”,而后点击下一步。如果您对cron表达式不相熟,上面将给出一些例子供您参考批改:0 0 12 ? 每天中午12点触发 0 15 10 ? 每天上午10:15触发0 0/5 14 ? 在每天下午2点到下午2:55期间的每5分钟触发0 0-5 14 ? 在每天下午2点到下午2:05期间的每1分钟触发 0 15 10 ? * MON-FRI 周一至周五的上午10:15触发您也能够参考官网文档https://help.aliyun.com/zh/ecs/user-guide/cron-expressions 配置执行动作,即飞书揭示要发送的音讯内容和Webhook地址。如果在下面的平安设置中抉择了“自定义关键词”,则音讯内容中请务必蕴含定义的关键词。如果平安设置抉择了“签名校验”,请参照本文下方的“自定义配置模式”进行配置。 填写根本信息。按要求简略填写根本信息即可,点击下一步即可实现配置。点击去列表查看即可查看并公布刚配置的连贯流。 点击“运行一次”能够调试刚刚的配置是否失常工作,确认无误后点击“公布”即可实现一个定时音讯告诉的工作流了~ 三、AppFlow配置(应用自定义配置模式)关上AppFlow首页,点击左侧连贯流,点击“创立连贯流”。 首先填写连贯流名称和形容等根本信息,点击下一步进入连贯流自定义配置页面。 抉择触发事件连接器,此处抉择“定时调度”即可,触发事件抉择“反复调度”,即反复定时触发该连贯流。 点击关上“入参配置”,填写cron表达式后点击“实现进入下一步”。 配置连接器执行动作,即飞书机器人连接器配置。抉择“飞书机器人”,执行动作抉择“发送文本音讯”,点击关上“入参配置”进行参数配置。 填写入参,将飞书机器人中获取的Webhook地址和签名校验密钥顺次填入,并填写音讯内容。请留神,如果在下面的平安设置中抉择了“自定义关键词”,则音讯内容中请务必蕴含定义的关键词。点击实现并保留流程。 在连贯流列表中将会看到刚创立的连贯流,点击“运行一次”能够调试刚刚的配置是否失常工作,确认无误后点击“公布”即可实现一个定时音讯告诉的工作流了~ 结语至此一个定时告诉飞书群聊的自动化连贯流就能够失常工作了,Appflow不仅反对飞书,同时还反对其余常见通信软件。当然,定时调度只是Appflow能够反对的场景之一,利用连接器市场中的其余连接器,能够实现多种多样的简单自动化场景。如果现有的连接器市场还无奈满足需要,咱们还能够反对自定义连接器。欢送大家一起来Appflow中充分发挥想象力,发明出各种好玩、高效的连贯流场景吧~ 附:Appflow自定义连接器办法如果您的利用反对Http拜访,那么就能够轻松配置到Appflow中进行连贯流编排~ 拜访Appflow首页,点击左侧连接器-自定义连接器,抉择“创立连接器”。 上传连接器图标并填写相干信息,点击保留连接器。 在左侧菜单栏顺次点击“鉴权设置”,目前Appflow反对多种鉴权形式,可按需抉择。 点击“执行动作”-“创立执行动作”,配置以后连接器可执行的动作,目前仅反对Http类型的执行动作。填写根本信息和接口门路,点击下一步。 装置接口要求填写Http各类参数构造,此处定义的各个参数将会在创立连贯流时填写。 点击下一步,定义响应体参数。响应体构造将决定在连贯流中参数插入的应用。 点击“公布”即可在创立连贯流时抉择自定义的连接器。 分割咱们有任何疑难或者需要或者合作意向都能够退出咱们的官网反对钉钉群~欢送大家一起减速群聊交换,为工作和Coding提效~

February 29, 2024 · 1 min · jiezi

关于定时任务:故障分析-MySQL-备份文件静默损坏一例分析

作者:付祥 现居珠海,次要负责 Oracle、MySQL、mongoDB 和 Redis 保护工作。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 背景线上一套 MySQL 打算降级到 8.0 ,通过备份还原搭建一个测试环境,用于降级测试。数据库采纳 xtrabackup 每天进行全备,压缩备份文件约 300G ,解压到一半就报错了: gzip: stdin: invalid compressed data--format violatedtar: Unexpected EOF in archivetar: Unexpected EOF in archivetar: Error is not recoverable: exiting now刚开始认为只是这个备份文件不残缺,又找了前一天备份文件,解压过程中也报了同样的谬误,备份文件比拟大,无疑减少了排障工夫。 故障剖析备份脚本通过 crontab 每天凌晨执行,线上都是同一套备份脚本,不同我的项目时常做备份数据还原,还是头一次遇到备份文件解压失败景象,查看了脚本,每个要害阶段都做了状态码判断是否胜利,若失败就告警,同时对 xtrabackup 备份日志最初一行是否蕴含 completed OK 关键词也做了判断,要害备份脚本如下: xtrabackup xxx --stream=tar --no-timestamp $bkdir 2> xxx.log | gzip - > xxx.tar.gz近期也没收到失败告警,阐明备份脚本是执行胜利了的,感觉太奇怪了,查看定时工作日志,发现同一工作同一时间点居然启了2次: [root@localhost backup]# grep backup /var/log/cronMar 6 00:00:01 localhost CROND[6212]: (root) CMD (sh xxx/mysql_ftp_backup.sh || echo 1 > xxx/err.log)Mar 6 00:00:01 localhost CROND[6229]: (root) CMD (sh xxx/mysql_ftp_backup.sh || echo 1 > xxx/err.log)Mar 7 00:00:01 localhost CROND[5387]: (root) CMD (sh xxx/mysql_ftp_backup.sh || echo 1 > xxx/err.log)Mar 7 00:00:01 localhost CROND[5420]: (root) CMD (sh xxx/mysql_ftp_backup.sh || echo 1 > xxx/err.log)crond 服务每次同时拉起2个过程执行备份,并发地往同一个压缩文件 xxx.tar.gz 写数据,备份数据互相笼罩,导致备份文件损坏,每天看似备份胜利的工作,其实备份都是有效的,这也阐明了定期备份复原演练的重要性。为何定时工作同一时间点会启动2次?查看 crond 过程: ...

March 23, 2023 · 2 min · jiezi

关于定时任务:解析-Golang-定时任务库-gron-设计和原理

解析 Golang 定时工作库 gron 设计和原理简略说,每一个位都代表了一个工夫维度,* 代表选集,所以,下面的语义是:在每天早上的4点05分触发工作。但 cron 毕竟只是一个操作系统级别的工具,如果定时工作失败了,或者压根没启动,cron 是没法提醒开发者这一点的。并且,cron 和 正则表达式都有一种魔力,不知道大家是否感同身受,这里引用共事的一句名言: 这世界上有些语言非常相似: shell脚本, es查问的那个dsl语言, 定时工作的crontab, 正则表达式. 他们相似就相似在每次要写的时候基本都得从新现学一遍。刚巧,最近看到了 gron 这个开源我的项目,它是用 Golang 实现一个并发安全的定时工作库。实现非常简略精美,代码量也不多。明天咱们就来一起拆散源码看一下,怎么基于 Golang 的能力做进去一个【定时工作库】。 Gron provides a clear syntax for writing and deploying cron jobs.grongron 是一个泰国小哥在 2016 年开源的作品,它的个性就在于非常简略和清晰的语义来定义【定时工作】,你不必再去记 cron 的语法。咱们来看下作为使用者怎么上手。首先,咱们还是一个 go get 安装依赖:$ go get github.com/roylee0704/gron复制代码假设咱们期望在【时机】到了当前,要做的工作是打印一个字符串,每一个小时执行一次,咱们就可能这样: package mainimport ( "fmt" "time" "github.com/roylee0704/gron")func main() { c := gron.New() c.AddFunc(gron.Every(1*time.Hour), func() { fmt.Println("runs every hour.") }) c.Start()}复制代码非常简略,而且即便是在 c.Start 之后咱们依然可能增加新的定时工作进去。反对了很好的扩展性。 定时参数留意到咱们调用 gron.New().AddFunc() 时传入了一个 gron.Every(1*time.Hour)。这里其实你可能传入任何一个 time.Duration,从而把调度间隔从 1 小时调整到 1 分钟以至 1 秒。除此之外,gron 还很贴心地封装了一个 xtime 包用来把常见的 time.Duration 封装起来,这里咱们开箱即用。 ...

August 18, 2022 · 3 min · jiezi

关于定时任务:在线QuartzCron定时任务表达式在线生成

在线QuartzCron定时工作表达式在线生成在线QuartzCron定时工作表达式在线生成 Quartz是一个齐全由java编写的开源作业调度框架,Quartz最次要的性能就是调度器(实现定时工作)。本工具能够在线生成QuartzCron表达式Quartz是一个齐全由java编写的开源作业调度框架,Quartz最次要的性能就是调度器(实现定时工作)。本工具能够在线生成QuartzCron表达式 https://tooltt.com/quartzcron/

April 1, 2022 · 1 min · jiezi

关于定时任务:Java中定时任务的6种实现方式你知道几种

简直在所有的我的项目中,定时工作的应用都是不可或缺的,如果使用不当甚至会造成资损。还记得多年前在做金融零碎时,出款业务是通过定时工作对外打款,过后因为银行接口解决能力无限,外加定时工作使用不当,导致收回大量反复出款申请。还好在前面环节将交易卡在了零碎外部,未产生资损。 所以,零碎的学习一下定时工作,是十分有必要的。这篇文章就带大家整体梳理学习一下Java畛域中常见的几种定时工作实现。 线程期待实现先从最原始最简略的形式来解说。能够先创立一个thread,而后让它在while循环里始终运行着,通过sleep办法来达到定时工作的成果。 public class Task { public static void main(String[] args) { // run in a second final long timeInterval = 1000; Runnable runnable = new Runnable() { @Override public void run() { while (true) { System.out.println("Hello !!"); try { Thread.sleep(timeInterval); } catch (InterruptedException e) { e.printStackTrace(); } } } }; Thread thread = new Thread(runnable); thread.start(); }}这种形式简略间接,然而可能实现的性能无限,而且须要本人来实现。 JDK自带Timer实现目前来看,JDK自带的Timer API算是最古老的定时工作实现形式了。Timer是一种定时器工具,用来在一个后盾线程打算执行指定工作。它能够安顿工作“执行一次”或者定期“执行屡次”。 在理论的开发当中,常常须要一些周期性的操作,比方每5分钟执行某一操作等。对于这样的操作最不便、高效的实现形式就是应用java.util.Timer工具类。 外围办法Timer类的外围办法如下: // 在指定延迟时间后执行指定的工作schedule(TimerTask task,long delay);// 在指定工夫执行指定的工作。(只执行一次)schedule(TimerTask task, Date time);// 提早指定工夫(delay)之后,开始以指定的距离(period)反复执行指定的工作schedule(TimerTask task,long delay,long period);// 在指定的工夫开始依照指定的距离(period)反复执行指定的工作schedule(TimerTask task, Date firstTime , long period);// 在指定的工夫开始进行反复的固定速率执行工作scheduleAtFixedRate(TimerTask task,Date firstTime,long period);// 在指定的提早后开始进行反复的固定速率执行工作scheduleAtFixedRate(TimerTask task,long delay,long period);// 终止此计时器,抛弃所有以后已安顿的工作。cancal();// 从此计时器的工作队列中移除所有已勾销的工作。purge();应用示例上面用几个示例演示一下外围办法的应用。首先定义一个通用的TimerTask类,用于定义用执行的工作。 ...

August 5, 2021 · 3 min · jiezi

关于定时任务:linux-定时任务-输出时间

有时候会对定时工作做下测试,看下执行状况,须要输入下以后执行工夫,不熟的话工夫格局容易弄错导致输入谬误 crontab -u apache -e* * * * * echo "11 _ $(date '+\%Y_\%m_\%d \%H:\%M:\%S')" >> /data/www/xxxx/storage/logs/cron.txt成果

June 10, 2021 · 1 min · jiezi

Android-实现定时任务之一-使用Handler的postDelayed

弃用的方法网上搜索有介绍使用Sleep方法实现的,这里就不介绍了 1/3. 在 Activity 中声明成员变量Handler handler=new Handler();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Runnable runnable=new Runnable() { @Override public void run() { Log.i(TAG,sdf.format(System.currentTimeMillis())); handler.postDelayed(this, 2000); }};2/3. 启动定时器handler.postDelayed(runnable,1000);//每两秒执行一次runnable. 3/3. 关闭定时器handler.removeCallbacks(runnable); 解释启动定时器中的 1000 表示点击按钮1秒钟后启动定时器runnable 中的 2000 表示每2秒钟执行一次

October 16, 2019 · 1 min · jiezi

Android-实现定时任务之二-Handler-结合-TimerTask

1/4. 在 Activity 中声明成员变量private final Timer timer = new Timer();private TimerTask task;Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub // 要做的事情 super.handleMessage(msg); }};2/4. 初始化任务在Activity 的生命周期函数 onCreate 中初始化任务,代码如下: task = new TimerTask() { @Override public void run() { Message message = new Message(); message.what = 1; handler.sendMessage(message); }};3/4. 启动定时器timer.schedule(task, 1000, 2000); 4/4. 关闭定时器timer.cancel(); 解释第三步中中间参数1000 是点击按钮后延迟1秒钟再启动定时器,后面的 2000 表示每2秒钟执行一次任务

October 16, 2019 · 1 min · jiezi

Java简易定时任务实现

前言接入微信支付的时候,看到微信支付的回调是按照某种频率去回调的,像15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h这样,其中有一次成功就不会再回调。于是在想怎么用Java做这个事情。有定时任务这类功能的框架像Spring和Quartz貌似都没有直接提供以上的功能。也是出于想练手的目的,决定自己写一写。 最终的实现效果// 具体的业务BaseJob task = new BaseJob() { // 任务执行的次数(模拟真实业务上的退出) int runTime = 1; @Override public void run() { // 业务逻辑 System.out.println("hello world"); // 这里模拟了微信回调成功,任务完成 if (runTime++ > 3) { this.setExit(true); } }};/** * 测试按照指定时间隔执行某个任务 * @throws IOException */@Testpublic void test1() throws IOException { // 新建一个产生指定时间的延迟时间生成器,内部就是个队列 DesignatDTGenerator designatDTGenerator = new DesignatDTGenerator(); // 设置时间间隔 designatDTGenerator.addDelayTime(1_000) // 1秒后执行 .addDelayTime(4_000) // 距离上次执行4秒后执行 .addDelayTime(15_000) // 距离上次执行15秒后执行 .addDelayTime(180_000) // 距离上次执行3分钟后执行 .addDelayTime(180_000) // 距离上次执行3分钟后执行 .addDelayTime(360_000) // 距离上次执行6分钟后执行 .addDelayTime(3_600_000); // 距离上次执行1小时后执行 // 构造一个提交的任务,传入具体的业务对象task,传入延迟时间生成器designatDTGenerator DelayTimeJob delayTimeJob = new DelayTimeJob(task, designatDTGenerator); // 新建一个执行器,执行器可以重复使用,每次提交新的任务即可 JobActuator actuator = new JobActuator(); // 提交任务,开始执行任务 actuator.addJob(delayTimeJob); // 阻塞主线程,方便查看运行结果 System.in.read();}/** * 测试按照固定时间间隔执行某个任务 * 只是延迟时间生成器不同而已,可以达到不同的调用效果 * @throws IOException */@Testpublic void test2() throws IOException { // 新建一个执行器 JobActuator actuator = new JobActuator(); // 新建一个产生固定时间的延迟时间生成器,每3s执行一次 FixedRateDTGenerator fixedRateDTGenerator = new FixedRateDTGenerator(3000); // 新建一个任务 DelayTimeJob delayTimeJob = new DelayTimeJob(task, fixedRateDTGenerator); // 提交任务,开始执行任务 actuator.addJob(delayTimeJob); // 阻塞主线程,方便查看运行结果 System.in.read();}类图 ...

July 16, 2019 · 3 min · jiezi

SpringBoot20-基础案例04定时任务和异步任务的使用方式

一、定时任务1、基本概念按照指定时间执行的程序。 2、使用场景数据分析数据清理系统服务监控二、同步和异步1、基本概念同步调用程序按照代码顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用顺序执行时,不等待异步调用的代码块返回结果就执行后面的程序。 2、使用场景短信通知邮件发送批量数据入缓存三、SpringBoot2.0使用定时器1、定时器执行规则注解@Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行@Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行@Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次@Scheduled(cron="/5") :通过cron表达式定义规则2、定义时间打印定时器import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;import java.util.Date;/** * 时间定时任务 */@Componentpublic class TimeTask { Logger LOG = LoggerFactory.getLogger(TimeTask.class.getName()) ; private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ; /** * 每3秒打印一次系统时间 */ @Scheduled(fixedDelay = 3000) public void systemDate (){ LOG.info("当前时间::::"+format.format(new Date())); }}3、启动类开启定时器注解@EnableScheduling // 启用定时任务@SpringBootApplicationpublic class TaskApplication { public static void main(String[] args) { SpringApplication.run(TaskApplication.class,args) ; }}四、SpringBoot2.0使用异步任务1、编写异步任务类import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;@Componentpublic class AsyncTask { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncTask.class) ; /* * [ asyncTask1-2] com.boot.task.config.AsyncTask : ======异步任务结束1====== * [ asyncTask1-1] com.boot.task.config.AsyncTask : ======异步任务结束0====== */ // 只配置了一个 asyncExecutor1 不指定也会默认使用 @Async public void asyncTask0 () { try{ Thread.sleep(5000); }catch (Exception e){ e.printStackTrace(); } LOGGER.info("======异步任务结束0======"); } @Async("asyncExecutor1") public void asyncTask1 () { try{ Thread.sleep(5000); }catch (Exception e){ e.printStackTrace(); } LOGGER.info("======异步任务结束1======"); }}2、指定异步任务执行的线程池这里可以不指定,指定执行的线城池,可以更加方便的监控和管理异步任务的执行。 ...

July 10, 2019 · 2 min · jiezi

那些年我们追过的定时调度

定时调度作为后端开发人员,我们总会遇到这样的业务场景:每周同步一批数据;每半个小时检查一遍服务器运行状况;每天早上八点给用户发送一份包含今日待办事项的邮件,等等。 这些场景中都离不开“定时器”,就像一个定好时间规则的闹钟,它会在指定时间触发,执行我们想要定义的调度任务。那么我们今天就来数一下,那些年我们用过的“定时调度”。 1.job (oracle)从刚工作就一直使用oracle数据库,最早接触的定时任务就是oracle数据库的job。job有定时执行的功能,可以在指定的时间点或每天的某个时间点自行执行任务。 而且oracle重新启动后,job会继续运行,不用重新启动。而且job的机制非常完备,可以查询相关的表或视图,查询job的定时规则和执行情况。缺点是作为oracle数据库层面的工具,自定义功能扩展,二次开发的难度比较大。 1.1 创建job 1.2 删除job 1.3 查询job2.crontab (linux)crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。 cron是服务名称,crond是后台进程,crontab则是定制好的计划任务表。大部分linux系统默认都安装了cron,可以检查一下。 crontab基本操作命令 crontab表达式格式 3.Timer和ScheduledExecutorService (java)Timer是jdk中提供的一个定时器工具,使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次。 TimerTask是一个实现了Runnable接口的抽象类,代表一个可以被Timer执行的任务。TimerTask类是一个抽象类,由Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()方法,该方法用于执行相应计时器任务要执行的操作。因此每一个具体的任务类都必须继承TimerTask,然后重写run()方法。 另外它还有两个非抽象的方法 当然,一般使用Timer的比较少,因为它的缺点比较明显: 1.单线程,当多个timer同时运行时,会等上一个执行完成,再执行下一个。2.Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止。 所以一般使用ScheduledExecutorService替代Timer。 ScheduledExecutorService:也是jdk自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。 4.SpringTask (spring)Timer和ScheduledExecutorService都是属于jdk层面上实现定时调度的类,功能还不足以让我们满意,那么现在介绍一个比较完善的定时调度工具 - SpringTask,是Spring提供的,支持注解和配置文件形式,支持crontab表达式,使用简单但功能强大。我个人非常喜欢SpringTask,仅仅是因为支持crontab表达式。 在springboot里面使用方式非常简单: 1.启动类添加开启定时调度的注解 @EnableScheduling2.在需要定时执行的方法上,增加注解 @Scheduled(cron ="crontab表达式") 默认的简单的使用步骤只有以上两步,但是SpringTask的默认使用方式也有一些不足: 1.默认线程池的poolsize为1,可以理解为Timer类似的单线程模式。无法动态修改crontab表达式,修改完只能重新部署后,才能生效。 问题1的解决方式,可以通过自定义 TaskExecutor来修改当前的线程池。问题2,则可以直接使用 threadPoolTaskScheduler类实现自定义的定时调度规则。 附解决两个问题的源码 TaskTimer.class 5.Quartz (其他产品)Quartz是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。它是一个功能强大、十分成熟的重量级产品,还支持负载均衡,实现分布式调度。 不过,对于Quartz的安装你要多花点功夫了,从数据库要建哪些表,到应用程序该如何部署。对于这样一个庞大的产品,本篇文章就不附上它的使用说明书了。 本人创业团队产品MadPecker,主要做BUG管理、测试管理、应用分发,网址:www.madpecker.com,有需要的朋友欢迎试用、体验!本文为MadPecker团队技术人员编写,转载请标明出处

June 17, 2019 · 1 min · jiezi

Springboot定时任务踩坑记录

前言在使用Springboot整合定时任务,发现当某个定时任务执行出现执行时间过长的情况时会阻塞其他定时任务的执行。问题定位后续通过翻查Springboot的文档以及打印日志(输出当前线程信息)得知问题是由于Springboot默认使用只要1个线程处理定时任务。问题复盘需要注意示例的Springboot版本为2.1.3.RELEASE。关键pom文件配置 <!–继承父项目–> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> …省略非关键配置 <!– 引入依赖–> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>定时任务import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;/** * 定时任务 * @author RJH * create at 2019-03-29 /@Componentpublic class SimpleTask { private static Logger logger= LoggerFactory.getLogger(SimpleTask.class); /* * 执行会超时的任务,定时任务间隔为5000ms(等价于5s) / @Scheduled(fixedRate = 5000) public void overtimeTask(){ try { logger.info(“current run by overtimeTask”); //休眠时间为执行间隔的2倍 Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } /* * 正常的定时任务 / @Scheduled(fixedRate = 5000) public void simpleTask(){ logger.info(“current run by simpleTask”); }}启动类import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication@EnableSchedulingpublic class TaskDemoApplication { public static void main(String[] args) { SpringApplication.run(TaskDemoApplication.class, args); }}运行结果…省略非关键信息2019-03-29 21:22:38.410 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by simpleTask2019-03-29 21:22:38.413 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by overtimeTask2019-03-29 21:22:48.413 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by simpleTask2019-03-29 21:22:48.414 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by overtimeTask2019-03-29 21:22:58.418 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by simpleTask2019-03-29 21:22:58.418 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by overtimeTask2019-03-29 21:23:08.424 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by simpleTask2019-03-29 21:23:08.424 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by overtimeTask2019-03-29 21:23:18.425 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by simpleTask2019-03-29 21:23:18.426 INFO 59731 — [ scheduling-1] com.rjh.task.SimpleTask : current run by overtimeTask…结果分析由运行结果可以看出:每次定时任务的运行都是由scheduling-1这个线程处理正常运行的simpleTask被overtimeTask阻塞导致了运行间隔变成了10秒后面通过查阅Springboot的文档也得知了定时任务默认最大运行线程数为1。解决方案由于使用的Springboot版本为2.1.3.RELEASE,所以有两种方法解决这个问题使用Springboot配置在配置文件中可以配置定时任务可用的线程数:## 配置可用线程数为10spring.task.scheduling.pool.size=10自定义定时任务的线程池使用自定义的线程池代替默认的线程池import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.TaskScheduler;import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;/* * 定时任务配置类 * @author RJH * create at 2019-03-29 /@Configurationpublic class ScheduleConfig { /* * 此处方法名为Bean的名字,方法名无需固定 * 因为是按TaskScheduler接口自动注入 * @return */ @Bean public TaskScheduler taskScheduler(){ // Spring提供的定时任务线程池类 ThreadPoolTaskScheduler taskScheduler=new ThreadPoolTaskScheduler(); //设定最大可用的线程数目 taskScheduler.setPoolSize(10); return taskScheduler; }} ...

March 29, 2019 · 2 min · jiezi

订阅 + 定时任务重构后台主机操作任务

问题描述主机状态一直显示有问题,去向动态链接库请求数据时,除了第一台主机访问成功外,以后的每一台主机返回的结果都是9(HOST_NOT_FOUND)。研究了很久也没研究明白,最后求助潘老师。潘老师指出是指针有问题,主机的指针是我们构造出来的,虽然该指针指向对象的name和context与动态链接库服务那边的都一样,但是他那里可能是按地址处理的,不是同一个地址,就报主机找不到了。经过测试,确实是这样。不能直接构造指针,需要使用同步计算机数据时返回的指针进行操作。所以,获取计算机状态,操作计算机都需要重新同步一下计算机,将指针同步过来。假设用户操作很频繁的话,对动态链接库的压力就很大。指不定啥时候就炸了。虽然我想到了这点,但也没想到好的解决方案。潘老师最终的设计方案非常好,采用定时任务,当有计算机的操作时,不实时进行操作,而是先存起来,每隔一段时间统一进行操作,从而减轻服务器压力。实现设计对主机进行关机或重启时,不立即执行,而是将其添加到一个要执行的任务列表中,当定时任务触发后,再统一执行。定时任务定时任务很简单,之前在计量中写过一个每天晚上定时统计数据的定时任务,这次写这个没什么难度,都不需要看文档了。注释很详尽,相信聪明的你完全可以理解。这里的存储计算机状态设置了一个主机名到计算机状态映射的HashMap,这里没有用ConcurrentHashMap,因为只有定时任务一个线程进行put,其他接口调用该Map只负责查询,不会出现冲突。@Async@Scheduled(fixedRate = 10000) // 每隔10s执行public void hostHandle() { logger.info("— 开始执行定时任务 —"); logger.debug(“获取关机重启的订阅者”); Set<String> shutdownSubscribers = this.cloneStringSetAndClear(hostService.getShutdownSubscribers()); Set<String> rebootSubscribers = this.cloneStringSetAndClear(hostService.getRebootSubscribers()); logger.debug(“获取主机结构体指针Map”); List<HostStruct.ByReference> byReferenceList = baseService.getHostStructReferenceList(); Map<String, HostStruct.ByReference> byReferenceMap = baseService.getHostStructReferenceMap(byReferenceList); if (!shutdownSubscribers.isEmpty()) { logger.info(“存在关机订阅,执行关机操作”); for (String name : shutdownSubscribers) { logger.debug(“获取结构体指针并关机”); HostStruct.ByReference byReference = byReferenceMap.get(name); baseService.shutdown(byReference); } } if (!rebootSubscribers.isEmpty()) { logger.info(“存在重启订阅,执行重启操作”); for (String name : rebootSubscribers) { logger.debug(“获取结构体指针并重启”); HostStruct.ByReference byReference = byReferenceMap.get(name); baseService.reboot(byReference); } } logger.debug(“查询主机列表”); for (HostStruct.ByReference byReference : byReferenceList) { logger.debug(“查询主机指针,同时获取主机信息”); Integer status = baseService.getHostStatus(byReference); logger.debug(“添加到Map中”); hostService.getHostStatusMap().put(Native.toString(byReference.name), status); } logger.info("— 定时任务执行完毕 —");}/** * 克隆字符串集合并清空原集合 * @param primarySet 原集合 * @return 克隆的字符串集合 */private Set<String> cloneStringSetAndClear(Set<String> primarySet) { logger.debug(“新建集合”); Set<String> set = new HashSet<>(primarySet); logger.debug(“清空原集合”); primarySet.clear(); logger.debug(“返回”); return set;}todo目前是用Set存储要操作的主机,但是经过查询,HashSet、LinkedHashSet、TreeSet都是线程不安全的。这里就怕执行定时任务的时候,突然来了一个关机的指令,两个线程同时访问Set,定时任务要克隆一个Set然后把这个清空,关机需要在Set中添加元素,两个线程如果交替执行,结果就很难说了。去concurrent包下找,也没找到合适的。看了看,有第三方库实现的线程安全的Set,以后引入进来。这里不知道是不是我用的有问题,是并发场景下不推荐用Set吗?为什么concurrent包下没有相关的实现类?总结感叹一句:C++还是难啊!C++老师对我的评价是字写得还不错,哈哈哈。佩服C++工程师! ...

March 9, 2019 · 1 min · jiezi

Grails集成Quartz插件实现定时任务(Job)

build.gradle依赖://定时器compile ‘org.grails.plugins:quartz:2.0.0.M4’创建一个job,执行命令:create-job com.test.Test修改TestJob:package com.testclass TestJob {static triggers = {} def execute(context) { // execute job println “执行…” // 获取传递过来的参数 def msg = context.mergedJobDataMap.get(“tip”) println msg }}控制器中动态调用:def personPush() { println “xxxxx” // 传参到job中 TestJob.schedule(new Date(),[tip: “执行了定时器。。。”])}暂停定时任务def jobManagerServicedef index() { // 停止所有定时任务 jobManagerService.pauseAll() render “Stop all Job."}恢复定时任务jobManagerService.resumeAll()JobManagerService服务中常用方法Map <String , List<JobDescriptor>> getAllJobs() {}List<JobDescriptor> getJobs(String group) {}def getRunningJobs() {}def pauseJob(String group, String name) {}def resumeJob(String group, String name) {}def pauseTrigger(String group, String name) {}def resumeTrigger(String group, String name) {}def pauseTriggerGroup(String group) {}def resumeTriggerGroup(String group) {}def pauseJobGroup(String group) {}def resumeJobGroup(String group) {}def pauseAll() {}def resumeAll() {}def removeJob(String group, String name) {}def unscheduleJob(String group, String name) {}def interruptJob(String group, String name) {}可自行查看grails.plugins.quartz.JobManagerService类常用方法:MyJob.schedule(String cronExpression, Map params) // 创建cron触发器MyJob.schedule(Long repeatInterval, Integer repeatCount, Map params) // 创建简单的触发器:以repeatInterval毫秒的延迟重复作业repeatCount + 1次MyJob.schedule(Date scheduleDate, Map params) // 将一个作业执行安排到特定日期MyJob.schedule(Trigger trigger) // 使用自定义触发器来安排作业的执行MyJob.triggerNow(Map params) // 强制立即执行工作 ...

January 18, 2019 · 1 min · jiezi

分布式任务框架之celery

架构Broker消息代理,作为临时储存任务的中间媒介,为 Celery 提供了队列服务。生产者将任务发送到 Broker,消费者再从 Broker 获取任务。Celery目前支持RabbitMQ、Redis、MongoDB、Beanstalk、SQLAlchemy、Zookeeper等 作为消息代理,但适用于生产环境的只有RabbitMQ和Redis,至于其他的方式,一是支持有限, 二是可能得不到更好的技术支持。 Celery官方推荐的是RabbitMQ,Celery的作者Ask Solem Hoel最初在VMware就是为RabbitMQ工作的,Celer最初的设计就是基于RabbitMQ,所以使用 RabbitMQ会非常稳定,成功案例很多。如果使用Redis,则有可能发生突然断电之类的问题 造成Redis突然终止后的数据丢失等后果。Beat任务调度器,负责调度并触发 Celery 定时周期任务。Beat 进程读取 CeleryConfig 中自定义的定时周期任务列表,将到期需要执行的定时任务发送到任务队列中。Worker任务执行单元,实际负责执行任务的服务进程,每一个 Worker 都有一个并发池(Prefork/Eventlet/Gevent/Thread)来支持多并发。Worker 会监听订阅的任务队列,当队列中有任务时,就会获取任务并执行。Result Backend/Store任务执行状态和结果存储,Celery 支持任务实时处理,也就是说 Celery 可以把任务执行的实时状态和最终结果回传生产者。这种回传也需要通过中间存储媒介。web监控管理添加管理任务任务的监控

January 3, 2019 · 1 min · jiezi

Linux cron 源码剖析

背景及 busybox 介绍流程图数据结构typedef struct CronFile { struct CronFile *cf_next; struct CronLine *cf_lines; char cf_username; smallint cf_wants_starting; / bool: one or more jobs ready / smallint cf_has_running; / bool: one or more jobs running / smallint cf_deleted; / marked for deletion (but still has running jobs) */} CronFile;typedef struct CronLine { struct CronLine *cl_next; char cl_cmd; / shell command / pid_t cl_pid; / >0:running, <0:needs to be started in this minute, 0:dormant /#define START_ME_REBOOT -2#define START_ME_NORMAL -1#if ENABLE_FEATURE_CROND_CALL_SENDMAIL int cl_empty_mail_size; / size of mail header only, 0 if no mailfile */ char cl_mailto; / whom to mail results, may be NULL */#endif char cl_shell; / ordered by size, not in natural order. makes code smaller: / char cl_Dow[7]; / 0-6, beginning sunday / char cl_Mons[12]; / 0-11 / char cl_Hrs[24]; / 0-23 / char cl_Days[32]; / 1-31 / char cl_Mins[60]; / 0-59 */} CronLine;cron 使用方法分析代码 ...

October 23, 2018 · 1 min · jiezi