unix 定时任务执行记录_unix 定时任务不执行 - CSDN
  • 定时任务(cron job)被用于安排那些需要被周期性执行的命令。利用它,你可以配置某些命令或者脚本,让它们在某个设定的时间内周期性地运行。cron 是 Linux 或者类 Unix 系统中最为实用的工具之一。cron 服务...

    定时任务(cron job)被用于安排那些需要被周期性执行的命令。利用它,你可以配置某些命令或者脚本,让它们在某个设定的时间内周期性地运行。cron 是 Linux 或者类 Unix 系统中最为实用的工具之一。cron 服务(守护进程)在系统后台运行,并且会持续地检查 /etc/crontab 文件和 /etc/cron.*/目录。它同样也会检查 /var/spool/cron/ 目录。

    crontab 命令

    crontab 是用来安装、卸载或者列出定时任务列表的命令。cron 配置文件则用于驱动 Vixie Cron 的 cron(8) 守护进程。每个用户都可以拥有自己的 crontab 文件,虽然这些文件都位于 /var/spool/cron/crontabs 目录中,但并不意味着你可以直接编辑它们。你需要通过 crontab 命令来编辑或者配置你自己的定时任务。

    定时配置文件的类型

    配置文件分为以下不同的类型:

    • UNIX 或 Linux 的系统级 crontab : 此类型通常由那些需要 root 或类似权限的系统服务和重要任务使用。第六个字段(见下方的字段介绍)为用户名,用来指定此命令以哪个用户身份来执行。如此一来,系统的 crontab 就能够以任意用户的身份来执行操作。
    • 用户的 crontab: 用户可以使用 crontab 命令来安装属于他们自己的定时任务。 第六个字段为需要运行的命令, 所有的命令都会以创建该 crontab 任务的用户的身份运行。

    注意: 这种问答形式的 Cron 实现由 Paul Vixie 编写而成,并且被包含在许多 Linux 发行版本和类 Unix 系统(如广受欢迎的第四版 BSD)中。它的语法被各种 crond 的实现所兼容。

    那么我该如何安装、创建或者编辑我自己的定时任务呢?

    要编辑你的 crontab 文件,需要在 Linux 或 Unix 的 shell 提示符后键入以下命令:

    $ crontab -e

    crontab 语法(字段介绍)

    语法为:

    1 2 3 4 5 /path/to/command arg1 arg2

    或者

    1 2 3 4 5 /root/ntp_sync.sh

    其中:

    • 第1个字段:分钟 (0-59)
    • 第2个字段:小时 (0-23)
    • 第3个字段:日期 (0-31)
    • 第4个字段:月份 (0-12 [12 代表 December])
    • 第5个字段:一周当中的某天 (0-7 [7 或 0 代表星期天])
    • /path/to/command – 计划执行的脚本或命令的名称

    便于记忆的格式:

    * * * * * 要执行的命令
    ----------------
    | | | | |
    | | | | ---- 周当中的某天 (0 - 7) (周日为 0 或 7)
    | | | ------ 月份 (1 - 12)
    | | -------- 一月当中的某天 (1 - 31)
    | ---------- 小时 (0 - 23)
    ------------ 分钟 (0 - 59)

    简单的 crontab 示例:

    ### 每隔 5 分钟运行一次 backupscript 脚本 ##
    */5 * * * * /root/backupscript.sh
    
    ### 每天的凌晨 1 点运行 backupscript 脚本 ##
    0 1 * * * /root/backupscript.sh
    
    ### 每月的第一个凌晨 3:15 运行 backupscript 脚本 ##
    15 3 1 * * /root/backupscript.sh

    如何使用操作符

    操作符允许你为一个字段指定多个值,这里有三个操作符可供使用:

    • 星号 (*) : 此操作符为字段指定所有可用的值。举个例子,在小时字段中,一个星号等同于每个小时;在月份字段中,一个星号则等同于每月。
    • 逗号 (,) : 这个操作符指定了一个包含多个值的列表,例如:1,5,10,15,20,25.
    • 横杠 (-) : 此操作符指定了一个值的范围,例如:5-15 ,等同于使用逗号操作符键入的 5,6,7,8,9,…,13,14,15。
    • 分隔符 (/) : 此操作符指定了一个步进值,例如: 0-23/ 可以用于小时字段来指定某个命令每小时被执行一次。步进值也可以跟在星号操作符后边,如果你希望命令行每 2 小时执行一次,则可以使用 */2。

    如何禁用邮件输出

    默认情况下,某个命令或者脚本的输出内容(如果有的话)会发送到你的本地邮箱账户中。若想停止接收 crontab 发送的邮件,需要添加 >/dev/null 2>&1 这段内容到执行的命令的后面,例如:

    0 3 * * * /root/backup.sh >/dev/null 2>&1

    如果想将输出内容发送到特定的邮件账户中,比如说 vivek@nixcraft.in 这个邮箱, 则你需要像下面这样定义一个 MAILTO 变量:

    MAILTO="vivek@nixcraft.in"
    0 3 * * * /root/backup.sh >/dev/null 2>&1

    访问 “禁用 Crontab 命令的邮件提示” 查看更多信息。

    任务:列出你所有的定时任务

    键入以下命令:

    # crontab -l
    # crontab -u username -l

    要删除所有的定时任务,可以使用如下命令:

    ### 删除当前定时任务
    crontab -r
    ### 删除某用户名下的定时任务,此命令需以 root 用户身份执行
    crontab -r -u username

    使用特殊字符串来节省时间

    你可以使用以下 8 个特殊字符串中的其中一个替代头五个字段,这样不但可以节省你的时间,还可以提高可读性。

    特殊字符 含义
    @reboot 在每次启动时运行一次
    @yearly 每年运行一次,等同于 “0 0 1 1 *”.
    @annually (同 @yearly)
    @monthly 每月运行一次, 等同于 “0 0 1 * *”.
    @weekly 每周运行一次, 等同于 “0 0 * * 0”.
    @daily 每天运行一次, 等同于 “0 0 * * *”.
    @midnight (同 @daily)
    @hourly 每小时运行一次, 等同于 “0 * * * *”.

    示例:

    每小时运行一次 ntpdate 命令

    @hourly /path/to/ntpdate

    关于 /etc/crontab 文件和 /etc/cron.d/* 目录的更多内容

    /etc/crontab 是系统的 crontab 文件。通常只被 root 用户或守护进程用于配置系统级别的任务。每个单独的用户必须像上面介绍的那样使用 crontab 命令来安装和编辑自己的任务。/var/spool/cron/ 或者 /var/cron/tabs/ 目录存放了个人用户的 crontab 文件,它应该备份在用户的家目录当中。

    理解默认的 /etc/crontab 文件

    典型的 /etc/crontab 文件内容是这样的:

    SHELL=/bin/bash
    PATH=/sbin:/bin:/usr/sbin:/usr/bin
    MAILTO=root
    HOME=/
    # run-parts
    01 * * * * root run-parts /etc/cron.hourly
    02 4 * * * root run-parts /etc/cron.daily
    22 4 * * 0 root run-parts /etc/cron.weekly
    42 4 1 * * root run-parts /etc/cron.monthly

    首先,环境变量必须被定义。如果 SHELL 行被忽略,cron 会使用默认的 sh shell。如果 PATH 变量被忽略,就没有默认的搜索路径,所有的文件都需要使用绝对路径来定位。如果 HOME 变量被忽略,cron 会使用调用者(用户)的家目录替代。

    另外,cron 会读取 /etc/cron.d/目录中的文件。通常情况下,像 sa-update 或者 sysstat 这样的系统守护进程会将他们的定时任务存放在此处。作为 root 用户或者超级用户,你可以使用以下目录来配置你的定时任务。你可以直接将脚本放到这里。run-parts命令会通过 /etc/crontab 文件来运行位于某个目录中的脚本或者程序。

    目录 描述
    /etc/cron.d/ 将所有的脚本文件放在此处,并从 /etc/crontab 文件中调用它们。
    /etc/cron.daily/ 运行需要 每天 运行一次的脚本
    /etc/cron.hourly/ 运行需要 每小时 运行一次的脚本
    /etc/cron.monthly/ 运行需要 每月 运行一次的脚本
    /etc/cron.weekly/ 运行需要 每周 运行一次的脚本

    备份定时任务

    # crontab -l > /path/to/file
    
    # crontab -u user -l > /path/to/file
    展开全文
  • Unix和类Unix的操作系统之中,crontab命令常用于设置周期性被执行的指令,也可以理解为设置定时任务。 crontab中的定时任务有时候没有成功执行,什么原因呢?这时就需要去日志里去分析一下了,那该如何查看...

    在Unix和类Unix的操作系统之中,crontab命令常用于设置周期性被执行的指令,也可以理解为设置定时任务。

    crontab中的定时任务有时候没有成功执行,什么原因呢?这时就需要去日志里去分析一下了,那该如何查看crontab的日志记录呢?

    1. linux

    看 /var/log/cron.log这个文件就可以,可以用 tail -f /var/log/cron 观察

    2. unix

    在 /var/spool/cron/tmp文件中,有croutXXX001864的tmp文件,tail 这些文件就可以看到正在执行的任务了。

    3. mail任务

    在 /var/spool/mail/root 文件中,有crontab执行日志的记录,用tail -f /var/spool/mail/root 即可查看最近的crontab执行情况。


    有朋友问到关于linux的crontab不知道是否到底执行了没有,也算写过一些基本备份的shell脚本,结合自己的实际生产环境简单讲述下如何通过cron执行的日志来分析crontab是否正确执行。

    例如服务器下oracle用户有如下的计划任务

    [oracle@localhost6 ~]$ crontab -l
    00 1 * * 0 /home/oracle/backup/hollyipcc.sh
    00 1 1 * * /home/oracle/backup/hollyreport_hollycrm.sh


    关于系统的计划任务都会先在/var/log

    [root@localhost ~]# cd /var/log/
    [root@localhost log]# less cron
    Sep 22 04:22:01 localhost crond[32556]: (root) CMD (run-parts /etc/cron.weekly)
    Sep 22 04:22:01 localhost anacron[32560]: Updated timestamp for job `cron.weekly' to 2013-09-22
    Sep 22 05:01:01 localhost crond[22768]: (root) CMD (run-parts /etc/cron.hourly)
    Sep 22 06:01:01 localhost crond[25522]: (root) CMD (run-parts /etc/cron.hourly)
    Sep 22 07:01:01 localhost crond[28255]: (root) CMD (run-parts /etc/cron.hourly)
    Sep 22 08:01:01 localhost crond[30982]: (root) CMD (run-parts /etc/cron.hourly)

    上面的/var/log/cron只会记录是否执行了某些计划的脚本,但是具体执行是否正确以及脚本执行过程中的一些信息则linux会每次都发邮件到该用户下。

    如上述oracle的计划任务执行信息,linux会发邮件到/var/spool/mail下面

    [root@localhost6 log]# cd /var/spool/mail/
    [root@localhost6 mail]# less oracle
    Date: Sun, 25 Aug 2013 01:00:01 +0800
    Message-Id: <201308241700.r7OH01aG016679@localhost6.localdomain6>
    From: root@localhost6.localdomain6 (Cron Daemon)
    To: oracle@localhost6.localdomain6
    Subject: Cron /home/oracle/backup/hollyipcc.sh
    Content-Type: text/plain; charset=UTF-8
    Auto-Submitted: auto-generated
    X-Cron-Env:
    X-Cron-Env:
    X-Cron-Env:
    X-Cron-Env:
    X-Cron-Env:
    
    backup hollyipcc
    
    Export: Release 10.2.0.4.0 - Production on Sunday, 25 August, 2013 1:00:02
    
    Copyright (c) 2003, 2007, Oracle. All rights reserved.
    
    Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
    With the Partitioning, OLAP, Data Mining and Real Application Testing options
    Starting "SYSTEM"."SYS_EXPORT_SCHEMA_01": system/******** dumpfile=hollyipcc_20130825.dmp logfile=hollyipcc_20130825.log directory
    =back schemas=hollyipcc parfile=/home/oracle/backup/parfile.par
    Estimate in progress using BLOCKS method...
    Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
    Total estimation using BLOCKS method: 5.932 GB
    Processing object type SCHEMA_EXPORT/TABLE/TABLE
    Processing object type SCHEMA_EXPORT/TABLE/GRANT/OWNER_GRANT/OBJECT_GRANT
    Processing object type SCHEMA_EXPORT/TABLE/INDEX/INDEX
    Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
    Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
    Processing object type SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
    . . exported "HOLLYIPCC"."BASE_CONTACTSTATE_BAK":"P201203" 1.389 GB 15716014 rows

    此时我们就能很好的判断crontab脚本是否执行,已经执行过程中是否正确以及一些错误的信息,希望这个可以帮助有些朋友很好的了解crontab的原理和诊断crontab的问题。

     

     

    展开全文
  • PHP定时任务实现执行

    2019-03-17 23:38:58
    PHP定时运行任务对于一个网站来说,是一个比较重要的任务,比如定时发布文档,定时清理垃圾信息等,现在的网站大多数都是采用PHP动态语言开发的...如果非要PHP去实现定时任务, 可以有以下几种解决方案: 一. 简单...

       PHP定时运行任务对于一个网站来说,是一个比较重要的任务,比如定时发布文档,定时清理垃圾信息等,现在的网站大多数都是采用PHP动态语言开发的,而对于PHP的实现决定了它没有Java和.Net这种AppServer的概念,而http协议是一个无状态的协议,PHP只能被用户触发,被调用,调用后会自动退出内存,没有常驻内存。

    如果非要PHP去实现定时任务, 可以有以下几种解决方案:

    一. 简单直接不顾后果型

    <?php
    ignore_user_abort();//关掉浏览器,PHP脚本也可以继续执行.
    set_time_limit(0);// 通过set_time_limit(0)可以让程序无限制的执行下去
    ini_set('memory_limit','512M'); // 设置内存限制
    $interval=60*30;// 每隔半小时运行
    do{//ToDo 
        sleep($interval);// 等待5分钟
    }while(true);

    缺点: 启动之后,便无法控制, 除非终止 PHP 宿主. 不要采用这样方法, 除非你是黑客.

    二. 简单可控型

    config.php文件
    <?php
    return 1;
    ?>
    
    cron.php文件
    ignore_user_abort();
    //关掉浏览器,PHP脚本也可以继续执行.
    set_time_limit(0);// 通过set_time_limit(0)可以让程序无限制的执行下去
    $interval=60*30;// 每隔半小时运行
    do{
        $run = include 'config.php';
        if(!$run)
            die('process abort');//ToDo
        sleep($interval);// 等待5分钟
    }while(true);

    通过 改变config.php 的 return 0 , 来实现停止程序. 一个可行的办法是config.php文件和某个特殊表单交互, 通过HTML页面设置一些变量来进行配置

    缺点: 占系统资源, 长时间运行,会有一些意想不到的隐患。比如内存管理方面的问题 .

    三. 简单改进型

    <?php
    $time=15;
    $url="http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
    /*function*/ 
    sleep($time);
    file_get_contents($url);
    ?>

    php脚本sleep 一段时间之后通过访问自身的方式继续执行. 就好像接力赛跑一样..这样就能保证每个PHP脚本执行时间不会太长. 也就不受time_out的限制了.

    因为每一次一次循环php文件都是独立执行,所以这种方法,避免了time_out的限制. 但是最好和上边一样 加上控制代码. cofig.php , 以便能够终止进程.

    四. 服务器定时任务

    Unix平台

        如果您使用 Unix 系统,您需要在您的 PHP 脚本的最前面加上一行特殊的代码,使得它能够被执行,这样系统就能知道用什么样的程序要运行该脚本。为 Unix 系统增加的第一行代码不会影响该脚本在 Windows 下的运行,因此您也可以用该方法编写跨平台的脚本程序。

    1、在Crontab中使用PHP执行脚本

    就像在Crontab中调用普通的shell脚本一样(具体Crontab用法),使用PHP程序来调用PHP脚本,每一小时执行 myscript.php 如下:

    # crontab -e
    00 * * * * /usr/local/bin/php /home/john/myscript.php

    /usr/local/bin/php为PHP程序的路径。

    2、在Crontab中使用URL执行脚本

    如果你的PHP脚本可以通过URL触发,你可以使用 lynx 或 curl 或 wget 来配置你的Crontab。

    下面的例子是使用Lynx文本浏览器访问URL来每小时执行PHP脚本。Lynx文本浏览器默认使用对话方式打开URL。但是,像下面的,我们在lynx命令行中使用-dump选项来把URL的输出转换来标准输出。

    00 * * * * lynx -dump http://www.sf.net/myscript.php

    下面的例子是使用 CURL 访问URL来每5分执行PHP脚本。Curl默认在标准输出显示输出。使用 "curl -o" 选项,你也可以把脚本的输出转储到临时文件temp.txt。

    */5 * * * * /usr/bin/curl -o temp.txt http://www.sf.net/myscript.php

    下面的例子是使用WGET访问URL来每10分执行PHP脚本。-q 选项表示安静模式。"-O temp.txt" 表示输出会发送到临时文件。

    */10 * * * * /usr/bin/wget -q -O temp.txt http://www.sf.net/myscript.php

    五. ini_set函数用法详解

     ini_set用来设置php.ini的值,在函数执行的时候生效,脚本结束后,设置失效。无需打开php.ini文件,就能修改配置,对于虚拟空间来说,很方便。

    函数格式:

    string ini_set(string $varname, string $newvalue)

    不是所有的参数都可以配置,可以查看手册中的列表。

    常见的设置:

    @ ini_set('memory_limit', '64M');

    menory_limit:设定一个脚本所能够申请到的最大内存字节数,这有利于写的不好的脚本消耗服务器上的可用内存。@符号代表不输出错误。

    @ini_set('display_errors', 1);

    display_errors:设置错误信息的类别。

    @ini_set('session.auto_start', 0);

    session.auto_start:是否自动开session处理,设置为1时,程序中不用session_start()来手动开启session也可使用session,

    如果参数为0,又没手动开启session,则会报错。

    @ini_set('session.cache_expire', 180);

    session.cache_expire:指定会话页面在客户端cache中的有限期(分钟)缺省下为180分钟。如果设置了session.cache_limiter=nocache时,qq好友列表获取此处设置无 效。

    @ini_set('session.use_cookies', 1);

    session.use_cookies:是否使用cookie在客户端保存会话ID;

    @ini_set('session.use_trans_sid', 0);

    session.use_trans_sid:是否使用明码在URL中显示SID(会话ID),

        默认是禁止的,因为它会给你用户带来安全危险:

        用户可能将包含有效的sid的URL通过email/irc/QQ/MSN等途径告诉其他人。

        包含有效sid的URL可能会保存在公用电脑上。

        用户可能保存带有固定不变的SID的URL在他们的收藏夹或者浏览历史记录里。 基于URL的会话管理总是比基于Cookie的会话管理有更多的风险,所以应当禁用。

    展开全文
  • 1、原理分析 在java中常见的定时调度方案有:ScheduledExecutorService和quartz两种方案。其本质上都是通过native的wait方法来实现...Quartz主要的执行类和执行方法。 其主要流程如下: public class QuartzSche...

    1、原理分析

    在java中常见的定时调度方案有:ScheduledExecutorService和quartz两种方案。其本质上都是通过native的wait方法来实现的.

    quartz定时调度是通过Object.wait方式(native方法)实现的,其本质是通过操作系统的时钟来实现的。Quartz主要的执行类和执行方法。
    其主要流程如下:

    public class QuartzSchedulerThread extends Thread{
       public void run() {
            boolean lastAcquireFailed = false;
    
            while (!halted.get()) {
                try {
                    // check if we're supposed to pause...
                    synchronized (sigLock) {
                        while (paused && !halted.get()) {
                            try {
                                // wait until togglePause(false) is called...
                                sigLock.wait(1000L);
                            } catch (InterruptedException ignore) {
                            }
                        }
    
                        if (halted.get()) {
                            break;
                        }
                    }
    
                    int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
                    if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...
    
                        List<OperableTrigger> triggers = null;
    
                        long now = System.currentTimeMillis();
    
                        clearSignaledSchedulingChange();
                        try {
                            triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                                    now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
                            lastAcquireFailed = false;
                            if (log.isDebugEnabled()) 
                                log.debug("batch acquisition of " + (triggers == null ? 0 : triggers.size()) + " triggers");
                        } catch (JobPersistenceException jpe) {
                            if(!lastAcquireFailed) {
                                qs.notifySchedulerListenersError(
                                    "An error occurred while scanning for the next triggers to fire.",
                                    jpe);
                            }
                            lastAcquireFailed = true;
                            continue;
                        } catch (RuntimeException e) {
                            if(!lastAcquireFailed) {
                                getLog().error("quartzSchedulerThreadLoop: RuntimeException "
                                        +e.getMessage(), e);
                            }
                            lastAcquireFailed = true;
                            continue;
                        }
    
                        if (triggers != null && !triggers.isEmpty()) {
    
                            now = System.currentTimeMillis();
                            long triggerTime = triggers.get(0).getNextFireTime().getTime();
                            long timeUntilTrigger = triggerTime - now;
                            while(timeUntilTrigger > 2) {
                                synchronized (sigLock) {
                                    if (halted.get()) {
                                        break;
                                    }
                                    if (!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {
                                        try {
                                            // we could have blocked a long while
                                            // on 'synchronize', so we must recompute
                                            now = System.currentTimeMillis();
                                            timeUntilTrigger = triggerTime - now;
                                            if(timeUntilTrigger >= 1)
                                                sigLock.wait(timeUntilTrigger);
                                        } catch (InterruptedException ignore) {
                                        }
                                    }
                                }
                                if(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) {
                                    break;
                                }
                                now = System.currentTimeMillis();
                                timeUntilTrigger = triggerTime - now;
                            }
    
                            // this happens if releaseIfScheduleChangedSignificantly decided to release triggers
                            if(triggers.isEmpty())
                                continue;
    
                            // set triggers to 'executing'
                            List<TriggerFiredResult> bndles = new ArrayList<TriggerFiredResult>();
    
                            boolean goAhead = true;
                            synchronized(sigLock) {
                                goAhead = !halted.get();
                            }
                            if(goAhead) {
                                try {
                                    List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
                                    if(res != null)
                                        bndles = res;
                                } catch (SchedulerException se) {
                                    qs.notifySchedulerListenersError(
                                            "An error occurred while firing triggers '"
                                                    + triggers + "'", se);
                                    //QTZ-179 : a problem occurred interacting with the triggers from the db
                                    //we release them and loop again
                                    for (int i = 0; i < triggers.size(); i++) {
                                        qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                    }
                                    continue;
                                }
    
                            }
    
                            for (int i = 0; i < bndles.size(); i++) {
                                TriggerFiredResult result =  bndles.get(i);
                                TriggerFiredBundle bndle =  result.getTriggerFiredBundle();
                                Exception exception = result.getException();
    
                                if (exception instanceof RuntimeException) {
                                    getLog().error("RuntimeException while firing trigger " + triggers.get(i), exception);
                                    qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                    continue;
                                }
    
                                // it's possible to get 'null' if the triggers was paused,
                                // blocked, or other similar occurrences that prevent it being
                                // fired at this time...  or if the scheduler was shutdown (halted)
                                if (bndle == null) {
                                    qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                    continue;
                                }
    
                                JobRunShell shell = null;
                                try {
                                    shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
                                    shell.initialize(qs);
                                } catch (SchedulerException se) {
                                    qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
                                    continue;
                                }
    
                                if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
                                    // this case should never happen, as it is indicative of the
                                    // scheduler being shutdown or a bug in the thread pool or
                                    // a thread pool being used concurrently - which the docs
                                    // say not to do...
                                    getLog().error("ThreadPool.runInThread() return false!");
                                    qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
                                }
    
                            }
    
                            continue; // while (!halted)
                        }
                    } else { // if(availThreadCount > 0)
                        // should never happen, if threadPool.blockForAvailableThreads() follows contract
                        continue; // while (!halted)
                    }
    
                    long now = System.currentTimeMillis();
                    long waitTime = now + getRandomizedIdleWaitTime();
                    long timeUntilContinue = waitTime - now;
                    synchronized(sigLock) {
                        try {
                          if(!halted.get()) {
                            // QTZ-336 A job might have been completed in the mean time and we might have
                            // missed the scheduled changed signal by not waiting for the notify() yet
                            // Check that before waiting for too long in case this very job needs to be
                            // scheduled very soon
                            if (!isScheduleChanged()) {
                              sigLock.wait(timeUntilContinue);
                            }
                          }
                        } catch (InterruptedException ignore) {
                        }
                    }
    
                } catch(RuntimeException re) {
                    getLog().error("Runtime error occurred in main trigger firing loop.", re);
                }
            } // while (!halted)
    
            // drop references to scheduler stuff to aid garbage collection...
            qs = null;
            qsRsrcs = null;
        }
    
    }
    • 获取最近需要执行的任务列表(30s内的将要执行的任务)。然后根据执行时间进行排序,然后计算出需要wait()的时间。当调度时间来临,欢迎主线程,将任务交给一个线程池进行执行。
    triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                                    now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
    • 30秒内没有需要执行的任务,则等待一个随机时间。getRandomizedIdleWaitTime产生一个30秒内随机等待时间。
    long now = System.currentTimeMillis();
                    long waitTime = now + getRandomizedIdleWaitTime();
                    long timeUntilContinue = waitTime - now;
                    synchronized(sigLock) {
                        try {
                          if(!halted.get()) {
                            // QTZ-336 A job might have been completed in the mean time and we might have
                            // missed the scheduled changed signal by not waiting for the notify() yet
                            // Check that before waiting for too long in case this very job needs to be
                            // scheduled very soon
                            if (!isScheduleChanged()) {
                              sigLock.wait(timeUntilContinue);
                            }
                          }
                        } catch (InterruptedException ignore) {
                        }
                    }
    

    2.quartz-db定时调度执行过程分析

    2.1 执行流程图

    在QuartzSchedulerThread主线程中,首先会从QRTZ_TRIGGERS表中取出最近30秒内将要执行的任务,然后等待executeTime-now时间,然后在等待唤醒时交给线程池处理。当任务执行完成时,会通过事件机制,更新QRTZ_TRIGGERS中的nextFireTime。在每次获取QRTZ_TRIGGERS最近30秒的Trigger时,都会先对QRTZ_LOCKS表中的Trigger行进行加锁,从而保证了一个任务只会在分布式环境中的一台机器上执行。
    这里写图片描述

    2.2 结合DB分析执行过程

    • QRTZ_TRIGGERS:用于存储接下来需要进行调度的triiger.

    SELECT TRIGGER_NAME, TRIGGER_GROUP, NEXT_FIRE_TIME, PRIORITY FROM QRTZ_TRIGGERS WHERE SCHED_NAME = ‘quartzScheduler’ AND TRIGGER_STATE = ‘WAITING’ AND NEXT_FIRE_TIME <= 1506322290680 AND (MISFIRE_INSTR = -1 OR (MISFIRE_INSTR != -1 AND NEXT_FIRE_TIME >= 1506322375502)) ORDER BY NEXT_FIRE_TIME ASC, PRIORITY DESC

    例如因为机器重启被中断的trigger
    (该trigger是在机器启动时从QRTZ_FIRED_TRIGGER扫描到的记录,都会加入到QRTZ_TRIGGERS等待进一步调用,其NEXT_FIRE_TIME为空,表示优先级最高)

    mysql> select TRIGGER_NAME,TRIGGER_GROUP,from_unixtime(NEXT_FIRE_TIME/1000) from QRTZ_TRIGGERS;
    +----------------------------------------------------------------+-----------------+------------------------------------+
    | TRIGGER_NAME                                                   | TRIGGER_GROUP   | from_unixtime(NEXT_FIRE_TIME/1000) |
    +----------------------------------------------------------------+-----------------+------------------------------------+
    | recover_caowenyideMacBook-Pro.local1506316013536_1506322246054 | RECOVERING_JOBS | 2017-09-25 14:48:30.0070           |
    | sampleJobCronTrigger                                           | DEFAULT         | 2017-09-25 14:56:45.8750           |
    +----------------------------------------------------------------+-----------------+------------------------------------+

    QRTZ_FIRED_TRIGGERS: 记录正在进行调度的TRIIGER
    INSERT INTO QRTZ_FIRED_TRIGGERS (SCHED_NAME, ENTRY_ID, TRIGGER_NAME, TRIGGER_GROUP, INSTANCE_NAME, FIRED_TIME, SCHED_TIME, STATE, JOB_NAME, JOB_GROUP, IS_NONCONCURRENT, REQUESTS_RECOVERY, PRIORITY) VALUES(‘quartzScheduler’, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

    mysql> select TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE from QRTZ_TRIGGERS;
    +----------------------------------------------------------------+-----------------+---------------+
    | TRIGGER_NAME                                                   | TRIGGER_GROUP   | TRIGGER_STATE |
    +----------------------------------------------------------------+-----------------+---------------+
    | recover_caowenyideMacBook-Pro.local1506316013536_1506322246054 | RECOVERING_JOBS | ACQUIRED      |
    | sampleJobCronTrigger                                           | DEFAULT         | WAITING       |
    +----------------------------------------------------------------+-----------------+---------------+
    
    mysql> select TRIGGER_NAME,TRIGGER_GROUP from QRTZ_FIRED_TRIGGERS;
    +----------------------------------------------------------------+-----------------+
    | TRIGGER_NAME                                                   | TRIGGER_GROUP   |
    +----------------------------------------------------------------+-----------------+
    | recover_caowenyideMacBook-Pro.local1506316013536_1506322246054 | RECOVERING_JOBS |
    +----------------------------------------------------------------+-----------------+
    

    SELECT * FROM QRTZ_LOCKS WHERE SCHED_NAME = ‘quartzScheduler’ AND LOCK_NAME = ? FOR UPDATE
    INSERT INTO QRTZ_LOCKS(SCHED_NAME, LOCK_NAME) VALUES (‘quartzScheduler’, ?)
    //获取锁失败直接抛异常,保证了只会有一个任务会获得锁

    mysql> select * from QRTZ_LOCKS;
    +-----------------+----------------+
    | SCHED_NAME      | LOCK_NAME      |
    +-----------------+----------------+
    | quartzScheduler | STATE_ACCESS   |
    | quartzScheduler | TRIGGER_ACCESS |
    +-----------------+----------------+
    • 执行任务
      job = sched.getJobFactory().newJob(firedTriggerBundle, scheduler);
    • 任务执行完成(事件机制)
      DELETE FROM QRTZ_TRIGGERS WHERE SCHED_NAME = ‘quartzScheduler’ AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?
      DELETE FROM QRTZ_FIRED_TRIGGERS WHERE SCHED_NAME = ‘quartzScheduler’ AND ENTRY_ID = ?

    • 并且更新下次要调度的trigger时间
      UPDATE QRTZ_TRIGGERS SET JOB_NAME = ?, JOB_GROUP = ?, DESCRIPTION = ?, NEXT_FIRE_TIME = ?, PREV_FIRE_TIME = ?, TRIGGER_STATE = ?, TRIGGER_TYPE = ?, START_TIME = ?, END_TIME = ?, CALENDAR_NAME = ?, MISFIRE_INSTR = ?, PRIORITY = ?, JOB_DATA = ? WHERE SCHED_NAME = ‘quartzScheduler’ AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?

    mysql> select TRIGGER_NAME,from_unixtime(NEXT_FIRE_TIME/1000),from_unixtime(PREV_FIRE_TIME/1000),from_unixtime(START_TIME/1000),TRIGGER_STATE from QRTZ_TRIGGERS;
    +----------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | TRIGGER_NAME         | from_unixtime(NEXT_FIRE_TIME/1000) | from_unixtime(PREV_FIRE_TIME/1000) | from_unixtime(START_TIME/1000) | TRIGGER_STATE |
    +----------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | sampleJobCronTrigger | 2017-09-25 15:45:00.0000           | 2017-09-25 15:40:00.0000           | 2017-09-25 11:55:02.0000       | WAITING       |
    +----------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    1 row in set (0.00 sec)

    PS:QRTZ_TRIGGERS每一个任务对应一条记录

    mysql> select TRIGGER_NAME,from_unixtime(NEXT_FIRE_TIME/1000),from_unixtime(PREV_FIRE_TIME/1000),from_unixtime(START_TIME/1000),TRIGGER_STATE from QRTZ_TRIGGERS;
    +------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | TRIGGER_NAME           | from_unixtime(NEXT_FIRE_TIME/1000) | from_unixtime(PREV_FIRE_TIME/1000) | from_unixtime(START_TIME/1000) | TRIGGER_STATE |
    +------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | sampleJobCronTrigger   | 2017-09-25 16:05:00.0000           | 2017-09-25 16:00:00.0000           | 2017-09-25 11:55:02.0000       | WAITING       |
    | sampleJobV2CronTrigger | 2017-09-25 16:05:00.0000           | 2017-09-25 16:00:00.0000           | 2017-09-25 15:57:17.0000       | WAITING       |
    +------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    2 rows in set (0.00 sec)

    案例1:线上有个任务有问题,立即kill掉jvm,但是机器重启后任务还是会执行,怎么解决?
    答案:在机器重启前删除掉QRTZ_FIRED_TRIGGERS中对应的记录

    数据库截图如下:

    mysql> select * from QRTZ_FIRED_TRIGGERS;;
    +-----------------+-------------------------------------------------------+----------------------+---------------+------------------------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+
    | SCHED_NAME      | ENTRY_ID                                              | TRIGGER_NAME         | TRIGGER_GROUP | INSTANCE_NAME                            | FIRED_TIME    | SCHED_TIME    | PRIORITY | STATE     | JOB_NAME        | JOB_GROUP | IS_NONCONCURRENT | REQUESTS_RECOVERY |
    +-----------------+-------------------------------------------------------+----------------------+---------------+------------------------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+
    | quartzScheduler | caowenyideMacBook-Pro.local15063261797261506326179669 | sampleJobCronTrigger | DEFAULT       | caowenyideMacBook-Pro.local1506326179726 | 1506327900007 | 1506327900000 |        5 | EXECUTING | sampleJobDetail | DEFAULT   | 1                | 1                 |
    +-----------------+-------------------------------------------------------+----------------------+---------------+------------------------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    mysql> select TRIGGER_NAME,from_unixtime(NEXT_FIRE_TIME/1000),from_unixtime(PREV_FIRE_TIME/1000),from_unixtime(START_TIME/1000),TRIGGER_STATE from QRTZ_TRIGGERS;
    +------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | TRIGGER_NAME           | from_unixtime(NEXT_FIRE_TIME/1000) | from_unixtime(PREV_FIRE_TIME/1000) | from_unixtime(START_TIME/1000) | TRIGGER_STATE |
    +------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | sampleJobCronTrigger   | 2017-09-25 16:30:00.0000           | 2017-09-25 16:25:00.0000           | 2017-09-25 11:55:02.0000       | BLOCKED       |
    | sampleJobV2CronTrigger | 2017-09-25 16:30:00.0000           | 2017-09-25 16:25:00.0000           | 2017-09-25 16:18:00.0000       | WAITING       |
    +------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    2 rows in set (0.00 sec)
    
    mysql> select TRIGGER_NAME,from_unixtime(NEXT_FIRE_TIME/1000),from_unixtime(PREV_FIRE_TIME/1000),from_unixtime(START_TIME/1000),TRIGGER_STATE from QRTZ_TRIGGERS;
    +----------------------------------------------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | TRIGGER_NAME                                                   | from_unixtime(NEXT_FIRE_TIME/1000) | from_unixtime(PREV_FIRE_TIME/1000) | from_unixtime(START_TIME/1000) | TRIGGER_STATE |
    +----------------------------------------------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    | recover_caowenyideMacBook-Pro.local1506326179726_1506328016310 | NULL                               | 2017-09-25 16:25:00.0000           | 2017-09-25 16:25:00.0000       | COMPLETE      |
    | sampleJobCronTrigger                                           | 2017-09-25 16:30:00.0000           | 2017-09-25 16:25:00.0000           | 2017-09-25 11:55:02.0000       | BLOCKED       |
    | sampleJobV2CronTrigger                                         | 2017-09-25 16:30:00.0000           | 2017-09-25 16:25:00.0000           | 2017-09-25 16:18:00.0000       | WAITING       |
    +----------------------------------------------------------------+------------------------------------+------------------------------------+--------------------------------+---------------+
    3 rows in set (0.00 sec)
    
    展开全文
  • ubuntu定时执行任务

    2017-11-25 09:31:26
    前几天女神给我发了条消息,“为什么我...ubuntu定时执行脚本cron是一个Linux下的后台进程,用来定期的执行一些任务。因为我用的是Ubuntu,所以这篇文章中的所有命令也只能保证在Ubuntu下有效。查看cron是否运行ps -
  • crontab定时任务和日志

    2020-07-30 09:46:10
    Unix和类Unix的操作系统之中,crontab命令常用于设置周期性被执行的指令,也可以理解为设置定时任务。 1.安装cron apt-get install cron 设置crontab定时任务 crontab -e */1 * * * * /usr/bin/python /python/...
  • 1、linux 看 /var/log/cron这个文件就可以,可以用tail -f /var/log/cron观察 2、unix 在 /var/spool/cron/tmp文件中,有croutXXX001864的...在 /var/spool/mail/root 文件中,有crontab执行日志的记录,用tail -...
  • 首先编写一个.sh可执行文件 创建:vi test.sh 具体里边写什么内容就看各自的需求了 默认创建的这个.sh文件是没有权限的 chmod +x test.sh 授权之后如果提示 nohup: failed to run command ‘./startup.sh...
  • 用于统一管理项目中的定时任务,提供可视化配置界面、执行日志记录、邮件通知等功能,无需依赖*unix下的crontab服务。 项目背景 开发此项目是为了解决本人所在公司的PHP项目中定时任务繁多,使用crontab不好管理的...
  • crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成操作系统后,默认会...# 一、用户任务调度:属于用户定时任务,用户定期要执行的工作,比如用户数据备份、定时邮...
  • 1、windows 的计划任务2、linux的脚本程序3、让web浏览器定时刷新 具体实现 1、windows计划任务 PHP很少在win服务器上跑,具体实现也不再深究,看网上实现的原理大概是写bat脚本,然后让window任务添加执行
  • 1.定时任务执行原理在java中常见的定时调度方案有:ScheduledExecutorService和quartz两种方案。其本质上都是通过native的wait方法来实现的.1.1 ScheduledExecutorService定时执行原理ScheduledExecutorService的...
  • 最近项目中经常用到队列和定时任务及线程的整合应用,涉及的场景是当多人访问系统时需要回调客户系统处理结果时,如何减少服务器压力并能处理业务需求,这里用到了队列减少服务器压力加入定时任务发送机制,使用的是...
  • 实现方式:第一种是利用Mysql本身实现,制定event定时任务,可以借助Navicat For Mysql或其他数据库开发工具去做;第二种实现方式是利用linux的定时任务,这个是利用Linux系统自带任务管理工具实现的。一、Mysql中...
  • LNMP定时任务

    2019-08-04 02:34:42
    crontab:是在unix/linux上定时执行某个任务的程序  crond服务:是一个定时执行的服务,可以通过crontab 命令添加或者编辑需要定时执行的任务,crond是工具,是linux服务项。 下面开始设置定时任务 1,创建定时...
  • UNIX下怎样实现和Windows下“计划任务”一样的功能$crontab -e 编辑脚本$crontab -l 察看脚本 用$crontab -e 编辑脚本,加入下列行:分 小时 星期 月 命令 Linux下crontab命令的用法任务调度的crond常驻命令...
  • 就像再windows上有计划任务一样,centos7 自然也有计划任务,而且设置更为灵活好用。在centos7 上可以利用系统自带默认运行着的服务crond,通过配置crontab来配置执行计划任务。crond命令每分锺会定期检查是否有要...
  • fileclear.sh tamcdir=${HOME}/ora/user_projects/domains/tamc cd ${tamcdir} echo rm -f `ls heapdump*.phd` rm -f heapdump*.phd echo rm -f `ls javacore*.txt` rm -f javacore*.txt echo rm -f `ls Snap*.
  • 使用PeterKottas.DotNetCore.WindowsService开发的定时任务,之前一直部署在windows服务器下面。最近开始迁移至linux环境下(centOS 7.6),这里使用Supervisor进行部署,也可以使用docker哦。接下来记录了本次安装...
  • 在项目的票务相关的系统服务器,由于几次没有及时情况系统日志造成了严重影响,所以服务器必须定期... 在Java开中常用的Quartz的Cron定义,和linux/unix中系统Cron定义差不多。但是在Quartz中Cron第一位应该是秒的...
1 2 3 4 5 ... 20
收藏数 11,500
精华内容 4,600
关键字:

unix 定时任务执行记录