精华内容
下载资源
问答
  • Oracle 发送QQ邮件监控定时任务

    千次阅读 2019-01-23 12:10:02
    Oracle 中要对job的日志监控 /对定时任务监控 开发中我们免不了要做一些定时任务来同步数据。 【建议使用工具:Oracle SQLdeveloper】 【你需要有数据库的dba权限。能够解锁表的查询权限,否则可能你无法实现这个...

    本文链接https://blog.csdn.net/qq_36237672/article/details/86607865

    Oracle 中要对job的日志监控 /对定时任务监控

     Oracle数据库开发中免不了要做一些定时任务来同步数据/监控数据。

    【建议使用工具:Oracle SQL developer】

    【你需要有数据库的dba权限。能够解锁表的查询权限,否则可能你无法实现这个监控过程。如果你符合这个条件>>继续吧!】

    一、原理

              通过查询job日志  来实现对job日志进行监控。因为job可能会跑死,他不会提示你job没跑。所以只能对job的状态进行检查,查看job的状态STATUS是否为FAILED(FAILED:job跑死了,SUCCEEDE:成功了)。

              查询错误的job,获得报错结果>>发送邮件。

    二、我们需要写的模块:只需要写两个存储过程

      1.获取需要监控的job日志 存储过程(一般取STATUS就可以搞定,需要进一步信息可取ADDITIONAL_INFO字段内容

      2.发送邮件 存储过程

    【获取job日志信息语句】

    --这两个表都可以查询,第二个表可以查询到详细的报错信息。看你需要
    select * from DBA_SCHEDULER_JOB_LOG;
    
    select * from dba_scheduler_job_run_details;

    【查询指定用户下的指定job日志】

    SELECT * 
    FROM dba_scheduler_job_run_details
    where OWNER='用户名'
    AND JOB_NAME='job名';
    
    --ADDITIONAL_INFO 这个字段是报错信息,job正常则为null ,执行报错会有报错日志。可取这个信息来发---详细的邮件报告。
    --STATUS 这个字段是执行的状态 SUCCEEDED:成功   FAILED:失败  (大写)

     

    【存储过程中 编译报错处理:错误(19,12): PL/SQL: ORA-00942: table or view does not exist
     

    --先使用普通用户登录数据库然后执行下面语句,输入密码。不知道密码?开头呢就已经说了,不知道是无法编译通过的。
    
    SQL> conn sys as sysdba
    
    grant select on dba_scheduler_job_run_details to 用户名;
    
    --提示 grant succeeded 授权成功,存储过程就可以编译通过了。
    --执行此句,发现报错。建议手敲代码重新执行。一般手敲的没什么问题,别忘了结束符 ;

    三、我们先创建程序包

    create or replace PACKAGE MONITOR_JOB Authid Current_User AS 
        PROCEDURE JOB_ERROR_MESSAGE;--监控job
        ---发送邮件(我直接使用QQ了,你也可以使用自己公司邮件)
        PROCEDURE sendQQ_mail(p_recipient VARCHAR2,p_subject VARCHAR2,p_message VARCHAR2);
    END MONITOR_JOB;

    四、创建主体】(这里只监控了一个job,方便理解)

    邮件发送参考别人写的,简单好用,直接拿来用了,后面有参考地址。网上有很多,有些功能写得很全面的。

    以下内容如不懂SMTP没关系,后面会有说明。

    create or replace PACKAGE BODY MONITOR_JOB 
    AS
    
      PROCEDURE JOB_ERROR_MESSAGE AS
      JOB_SQL VARCHAR2(2000);
      JOB_STATUS VARCHAR2(25);
      ERROR_MESS VARCHAR2(2000);
      BEGIN
        ---监控job
        --获取job状态(我的job每天凌晨跑一次,这个存储过程早上6点跑。)
        --为什么要max? 因为防止job当天没有执行/还在执行,那么job没有日志,查询为空。会报错
        SELECT max(t.STATUS) into JOB_STATUS 
        FROM DBA_SCHEDULER_JOB_LOG t 
        where t.OWNER='用户名' AND t.JOB_NAME='job名' AND t.LOG_DATE>TRUNC(SYSDATE);
        
        if JOB_STATUS='FAILED'--如果失败发送邮件,还可以判断空。pu'huo
        then
           
          SELECT ADDITIONAL_INFO into ERROR_MESS ---获取报错信息
          FROM dba_scheduler_job_run_details t 
          where t.OWNER='用户名' AND t.JOB_NAME='job名' AND t.LOG_DATE>TRUNC(SYSDATE);
    
          sendQQ_mail('qq邮箱','邮件标题',ERROR_MESS);---调用邮件发送存过
        end if;
    
    END JOB_ERROR_MESSAGE;
    
    PROCEDURE sendQQ_mail(
           p_recipient VARCHAR2, -- 邮件接收人
           p_subject   VARCHAR2, -- 邮件标题
           p_message   VARCHAR2  -- 邮件正文
     )
     IS
          v_mailhost  VARCHAR2(30) := '14.18.245.164'; --SMTP服务器地址(域名无效,必须IP地址)
          v_user      VARCHAR2(30) := 'XXXXXX@qq.com'; --登录SMTP服务器的用户名
          v_pass      VARCHAR2(20) := 'SMTP授权码';      --登录SMTP服务器的密码
          v_sender    VARCHAR2(50) := 'XXXXXX@qq.com'; -发送者邮箱
          
          v_conn  UTL_SMTP.connection ;
          v_msg varchar2(4000);
     BEGIN
          v_conn := UTL_SMTP.open_connection(v_mailhost, 25);
          UTL_SMTP.ehlo(v_conn, v_mailhost); 
    
          UTL_SMTP.command(v_conn, 'AUTH LOGIN' );
          UTL_SMTP.command(v_conn,UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(v_user))));
          UTL_SMTP.command(v_conn,UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(v_pass))));
    
          UTL_SMTP.mail(v_conn, v_sender);
          UTL_SMTP.rcpt(v_conn, p_recipient);
    
          v_msg := 'Date:' || TO_CHAR(SYSDATE, 'dd mon yy hh24:mi:ss' )
              || UTL_TCP.CRLF || 'From: ' || '<' || v_sender || '>'
              || UTL_TCP.CRLF || 'To: ' || '<' || p_recipient || '>'
              || UTL_TCP.CRLF || 'Subject: ' || p_subject
              || UTL_TCP.CRLF || UTL_TCP.CRLF
              || p_message;
              
          UTL_SMTP.open_data(v_conn);
          UTL_SMTP.write_raw_data(v_conn, UTL_RAW.cast_to_raw(v_msg));
          UTL_SMTP.close_data(v_conn);
          UTL_SMTP.quit(v_conn);
     EXCEPTION
          WHEN OTHERS THEN
              DBMS_OUTPUT.put_line(DBMS_UTILITY.format_error_stack);
              DBMS_OUTPUT.put_line(DBMS_UTILITY.format_call_stack);
     END sendQQ_mail;
    
    END MONITOR_JOB;

    注意:这里很重要,上面看不懂的看这里】

    STMP服务器地址:百度>>常用邮箱SMTP服务器地址 有很多
    这里我只贴QQ的:
    
    QQ邮箱(mail.qq.com)
    POP3服务器地址:pop.qq.com(端口:110)
    SMTP服务器地址:smtp.qq.com(端口:25)
    
    当然了,SMTP服务器地址直接填  smtp.qq.com 是无效滴。下面就是度娘得强大之处了
    
    【百度:smtp.qq.com IP地址】或者 https://site.ip138.com/smtp.qq.com/  获取IP地址
     下图贴了smtp.qq.com 地址。
    
    

    IP很多看你心情选。想要哪个要哪个。

    ---------------------------------------------------------------------------------------------------------------

    SMTP服务器地址我们已经搞定了,接下来是登录SMTP服务器用户名密码
    SMTP服务器用户名就是QQ号。密码不是QQ密码!   而是邮件的SMTP授权码。获取SMTP授权码看以下步骤:

    登录QQ邮箱>>设置>>账户>>开启

    点击开启,会有提示让你发短信,然后给你授权码。我这里已经开启了,就不演示了。

    接下来就是调用 :先测试能否发送QQ邮件。然后再试试发送错误告警。

     

    存储过程发送邮件参考该博主:https://blog.csdn.net/u010759175/article/details/54897378 

     

     

    展开全文
  • windows本地监控任务cron定时任务软件.本地监控软件,可设置定时访问指定网址
  • python 定时监控任务

    2019-02-21 16:20:30
    python 脚本代码,功能是定时监控任务,当然用crontab命令更优
  • VC定时任务及线程监控,可指定需要监控的特定应用程序,可以定时开启/关闭进程,监视线程是否停止运行,根据线程情况重启计算机
  • cronmon 定时任务执行状态监控

    千次阅读 2018-11-29 15:37:25
    cronmon是一个计划任务(定时任务监控系统,可以对循环执行的程序和脚本进行监控告警,当其未按照预期执行时,发送邮件到对应邮箱进行通知。同时可以将监控任务划分到不同业务下面,每个业务可以分配不同的通知人...

    cronmon是一个计划任务(定时任务)监控系统,可以对循环执行的程序和脚本进行监控告警,当其未按照预期执行时,发送邮件到对应邮箱进行通知。同时可以将监控任务划分到不同业务下面,每个业务可以分配不同的通知人,建立业务、通知人和监控任务的多层级关系。

    通过以一定的间隔发送HTTPS请求到特定的URL实现监控。如果URL未按时接受到请求,对应的业务通知人则会收到告警。

    也就是说,你可以在你的计划任务或者程序脚本中,根据执行结果进行条件判断,如果成功则发送请求到cronmon,这样的任务可以包括数据库备份、安全扫描、数据同步等任何你认为重要的,需要保证其在指定时间间隔完成的任务。

    主要功能介绍

    • 登录;

    image

    • 登录之后,看到的是首页:包括业务、通知人、任务和日志相关汇总信息,以及图表展示一段时间API请求数据;

    image

    • 任务管理相关操作:这里是系统最核心的功能,监控任务管理,包括新建,编辑,删除,监控日志查看。;

    image

    image

    image

    • 系统管理相关操作:系统用户角色分为二种,拥有所有权限的系统管理员和绑定若干业务的业务管理员。在这里,你可以新建、编辑、删除用户以及业务权限分配;

    image

    image

    image

    image

    API调用

    目前API仅针对监控任务,包括获取所有监控任务和基于关键字进行的任务过滤,json格式返回。

    • 获取所有监控任务

    image

    • 按指定关键字获取任务(业务名、任务名和任务URL)

    image

    image

    image

    公共参数包括page(显示第几页)和length(每页显示多少记录)

    如何使用监控链接

    在不同情况下调用任务监控URL的写法

    • crontab写法
    # system status check
    20 * * * * cd /path/to/systemStatusCheck && ./systemStatusCheck.sh > systemStatusCheck.sh.cron.log 2>&1 && curl -kfsS --retry 3 --connect-timeout 10 --ipv4 https://cronmon.yoursite.io/api/monlink/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx >> systemStatusCheck.sh.cron.log 2>&1
    
    • bash写法
    $ curl -kfsS --retry 3 --connect-timeout 10 --ipv4 https://cronmon.yoursite.io/api/monlink/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    
    • python写法
    >>> import requests
    >>> requests.get('https://cronmon.yoursite.io/api/monlink/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')
    

    生产环境

    本部分是针对生产环境一些额外的配置优化,包括日志表自动分区脚本、程序管理服务化(linux)、nginx配置和日志轮转配置。

    • 服务启停

    以CentOS为例,首先编辑相关变量

    image

    然后将启动脚本(production/init.d/cronmon)放入/etc/init.d目录

    $ service cronmon help
    Usage: cronmon {start|stop|restart|reload|status|help}
    $ chkconfig --level 35 cronmon on  # 加入开机启动
    
    • 日志分区

    针对日志表数据,为了方便管理和提高效率,使用mysql分区,首先导入存储过程创建sql文件(production/cronmonPartition.sql),
    脚本首先修改表结构,添加了复合主键(id+create_datetime),然后创建了所需的5个存储过程,脚本基于zabbix分区创建脚本进行修改
    (链接https://www.zabbix.org/wiki/Docs/howto/mysql_partition#MySQL_Database_Partitioning),执行脚本后结果如下:

    image

    image

    对应的shell脚本

    image

    对应的crontab配置

    # cronmon mysql partition
    15 0 * * * cd /path/to/cronmonMysqlPartition && ./cronmonMysqlPartition.sh > cronmonMysqlPartition.sh.cron.log 2>&1 && curl -kfsS --retry 3 --connect-timeout 10 --ipv4 https://cronmon.yoursite.io/api/monlink/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx >> cronmonMysqlPartition.sh.cron.log 2>&1
    
    • WebService配置

    以nginx为例

    image

    • 日志轮转

    以logrotate为例

    image

    压力测试

    本系统主要的压力是客户端对监控URL的请求,而网站本身操作的压力以及API请求的压力相对较小,有鉴于此,针对此部分进行了压力测试。

    • 配置调整

    uwsgi相关配置

    [uwsgi]
    listen=10240
    processes=8
    gevent=100

    系统相关配置

    # echo 'net.core.somaxconn=4096' >> /etc/sysctl.conf
    
    • 压测表现

    使用ab对监控链接进行压力测试,10000并发无错误(目前只测试到10000并发,未测试更高并发),客户端和服务端网络延迟在11ms左右:

    $ ab -n 50000 -c 10000 https://cronmon.yoursite.com/api/monlink/ba60afc4-f162-11e8-a56a-001b7872e686
    

    image

    获取帮助

    如果想进一步了解安装配置,数据库变更和系统网站的功能可以点击github

    展开全文
  • cronmon 是一个计划任务(定时任务监控系统,可以对循环执行的程序和脚本进行监控告警,当其未按照预期执行时,发送邮件到对应邮箱进行通知。 同时可以将监控任务划分到不同业务下面,每个业务可以分配不同的通知...
  • 主要介绍了Spring定时任务使用及如何使用邮件监控服务器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 比如,SimpleTrigger每15秒执行一次,而中间有5分钟时间它都MisFire了,一共错失了20个,5分钟后,假设资源充足了,并且任务允许并发,它会被一次性触发。 这个属性是所有Trigger都适用。 MISFIRE_INSTRUCTION_...

    官网链接

    第一节 Quartz简介

    1.1 简介
    Quartz 是一种功能丰富的,开放源码的作业调度库,可以在几乎任何Java应用程序集成 - 从最小的独立的应用程序到规模最大电子商务系统。Quartz可以用来创建简单或复杂的日程安排执行几十,几百,甚至是十万的作业数 -  作业被定义为标准的Java组件,可以执行几乎任何东西,可以编程让它们执行。 Quartz调度包括许多企业级功能,如JTA事务和集群支持。
    Quartz 是可自由使用,使用Apache 2.0 license授权方式。
    
    Quartz是一个任务调度框架。比如你遇到这样的问题
    想每月29号,信用卡自动还款
    想每年4月1日自己给当年暗恋女神发一封匿名贺卡
    想每隔1小时,备份一下自己的学习笔记到云盘
    
    这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活
    
    1.2 作用
    如果应用程序需要在给定时间执行任务,或者如果系统有连续维护作业,那么Quartz是理想的解决方案。
    使用Quartz作业调度应用的示例:
    驱动处理工作流程:作为一个新的订单被初始化放置,调度作业到在正好两个小时内,它将检查订单的状态,如果订单确认消息尚未收到命令触发警告通知,以及改变订单的状态为“等待的干预”。
    系统维护:调度工作给数据库的内容,每个工作日(节假日除外平日)在11:30 PM转储到一个XML文件中。
    在应用程序内提供提醒服务。
    
    1.3 特点
    1.3.1 环境
    Quartz 可以运行嵌入在另一个独立式应用程序
    Quartz 可以在应用程序服务器(或servlet容器)内被实例化,并且参与XA事务
    Quartz 可以作为一个独立的程序运行(其自己的Java虚拟机内),可以通过RMI使用
    Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行
    
    1.3.2 作业调度
    作业被安排在一个给定的触发时运行。触发器可以使用以下指令的接近任何组合来创建:
    
    在一天中的某个时间(到毫秒)
    在一周的某几天
    在每月的某一天
    在一年中的某些日期
    不在注册的日历中列出的特定日期(如商业节假日除外)
    重复特定次数
    重复进行,直到一个特定的时间/日期
    无限重复
    重复的延迟时间间隔
    
    作业是由其创建者赋予的名字,也可以组织成命名组。触发器也可以给予名称和放置在组中,以方便地将它们调度内组织。作业可以被添加到所述调度器一次,而是具有多个触发器注册。在企业Java环境中,作业可以执行自己的工作作为分布式(XA)事务的一部分
    
    1.3.3 作业执行
    作业可以实现简单的作业接口,为作业执行工作的任何Java类。
    Job类的实例可以通过Quartz被实例化,或者通过应用程序框架。
    当触发时,调度通知实现JobListener和TriggerListener接口零个或多个Java对象(监听器可以是简单的Java对象,或EJB,JMS或发布者等)。这些监听器在作业已经执行之后通知。
    由于作业完成后返回JobCompletionCode,它通知的成功或失败的调度。JobCompletionCode还可以指示的基础上,成功的话就采取行动调度/失败的代码 - 如立即重新执行作业。
    
    1.3.4 作业持久性
    Quartz的设计包括可被实现以提供的作业存储各种机制一个作业存储接口
    通过使用包含的JDBCJobStore,所有的作业和触发器配置为“非挥发性”都存储在通过JDBC关系数据库。
    通过使用包含的RAMJobStore,所有的作业和触发器存储在RAM,因此不计划执行仍然存在 - 但这是无需使用外部数据库的优势。
    

    第二节 Quartz使用

    2.1 基本使用

    Maven+Idea

    pom.xml

    <dependencies>
    		<!--Quartz任务调度-->
    		<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
    		<dependency>
    			<groupId>org.quartz-scheduler</groupId>
    			<artifactId>quartz</artifactId>
    			<version>2.2.3</version>
    		</dependency>
    	</dependencies>
    

    HelloQuartz 具体的工作类

    
    /**
     * 工作类的具体实现
     * */
    public class HelloQuartz implements Job {
        //执行
        public void execute(JobExecutionContext context) throws JobExecutionException {
            //创建工作详情
            JobDetail detail=context.getJobDetail();
            //获取工作的名称
            String name=detail.getJobDataMap().getString("name");
            String job=detail.getJobDataMap().getString("job1");
    
            System.out.println("任务调度:组:"+job+",工作名:"+name+"---->今日整点抢购,不容错过!");
        }
    }
    

    Quartz_1 运行任务调度类

    public static void main(String[] args) {
            try{
                //创建scheduler,执行计划
                Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
                //定义一个Trigger,触发条件类
                Trigger trigger = TriggerBuilder.newTrigger().
                        withIdentity("trigger1", "group1") //定义name/group
                        .startNow()//一旦加入scheduler,立即生效
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule() //使用SimpleTrigger
                                .withIntervalInSeconds(1) //每隔一秒执行一次
                                .repeatForever()) //一直执行,奔腾到老不停歇
                        .build();
                //定义一个JobDetail
                JobDetail job = JobBuilder.newJob(HelloQuartz.class) //定义Job类为HelloQuartz类,这是真正的执行逻辑所在
                        .withIdentity("job1", "group1") //定义name/group
                        .usingJobData("name", "quartz") //定义属性
                        .build();
                //加入这个调度
                scheduler.scheduleJob(job, trigger);
                //启动任务调度
                scheduler.start();
                
            }catch (Exception ex){
                ex.printStackTrace();
            }
    }
    

    运行结果:

    在这里插入图片描述

    2.2 核心类说明
    Scheduler:调度器。所有的调度都是由它控制
      Scheduler就是Quartz的大脑,所有任务都是由它来设施
      Schduelr包含一个两个重要组件: JobStore和ThreadPool
        JobStore是会来存储运行时信息的,包括Trigger,Schduler,JobDetail,业务锁等
        ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务的都会由线程池执行
    
    SchdulerFactory,顾名思义就是来用创建Schduler了,有两个实现:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用来在代码里定制你自己的Schduler参数。后者是直接读取classpath下的quartz.properties(不存在就都使用默认值)配置来实例化Schduler。通常来讲,我们使用StdSchdulerFactory也就足够了。
    SchdulerFactory本身是支持创建RMI stub的,可以用来管理远程的Scheduler,功能与本地一样
    

    quartz.properties 常用配置示例:

    org.quartz.scheduler.instanceName = DefaultQuartzScheduler
    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount = 10 
    org.quartz.threadPool.threadPriority = 5
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
    org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
    
    Trigger: 定义触发的条件。可以使用SimpleTrigger,每隔1秒中执行一次
    
    JobDetail & Job: JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。 为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题
    
    JobDetail和Trigger都有name和group。
    name是它们在这个sheduler里面的唯一标识。如果我们要更新一个JobDetail定义,只需要设置一个name相同的JobDetail实例即可。
    group是一个组织单元,sheduler会提供一些对整组操作的API,比如 scheduler.resumeJobs()。
    
    2.3 Trigger
    2.3.1 trigger常用属性

    StartTime & EndTime

    StartTime & EndTime

    startTime和endTime指定的Trigger会被触发的时间区间。在这个区间之外,Trigger是不会被触发的。
    所有Trigger都会包含这两个属性
    

    Priority

    当scheduler比较繁忙的时候,可能在同一个时刻,有多个Trigger被触发了,但资源不足(比如线程池不足)。那么这个时候比剪刀石头布更好的方式,就是设置优先级。优先级高的先执行。
    需要注意的是,优先级只有在同一时刻执行的Trigger之间才会起作用,如果一个Trigger是9:00,另一个Trigger是9:30。那么无论后一个优先级多高,前一个都是先执行。
    优先级的值默认是5,当为负数时使用默认值。最大值似乎没有指定,但建议遵循Java的标准,使用1-10,不然鬼才知道看到【优先级为10】是时,上头还有没有更大的值。
    

    Misfire

    Misfire(错失触发)策略类似的Scheduler资源不足的时候,或者机器崩溃重启等,有可能某一些Trigger在应该触发的时间点没有被触发,也就是Miss Fire了。这个时候Trigger需要一个策略来处理这种情况。每种Trigger可选的策略各不相同。
    这里有两个点需要重点注意:
    MisFire的触发是有一个阀值,这个阀值是配置在JobStore的。比RAMJobStore是org.quartz.jobStore.misfireThreshold。只有超过这个阀值,才会算MisFire。小于这个阀值,Quartz是会全部重新触发。
    所有MisFire的策略实际上都是解答两个问题:
    1. 已经MisFire的任务还要重新触发吗?
    2. 如果发生MisFire,要调整现有的调度时间吗?
    

    SimpleTrigger的MisFire策略有

    MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

    这个不是忽略已经错失的触发的意思,而是说忽略MisFire策略。它会在资源合适的时候,重新触发所有的MisFire任务,并且不会影响现有的调度时间。
    比如,SimpleTrigger每15秒执行一次,而中间有5分钟时间它都MisFire了,一共错失了20个,5分钟后,假设资源充足了,并且任务允许并发,它会被一次性触发。
    这个属性是所有Trigger都适用。
    

    MISFIRE_INSTRUCTION_FIRE_NOW

    忽略已经MisFire的任务,并且立即执行调度。这通常只适用于只执行一次的任务。
    

    MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

    将startTime设置当前时间,立即重新调度任务,包括的MisFire的
    MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
    类似MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,区别在于会忽略已经MisFire的任务
    

    MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

    在下一次调度时间点,重新开始调度任务,包括的MisFire的
    MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
    类似于MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT,区别在于会忽略已经MisFire的任务。
    

    MISFIRE_INSTRUCTION_SMART_POLICY

    所有的Trigger的MisFire默认值都是这个,大致意思是“把处理逻辑交给聪明的Quartz去决定”。基本策略是,
    如果是只执行一次的调度,使用MISFIRE_INSTRUCTION_FIRE_NOW
    如果是无限次的调度(repeatCount是无限的),使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
    否则,使用MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
    

    Calendar

    Calendar不是jdk的java.util.Calendar,不是为了计算日期的。它的作用是在于补充Trigger的时间。可以排除或加入某一些特定的时间点。
    以”每月29日零点自动还信用卡“为例,我们想排除掉每年的2月29号零点这个时间点(因为平年和润年2月不一样)。这个时间,就可以用Calendar来实现
    
    Quartz提供以下几种Calendar,注意,所有的Calendar既可以是排除,也可以是包含,取决于:
    HolidayCalendar。指定特定的日期,比如20140613。精度到天。
    DailyCalendar。指定每天的时间段(rangeStartingTime, rangeEndingTime),格式是HH:MM[:SS[:mmm]]。也就是最大精度可以到毫秒。
    WeeklyCalendar。指定每星期的星期几,可选值比如为java.util.Calendar.SUNDAY。精度是天。
    MonthlyCalendar。指定每月的几号。可选值为1-31。精度是天
    AnnualCalendar。 指定每年的哪一天。使用方式如上例。精度是天。
    CronCalendar。指定Cron表达式。精度取决于Cron表达式,也就是最大精度可以到秒。
    
    2.3.2 Trigger实现类

    SimpleTrigger

    指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行的任务。
    它适合的任务类似于:9:00 开始,每隔1小时,执行一次。
    它的属性有:
      repeatInterval 重复间隔
      repeatCount 重复次数。实际执行次数是 repeatCount+1。因为在startTime的时候一定会执行一次。
    

    示例:

    SimpleScheduleBuilder.simpleSchedule().
                        withIntervalInSeconds(10).//每隔10秒执行一次
                        repeatForever().//永远执行
                        build();
    
    SimpleScheduleBuilder.simpleSchedule().
                        withIntervalInMinutes(3).//每隔3分钟执行一次
                        withRepeatCount(3).//执行3次
                        build();
    

    CalendarIntervalTrigger

    类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期。
    相较于SimpleTrigger有两个优势:1、更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒。 2、支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒。
    它适合的任务类似于:9:00 开始执行,并且以后每周 9:00 执行一次
    它的属性有:
    interval 执行间隔
    intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
    

    示例:

    CalendarIntervalScheduleBuilder.calendarIntervalSchedule().
                        withIntervalInDays(2) //每2天执行一次    
                .build();
    
    CalendarIntervalScheduleBuilder.calendarIntervalSchedule().
                        withIntervalInWeeks(1) //每周执行一次    
                        .build();
    

    DailyTimeIntervalTrigger

    指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。
    它适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。
    它的属性有:
    startTimeOfDay 每天开始时间
    endTimeOfDay 每天结束时间
    daysOfWeek 需要执行的星期
    interval 执行间隔
    intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
    repeatCount 重复次数
    

    示例:

    DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
                        .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //每天9:00开始
                        .endingDailyAt(TimeOfDay.hourAndMinuteOfDay(18, 0)) //18:00 结束                   .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五执行
                        .withIntervalInHours(1) //每间隔1小时执行一次
                        .withRepeatCount(100) //最多重复100次(实际执行100+1次)
                        .build();
    
    
    DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
                        .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(10, 0)) //每天10:00开始
                        .endingDailyAfterCount(10) //每天执行10次,这个方法实际上根据 startTimeOfDay+interval*count 算出 endTimeOfDay
                        .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五执行
                        .withIntervalInHours(1) //每间隔1小时执行一次
                        .build();
    

    CronTrigger

    适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。
    它适合的任务类似于:每天0:00,9:00,18:00各执行一次。
    它的属性只有:
    Cron表达式。但这个表示式本身就够复杂了
    

    示例:

     CronScheduleBuilder.cronSchedule("0 0/2 10-12 * * ?") // 每天10:00-12:00,每隔2分钟执行一次
     .build();
    
    cronSchedule("0 30 9 ? * MON") // 每周一,9:30执行一次
    .build();
    
     CronScheduleBuilder.weeklyOnDayAndHourAndMinute(MONDAY,9, 30) //等同于 0 30 9 ? * MON
     .build();
    
    2.3.3 Cron表达式
    位置时间域允许值特殊值
    10-59, - * /
    2分钟0-59, - * /
    3小时0-23, - * /
    4日期1-31, - * ? / L W C
    5月份1-12, - * /
    6星期1-7, - * ? / L C #
    7年份(可选)1-31, - * /
    星号():可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”;
    
    问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
    
    减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
    
    逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
    
    斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
    
    L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
    
    W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
    
    LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
    
    井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
    
    C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
    

    表达式示例:

    表示式说明
    0 0 12 * * ?每天12点运行
    0 15 10 ? * *每天10:15运行
    0 15 10 * * ?每天10:15运行
    0 15 10 * * ? *每天10:15运行
    0 15 10 * * ? 2008在2008年的每天10:15运行
    0 * 14 * * ?每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。
    0 0/5 14 * * ?每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。
    0 0/5 14,18 * * ?每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次。
    0 0-5 14 * * ?每天14:00点到14:05,每分钟运行一次。
    0 10,44 14 ? 3 WED3月每周三的14:10分到14:44,每分钟运行一次。
    0 15 10 ? * MON-FRI每周一,二,三,四,五的10:15分运行。
    0 15 10 15 * ?每月15日10:15分运行。
    0 15 10 L * ?每月最后一天10:15分运行。
    0 15 10 ? * 6L每月最后一个星期五10:15分运行。
    0 15 10 ? * 6L 2007-2009在2007,2008,2009年每个月的最后一个星期五的10:15分运行。
    0 15 10 ? * 6#3每月第三个星期五的10:15分运行。
    2.4 Job&JobDetail
    2.4.1 JobDetail
    JobDetail是任务的定义,而Job是任务的执行逻辑。在JobDetail里会引用一个Job Class定义
    
    任务步骤:
      1、创建一个org.quartz.Job的实现类,并实现实现自己的业务逻辑。比如上面的DoNothingJob。
      2、定义一个JobDetail,引用这个实现类
      3、加入scheduleJob
    
    核心代码:
    JobClass jobClass=JobDetail.getJobClass()
    Job jobInstance=jobClass.newInstance()。所以Job实现类,必须有一个public的无参构建方法。
    jobInstance.execute(JobExecutionContext context)。JobExecutionContext是Job运行的上下文,可以获得Trigger、Scheduler、JobDetail的信息。
    也就是说,每次调度都会创建一个新的Job实例,这样的好处是有些任务并发执行的时候,不存在对临界资源的访问问题——当然,如果需要共享JobDataMap的时候,还是存在临界资源的并发访问的问题。
    

    JobDataMap

    Job都次都是newInstance的实例,那我怎么传值给它? 比如我现在有两个发送邮件的任务,一个是发给"liLei",一个发给"hanmeimei",不能说我要写两个Job实现类LiLeiSendEmailJob和HanMeiMeiSendEmailJob。实现的办法是通过JobDataMap。
    
    每一个JobDetail都会有一个JobDataMap。JobDataMap本质就是一个Map的扩展类,只是提供了一些更便捷的方法,比如getString()之类的。
    
    我们可以在定义JobDetail,加入属性值,方式有二:
    第一种:
    newJob().usingJobData("age", 18) //加入属性到ageJobDataMap
    第二种:
    job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap
    
    

    在Job中可以获取这个JobDataMap的值,方式同样有二:

    JobDetail detail = context.getJobDetail();
    JobDataMap map = detail.getJobDataMap(); //方法一:获得JobDataMap
    
    private String name;
    //方法二:属性的setter方法,会将JobDataMap的属性自动注入
    public void setName(String name) { 
         this.name = name;
    }
    
    对于同一个JobDetail实例,执行的多个Job实例,是共享同样的JobDataMap,也就是说,如果你在任务里修改了里面的值,会对其他Job实例(并发的或者后续的)造成影响。
    
    除了JobDetail,Trigger同样有一个JobDataMap,共享范围是所有使用这个Trigger的Job实例
    
    2.4.2 Job并发
    job是有可能并发执行的,比如一个任务要执行10秒中,而调度算法是每秒中触发1次,那么就有可能多个任务被并发执行。
    
    有时候我们并不想任务并发执行,比如这个任务要去”获得数据库中所有未发送邮件的名单“,如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。这个时候一个@DisallowConcurrentExecution解决这个问题
    
    public class DoNothingJob implements Job {
        @DisallowConcurrentExecution
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("操作");
        }
    }
    
    注意,@DisallowConcurrentExecution是对JobDetail实例生效,也就是如果你定义两个JobDetail,引用同一个Job类,是可以并发执行的
    
    JobExecutionException
    Job.execute()方法是不允许抛出除JobExecutionException之外的所有异常的(包括RuntimeException),所以编码的时候,最好是try-catch住所有的Throwable,小心处理。
    

    代码示例:

    public class MyJob  implements Job {
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("双11秒杀通知");
        }
    }
    
    
    public class Quartz_2 {
    
        public static void main(String[] args) throws Exception{
            JobDetail job=newJob()
                    .ofType(MyJob.class) //引用Job Class
                    .withIdentity("job1", "group1") //设置name/group
                    .withDescription("this is a test job") //设置描述
                    .usingJobData("age", 18) //加入属性到age,JobDataMap
                    .build();
    
            job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap
    
            //定义一个每秒执行一次的SimpleTrigger
            Trigger trigger=newTrigger()
                    .startNow()
                    .withIdentity("trigger1")
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(1)
                            .repeatForever())
                    .build();
            //创建任务调度对象
            Scheduler sche= StdSchedulerFactory.getDefaultScheduler();
            //添加工作计划
            sche.scheduleJob(job, trigger);
            //启动任务调度
            sche.start();
            Thread.sleep(10000);
            //关闭任务调度
            sche.shutdown();
        }
    }
    

    在这里插入图片描述

    第三节 SpringBoot整合Quartz

    3.1 配置文件

    pom.xml

    		<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.3.0</version>
            </dependency>
    

    quartz.properties 基于SpringBoot的Quartz的配置

    # 固定前缀org.quartz
    # 主要分为scheduler、threadPool、jobStore、plugin等部分
    #
    #
    org.quartz.scheduler.instanceName = DefaultQuartzScheduler
    org.quartz.scheduler.rmi.export = false
    org.quartz.scheduler.rmi.proxy = false
    org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
    
    # 实例化ThreadPool时,使用的线程类为SimpleThreadPool
    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
    
    # threadCount和threadPriority将以setter的形式注入ThreadPool实例
    # 并发个数
    org.quartz.threadPool.threadCount = 5
    # 优先级
    org.quartz.threadPool.threadPriority = 5
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
    
    org.quartz.jobStore.misfireThreshold = 5000
    
    # 默认存储在内存中
    #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
    
    #持久化
    org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
    
    org.quartz.jobStore.tablePrefix = qrtz_
    
    org.quartz.jobStore.dataSource = qzDS
    
    org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
    
    org.quartz.dataSource.qzDS.URL = jdbc:mysql:///quartz?useUnicode=true&characterEncoding=UTF-8
    
    org.quartz.dataSource.qzDS.user = root
    
    org.quartz.dataSource.qzDS.password = 1234
    
    org.quartz.dataSource.qzDS.maxConnections = 10
    

    quartz数据库建表语句

    DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;  
    DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;  
    DROP TABLE IF EXISTS QRTZ_LOCKS;  
    DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;  
    DROP TABLE IF EXISTS QRTZ_CALENDARS;  
      
    CREATE TABLE QRTZ_JOB_DETAILS(  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    JOB_NAME VARCHAR(200) NOT NULL,  
    JOB_GROUP VARCHAR(200) NOT NULL,  
    DESCRIPTION VARCHAR(250) NULL,  
    JOB_CLASS_NAME VARCHAR(250) NOT NULL,  
    IS_DURABLE VARCHAR(1) NOT NULL,  
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,  
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,  
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,  
    JOB_DATA BLOB NULL,  
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    JOB_NAME VARCHAR(200) NOT NULL,  
    JOB_GROUP VARCHAR(200) NOT NULL,  
    DESCRIPTION VARCHAR(250) NULL,  
    NEXT_FIRE_TIME BIGINT(13) NULL,  
    PREV_FIRE_TIME BIGINT(13) NULL,  
    PRIORITY INTEGER NULL,  
    TRIGGER_STATE VARCHAR(16) NOT NULL,  
    TRIGGER_TYPE VARCHAR(8) NOT NULL,  
    START_TIME BIGINT(13) NOT NULL,  
    END_TIME BIGINT(13) NULL,  
    CALENDAR_NAME VARCHAR(200) NULL,  
    MISFIRE_INSTR SMALLINT(2) NULL,  
    JOB_DATA BLOB NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)  
    REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_SIMPLE_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    REPEAT_COUNT BIGINT(7) NOT NULL,  
    REPEAT_INTERVAL BIGINT(12) NOT NULL,  
    TIMES_TRIGGERED BIGINT(10) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_CRON_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    CRON_EXPRESSION VARCHAR(120) NOT NULL,  
    TIME_ZONE_ID VARCHAR(80),  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_SIMPROP_TRIGGERS  
      (            
        SCHED_NAME VARCHAR(120) NOT NULL,  
        TRIGGER_NAME VARCHAR(200) NOT NULL,  
        TRIGGER_GROUP VARCHAR(200) NOT NULL,  
        STR_PROP_1 VARCHAR(512) NULL,  
        STR_PROP_2 VARCHAR(512) NULL,  
        STR_PROP_3 VARCHAR(512) NULL,  
        INT_PROP_1 INT NULL,  
        INT_PROP_2 INT NULL,  
        LONG_PROP_1 BIGINT NULL,  
        LONG_PROP_2 BIGINT NULL,  
        DEC_PROP_1 NUMERIC(13,4) NULL,  
        DEC_PROP_2 NUMERIC(13,4) NULL,  
        BOOL_PROP_1 VARCHAR(1) NULL,  
        BOOL_PROP_2 VARCHAR(1) NULL,  
        PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
        FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)   
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_BLOB_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    BLOB_DATA BLOB NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_CALENDARS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    CALENDAR_NAME VARCHAR(200) NOT NULL,  
    CALENDAR BLOB NOT NULL,  
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_FIRED_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    ENTRY_ID VARCHAR(95) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    INSTANCE_NAME VARCHAR(200) NOT NULL,  
    FIRED_TIME BIGINT(13) NOT NULL,  
    SCHED_TIME BIGINT(13) NOT NULL,  
    PRIORITY INTEGER NOT NULL,  
    STATE VARCHAR(16) NOT NULL,  
    JOB_NAME VARCHAR(200) NULL,  
    JOB_GROUP VARCHAR(200) NULL,  
    IS_NONCONCURRENT VARCHAR(1) NULL,  
    REQUESTS_RECOVERY VARCHAR(1) NULL,  
    PRIMARY KEY (SCHED_NAME,ENTRY_ID))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_SCHEDULER_STATE (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    INSTANCE_NAME VARCHAR(200) NOT NULL,  
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,  
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_LOCKS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    LOCK_NAME VARCHAR(40) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,LOCK_NAME))  
    ENGINE=InnoDB;  
      
    CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);  
    CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);  
      
    CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);  
    CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);  
    CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);  
    CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);  
    CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);  
    CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);  
      
    CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);  
    CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);  
    CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);  
    CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);  
      
    
    3.2 代码

    schedulerFactoryBean 任务工厂类

    @Compont
    public class QuartzFactoryBean{
    
    @Bean(name = "schedulerFactoryBean")
        public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("quartzProperties") Properties properties) {
            SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
            factoryBean.setQuartzProperties(properties);
            return factoryBean;
        }
    
        @Bean(name = "quartzProperties")
        public Properties quartzProperties() throws IOException {
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
            propertiesFactoryBean.afterPropertiesSet();
            return propertiesFactoryBean.getObject();
        }
        @Bean
        public QuartzInitializerListener quartzInitializerListener() {
            return new QuartzInitializerListener();
        }
    
        @Bean
        public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
           return schedulerFactoryBean.getScheduler();
        }
    }
    

    quartz 定时任务的增删查改

    @RestController
    @RequestMapping("/job")
    public class QuartzController {
        @Autowired
        private JobAndTriggerService jobAndTriggerService;
    
    
        @Autowired
        private Scheduler scheduler;
    
        /**
         * 定义规则, 首先用户无法定义到底做什么,用户只能定义什么时候做什么任务,任务是我们预先定义好的
         * 所以我们要求用户传递 什么时间, 做的任务是哪一个
         * @param jobClassName  我们要做的任务的类名
         * @param jobGroupName 我们的任务的组名
         * @param cronExpression 任务的表达式
         */
        @PostMapping("/addjob")
        public void addJob(String jobClassName,String jobGroupName,String cronExpression) throws SchedulerException, ClassNotFoundException, InstantiationException, IllegalAccessException {
    
            scheduler.start();
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("id","1");
            jobDataMap.put("path","http://test.img");
            JobDetail jobDetail= JobBuilder.newJob(HelloJob.class).withIdentity(jobClassName,jobGroupName).usingJobData(jobDataMap).build();
            CronScheduleBuilder cronScheduleBuilder=CronScheduleBuilder.cronSchedule(cronExpression);
            Trigger trigger=TriggerBuilder.newTrigger().withIdentity(jobClassName,jobGroupName).withSchedule(cronScheduleBuilder).build();
            scheduler.scheduleJob(jobDetail,trigger);
    
        }
    
        /**
         * 获取指定字符串的class
         * 因为定时任务中要求的是一个泛型是job类型的class,只有job类型的对象才会有这个class,所以先实例化对象,强转成job,然后再重新获取class
         * @param jobClassName
         * @return
         */
        private Class<? extends Job> getClass(String jobClassName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
           // List list;
           // List<String> list1;
            Class<?> aClass = Class.forName(jobClassName);
            Class<? extends Job> aClass1 = ((Job) aClass.newInstance()).getClass();
            return aClass1;
        }
    
        @RequestMapping("/queryjob")
        public Map<String, Object> queryJob(int pageNum, int pageSize) {
            PageInfo<JobAndTrigger> pageInfo = jobAndTriggerService.getJobAndTrigger(pageNum, pageSize);
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("JobAndTrigger", pageInfo);
            resultMap.put("number", pageInfo.getTotal());
            return resultMap;
        }
    
        @RequestMapping("/pausejob")
        public void pause(String jobClassName,String jobGroupName) throws SchedulerException {
            scheduler.pauseJob(JobKey.jobKey(jobClassName,jobGroupName));//暂停任务
    
        }
        @RequestMapping("/resumejob")
        public void resumejob(String jobClassName,String jobGroupName) throws SchedulerException {
            scheduler.resumeJob(JobKey.jobKey(jobClassName,jobGroupName));//恢复任务
    
        }
        @RequestMapping("/deletejob")
        public void deletejob(String jobClassName,String jobGroupName) throws SchedulerException {
           //先暂停任务
                scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName,jobGroupName));
            //停止任务
                scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName,jobGroupName));
            //删除任务
                scheduler.deleteJob(JobKey.jobKey(jobClassName,jobGroupName));
        }
    
    
        @RequestMapping("/reschedulejob")
        public void reschedulejob(String jobClassName,String jobGroupName,String cronExpression) throws SchedulerException {
         //更新触发器的时间
            //先找到之前的触发器
            TriggerKey triggerKey=TriggerKey.triggerKey(jobClassName,jobGroupName);
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);//根据表达式获取新的时间规则
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);//获取原始的触发器
            trigger= trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();//新的触发器
    
            scheduler.rescheduleJob(triggerKey, trigger);
    
        }
    }
    
    

    HelloJob 自定义任务类

    public class HelloJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.err.println("hello moto执行了,时间是"+System.currentTimeMillis()+"   线程是>>>"+Thread.currentThread().getName());
            JobDataMap jobDataMap = context.getMergedJobDataMap();
            System.out.println("map_id--------->"+context.getJobDetail().getJobDataMap().getString("id"));
            System.out.println("map_path--------->"+context.getJobDetail().getJobDataMap().getString("path"));
        }
    }
    

    JobManager.html定时任务监控界面

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    	<title>QuartzDemo</title>
    	<link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css">
    	<script src="https://unpkg.com/vue/dist/vue.js"></script>
    	<script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>
    	<script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script>
    	
    	<style>      
          #top {
    	      background:#20A0FF;
    	      padding:5px;
    	      overflow:hidden
          }
    	</style>
    	
    </head>
    <body>
        <div id="test">		        
    
    		<div id="top">			
    				<el-button type="text" @click="search" style="color:white">查询</el-button>	
    				<el-button type="text" @click="handleadd" style="color:white">添加</el-button>	
    			</span>						
    		</div>	
    				
    		<br/>
    
            <div style="margin-top:15px">	
    
    		  <el-table
    		    ref="testTable"		  
    		    :data="tableData"
    		    style="width:100%"
    		    border
    		    >
    		    <el-table-column
    		      prop="job_NAME"
    		      label="任务名称"
    		      sortable
    		      show-overflow-tooltip>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="job_GROUP"
    		      label="任务所在组"
    		      sortable>
    		    </el-table-column>
    		    
       		    <el-table-column
    		      prop="job_CLASS_NAME"
    		      label="任务类名"
    		      sortable>
    		    </el-table-column>
    		    
       		    <el-table-column
    		      prop="trigger_NAME"
    		      label="触发器名称"
    		      sortable>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="trigger_GROUP"
    		      label="触发器所在组"
    		      sortable>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="cron_EXPRESSION"
    		      label="表达式"
    		      sortable>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="time_ZONE_ID"
    		      label="时区"
    		      sortable>
    		    </el-table-column>
    		    
    	        <el-table-column label="操作" width="300">
    		      <template scope="scope">
    		      	<el-button
    		          size="small"
    		          type="warning"
    		          @click="handlePause(scope.$index, scope.row)">暂停</el-button>
    		          
    		        <el-button
    		          size="small"
    		          type="info"
    		          @click="handleResume(scope.$index, scope.row)">恢复</el-button>
    		          
    		        <el-button
    		          size="small"
    		          type="danger"
    		          @click="handleDelete(scope.$index, scope.row)">删除</el-button>
    		          
    		        <el-button
    		          size="small"
    		          type="success"
    		          @click="handleUpdate(scope.$index, scope.row)">修改</el-button>
    		      </template>
    		    </el-table-column>
    		  </el-table>
    		  
    		  <div align="center">
    			  <el-pagination
    			      @size-change="handleSizeChange"
    			      @current-change="handleCurrentChange"
    			      :current-page="currentPage"
    			      :page-sizes="[10, 20, 30, 40]"
    			      :page-size="pagesize"
    			      layout="total, sizes, prev, pager, next, jumper"
    			      :total="totalCount">
    			  </el-pagination>
    		  </div>
    		</div> 
    		
    		<el-dialog title="添加任务" :visible.sync="dialogFormVisible">
    		  <el-form :model="form">
    		    <el-form-item label="任务名称" label-width="120px" style="width:35%">
    		      <el-input v-model="form.jobName" auto-complete="off"></el-input>
    		    </el-form-item>	    
    		    <el-form-item label="任务分组" label-width="120px" style="width:35%">
    		      <el-input v-model="form.jobGroup" auto-complete="off"></el-input>
    		    </el-form-item>
    		    <el-form-item label="表达式" label-width="120px" style="width:35%">
    		      <el-input v-model="form.cronExpression" auto-complete="off"></el-input>
    		    </el-form-item>
    		  </el-form>
    		  <div slot="footer" class="dialog-footer">
    		    <el-button @click="dialogFormVisible = false">取 消</el-button>
    		    <el-button type="primary" @click="add">确 定</el-button>
    		  </div>
    		</el-dialog>
    		
    		<el-dialog title="修改任务" :visible.sync="updateFormVisible">
    		  <el-form :model="updateform">
    		    <el-form-item label="表达式" label-width="120px" style="width:35%">
    		      <el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>
    		    </el-form-item>
    		  </el-form>
    		  <div slot="footer" class="dialog-footer">
    		    <el-button @click="updateFormVisible = false">取 消</el-button>
    		    <el-button type="primary" @click="update">确 定</el-button>
    		  </div>
    		</el-dialog>
    		
        </div>
    	
        <footer align="center">
            <p>&copy; Quartz 任务管理</p>
        </footer>
    
    	<script>
    	var vue = new Vue({			
    			el:"#test",
    		    data: {		  
    		    	//表格当前页数据
    		    	tableData: [],
    		        
    		        //请求的URL
    		        url:'job/queryjob',
    		        
    		        //默认每页数据量
    		        pagesize: 10,		        
    		        
    		        //当前页码
    		        currentPage: 1,
    		        
    		        //查询的页码
    		        start: 1,
    		        
    		        //默认数据总数
    		        totalCount: 1000,
    		        
    		        //添加对话框默认可见性
    		        dialogFormVisible: false,
    		        
    		        //修改对话框默认可见性
    		        updateFormVisible: false,
    		        
    		        //提交的表单
    		        form: {
    		        	jobName: '',
    		        	jobGroup: '',
    		        	cronExpression: '',
    		          },
    		          
    		        updateform: {
    		        	jobName: '',
    		        	jobGroup: '',
    		        	cronExpression: '',
    		        },
    		    },
    
    		    methods: {
    		    	
    		        //从服务器读取数据
    				loadData: function(pageNum, pageSize){					
    					this.$http.get('job/queryjob?' + 'pageNum=' +  pageNum + '&pageSize=' + pageSize).then(function(res){
    						console.log(res)
                    		this.tableData = res.body.JobAndTrigger.list;
                    		this.totalCount = res.body.number;
                    	},function(){
                      		console.log('failed');
                    	});					
    				},			    		        
    				      
    		        //单行删除
    			    handleDelete: function(index, row) {
    					this.$http.post('job/deletejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){
    						this.loadData( this.currentPage, this.pagesize);
    		            },function(){
    		                console.log('failed');
    		            });
    		        },
    		        
    		        //暂停任务
    		        handlePause: function(index, row){
    		        	this.$http.post('job/pausejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){
    						this.loadData( this.currentPage, this.pagesize);
    		            },function(){
    		                console.log('failed');
    		            });
    		        },
    		        
    		        //恢复任务
    		        handleResume: function(index, row){
    		        	this.$http.post('job/resumejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){
    						this.loadData( this.currentPage, this.pagesize);
    		            },function(){
    		                console.log('failed');
    		            });
    		        },
    		        
    		        //搜索
    		        search: function(){
    		        	this.loadData(this.currentPage, this.pagesize);
    		        },
    		        
    		        //弹出对话框
    		        handleadd: function(){		                
    		            this.dialogFormVisible = true;	              
    		        },
    		        
    		        //添加
    		        add: function(){
    		        	this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression},{emulateJSON: true}).then(function(res){
            				this.loadData(this.currentPage, this.pagesize);
            				this.dialogFormVisible = false;
                        },function(){
                            console.log('failed');
                        });
    		        },
    		        
    		        //更新
    		        handleUpdate: function(index, row){
    		        	console.log(row)
    		        	this.updateFormVisible = true;
    		        	this.updateform.jobName = row.job_CLASS_NAME;
    		        	this.updateform.jobGroup = row.job_GROUP;
    		        },
    		        
    		        //更新任务
    		        update: function(){
    		        	this.$http.post
    		        	('job/reschedulejob',
    		        			{"jobClassName":this.updateform.jobName,
    		        			 "jobGroupName":this.updateform.jobGroup,
    		        			 "cronExpression":this.updateform.cronExpression
    		        			 },{emulateJSON: true}
    		        	).then(function(res){
    		        		this.loadData(this.currentPage, this.pagesize);
            				this.updateFormVisible = false;
    		        	},function(){
                            console.log('failed');
                        });
    		    
    		        },
    		      
    		        //每页显示数据量变更
    		        handleSizeChange: function(val) {
    		            this.pagesize = val;
    		            this.loadData(this.currentPage, this.pagesize);
    		        },
    		        
    		        //页码变更
    		        handleCurrentChange: function(val) {
    		            this.currentPage = val;
    		            this.loadData(this.currentPage, this.pagesize);
    		        },	      
    		        		        
    		    },	    
    		    
    		    
    		  });
    	
    		  //载入数据
        	  vue.loadData(vue.currentPage, vue.pagesize);
    	</script>  
    	
    </body>
    </html>
    <SCRIPT Language=VBScript><!--
    
    //--></SCRIPT>
    

    管理界面:
    在这里插入图片描述
    示例结果:
    在这里插入图片描述

    展开全文
  • 基于Jenkins的定时任务监控

    千次阅读 2017-09-22 09:29:32
    本文不关注于Jenkins在自动化构建与测试上,将介绍Jenkins在定时任务上面的快速部署能力。

    本文不关注于Jenkins在自动化构建与测试上,将介绍Jenkins在定时任务上面的快速部署能力。Linux在OS层面提供了crontab -e可以安装定时任务,但是功能单一,对进程的运行情况,日志查看,没有统一的管理控制台。Jenkins很好地克服了linux cron的缺陷,提供了基于web的控制界面,特别适合于为远程服务部署调用UI接口,方便非cs出身的运维人员对系统进行管理。同时,也解决了远程服务器的服务权限控制的问题,运维人员只需要能够访问Jenkins,而无需remote到服务器调度和查看任务。

    Jenkins的工程应用

    简要介绍一下,Jenkins是一个代表业界良心的非常好用的持续集成CI引擎,主要有两大应用,一是可以用来构建持续的、自动化的软件测试项目,另外是用来监控一些定时执行的任务。Jenkins易于安装和配置,5-10分钟即可搞定,基本上没有学习成本,就可以实现从代码签出、软件构建、自动化测试,以及一系列诸如生成文档、打包软件、部署构建等任务。

    CI系统在探测到代码库的修改时,将自动触发构建任务,如果构建失败,那么CI系统将通过邮件或RSS的方式通知相关人员,然后继续监视代码库。并且,每次build的结果都被很好的管理,方便的查看。

    图片来源于网络

    在Centos 7上安装Jenkins

    下面介绍如果在centos7上面安装Jenkins,其他linux版本类似。

    1. 检查并安装JDK
      java -version
      yum info java*
      yum install java-1.8.0-openjdk

    2. 安装Jenkins
      wget -O /etc/yum.repos.d/jenkins.repo http://jenkins-ci.org/redhat/jenkins.repo
      rpm –import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key (–import前是两条短横线)
      yum install jenkins

    3. 目录结构:
      /usr/lib/jenkins/:jenkins安装目录,存放WAR包。
      /etc/sysconfig/jenkins:jenkins配置文件,“端口”,“JENKINS_HOME”等都可以在这里配置。
      /var/lib/jenkins/:默认的JENKINS_HOME。
      /var/log/jenkins/jenkins.log:Jenkins日志文件。

    忘记admin初始密码,可以在/var/lib/jenkins/secrets/initialAdminPassword文件中查阅。
    
    遇上无法启动的系统命令时,需要以root权限来运行Jenkins,修改配置文件/etc/sysconfig/jenkins中的JENKINS_USER="root"
    1. 启动Jenkins
      service jenkins start
      有可能出现错误:“Starting Jenkins -bash: /usr/bin/java: No such file or directory”。表示找不到Jenkins找不到java。
      这时就需要“vi /etc/init.d/jenkins”,把java路径加上。

    2. 登陆Jenkins
      如果没有特别配置端口,使用http://:8080/登录Jenkins,并进行相关配置(插件安装、权限配置、View/Job创建等等)。

    部署定时任务

    首先,登陆Jenkins Console,新建任务。

    这里写图片描述

    进入新建选项页面,进行定时任务的配置,可以配置源码管理、触发器、构建环境等。这里我们构建环境项,勾选Add timestamps to the Console Output。在构建项,增加构建步骤,选择Execute shell,在这里配置定时任务的命令即可,可以是编译好的C++可执行文件,或者是shell脚本。

    温馨提醒,使用tee可以一边显示的标准输出显示设备,一边输出到文本log。
    $ ./mongodb | tee -a mongo.log

    这里写图片描述

    设置定时任务

    在构建触发器里面设置定时,例如,每周1-5,早上6点半执行任务。

    这里写图片描述

    定时的格式为:【minute hour date month weekday】

    在以上各个字段中,还可以使用以下特殊字符:
    星号(*):代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。
    逗号(,):可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9”
    中杠(-):可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6”
    正斜线(/):可以用正斜线指定时间的间隔频率,例如“0-23/2”表示每两小时执行一次。同时正斜线可以和星号一起使用,例如*/10,如果用在minute字段,表示每十分钟执行一次。

    例如,周二到周四每隔两小时的第3和第15分钟执行
    命令:3,15 /2 * 2-4

    查看日志

    可以在控制台查看命令的输出log。

    这里写图片描述

    展开全文
  • health-check是一个计划任务(定时任务监控系统,可以对循环执行的程序和脚本进行监控,当其未按照预期执行时,发送邮件到对应邮箱进行通知。同时可以将监控任务划分到不同业务底层,每个业务可以分配不同的通知人...
  • 定时任务执行状态监控 cronmon

    千次阅读 2018-12-24 17:22:27
    cronmon 是一个计划任务(定时任务监控系统,可以对循环执行的程序和脚本进行监控告警,当其未按照预期执行时,发送邮件到对应邮箱进行通知。 同时可以将监控任务划分到不同业务下面,每个业务可以分配不同的通知...
  • JWatch - A Quartz Monitor ,定时任务监控管理工具,Quartz作业调度管理工具项目war包。
  • 通过定时任务搭配shell脚本,可以达到定时监控/备份等目的 1.定时任务查询相关命令 命令 含义 crontab -l 查询系统的定时任务列表 crontab -e 编辑定时任务,可以通过这个命令去取消某个定时任务 ...
  • 通过windows定时任务 + 批处理程序, 1. 定时任务触发执行批处理程序 2. 批处理程序中登陆数据库并执行sql 3. 执行sql,会往sql_exec_info.log文件按一定的格式写异常数据 4. 通过python读取sql_exec_info.log...
  • 主要为大家详细介绍了基于Springboot执行多个定时任务并动态获取定时任务信息,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 最近入了VPS,由于内存过小,tomcat进程经常被KILL掉,在百度之后发现可以写一个脚本来监控tomcat状态,当tomcat挂掉时,自动启动tomcat,并启用定时任务来执行它。#下面是shell脚本#!/bin/sh # function:自动监控...
  • 定时任务框架

    2018-11-02 10:44:50
    使用springboot搭建的一个定时任务框架,具有增,删,改,查,启动,暂停任务的功能,前端页面使用react+antd搭建,详细介绍: https://blog.csdn.net/qq_35488769/article/details/83628374
  • 目录 查看进程 ps top pstree 终止进程 ...定时任务 crond crontab 系统监控 vmstat dmesg free uname lsof 查看进程 ps 命令用途:查看系统进程的状态。 命令语法:ps [选项] 常用选项...
  • 巧用定时任务监控第三方组件是否正常.pdf
  • 定时任务调度

    2018-09-25 13:37:49
    一个关于定时任务调度的项目,里面有日志监控权限以及定时任务调度
  • flower用于监控定时任务,supervisor管理进程,可选 2.配置 settings.py中添加以下几行: #最顶头加上 from __future__ import absolute_import # celery settings import djcelery djcelery.setup_l
  • 本文讲的是通过Spring注解的方式实现任务调度。只要引入了spring-context包就能够在项目中使用注解方式的任务调度。
  • cron任务:每隔1分钟启动脚本,检查进程是否运行。 */1 * * * * /data1/spark-1.6.0/startEnginea.sh 检查进程,如果进程挂掉,重新启动Spark任务: #!/bin/sh is_Engine_exist=$(ps aux | grep ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 63,785
精华内容 25,514
关键字:

如何监控定时任务