Spring boot 2.0 Actuator 的健康检查

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot 2.0 Actuator 的健康检查相关的知识,希望对你有一定的参考价值。

参考技术A

在当下流行的Service Mesh架构中,由于Spring boot框架的种种优点,它特别适合作为其中的应用开发框架。

说到Service Mesh的微服务架构,主要特点是将服务开发和服务治理分离开来,然后再结合容器化的Paas平台,将它们融合起来,这依赖的都是互相之间默契的配合。也就是说各自都暴露出标准的接口,可以通过这些接口互相交织在一起。

Service Mesh的架构设计中的要点之一,就是 全方位的监控 ,因此一般我们选用的服务开发框架都需要有方便又强大的监控功能支持。在Spring boot应用中开启监控特别方便,监控面也很广,还支持灵活定制。

在Spring boot应用中,要实现可监控的功能,依赖的是 spring-boot-starter-actuator 这个组件。它提供了很多监控和管理你的spring boot应用的HTTP或者JMX端点,并且你可以有选择地开启和关闭部分功能。当你的spring boot应用中引入下面的依赖之后,将自动的拥有审计、健康检查、Metrics监控功能。

具体的使用方法:

“*”号代表启用所有的监控端点,可以单独启用,例如, health , info , metrics 等。

一般的监控管理端点的配置信息,如下:

上述配置信息仅供参考,具体须参照官方文档,由于spring boot的版本更新比较快,配置方式可能有变化。

今天重点说一下Actuator监控管理中的健康检查功能,随时能掌握线上应用的健康状况是非常重要的,尤其是现在流行的容器云平台下的应用,它们的自动恢复和扩容都依赖健康检查功能。

当我们开启 health 的健康端点时,我们能够查到应用健康信息是一个汇总的信息,访问 http://127.0.0.1:10111/actuator/health 时,我们获取到的信息是 "status":"UP" ,status的值还有可能是 DOWN。

要想查看详细的应用健康信息需要配置 management.endpoint.health.show-details 的值为 always ,配置之后我们再次访问 http://127.0.0.1:10111/actuator/health ,获取的信息如下:

从上面的应用的详细健康信息发现,健康信息包含磁盘空间、redis、DB,启用监控的这个spring boot应用确实是连接了redis和oracle DB,actuator就自动给监控起来了,确实是很方便、很有用。

经过测试发现,details中所有的监控项中的任何一个健康状态是 DOWN ,整体应用的健康状态也是 DOWN 。

Spring boot的健康信息都是从 ApplicationContext 中的各种 HealthIndicator
Beans中收集到的,Spring boot框架中包含了大量的 HealthIndicators 的实现类,当然你也可以实现自己认为的健康状态。

默认情况下,最终的spring boot应用的状态是由 HealthAggregator 汇总而成的,汇总的算法是:

Spring boot框架自带的 HealthIndicators 目前包括:

有时候需要提供自定义的健康状态检查信息,你可以通过实现 HealthIndicator 的接口来实现,并将该实现类注册为spring bean。你需要实现其中的 health() 方法,并返回自定义的健康状态响应信息,该响应信息应该包括一个状态码和要展示详细信息。例如,下面就是一个接口 HealthIndicator 的实现类:

另外,除了Spring boot定义的几个状态类型,我们也可以自定义状态类型,用来表示一个新的系统状态。在这种情况下,你还需要实现接口 HealthAggregator ,或者通过配置 management.health.status.order 来继续使用 HealthAggregator 的默认实现。

例如,在你自定义的健康检查 HealthIndicator 的实现类中,使用了自定义的状态类型 FATAL ,为了配置该状态类型的严重程度,你需要在application的配置文件中添加如下配置:

在做健康检查时,响应中的HTTP状态码反应了整体的健康状态,(例如, UP 对应 200, 而 OUT_OF_SERVICE 和 DOWN 对应 503)。同样,你也需要为自定义的状态类型设置对应的HTTP状态码,例如,下面的配置可以将 FATAL 映射为 503(服务不可用):

下面是内置健康状态类型对应的HTTP状态码列表:

本文主要介绍了Spring boot中提供的应用健康检查功能的使用方法和原理,顺带介绍了一点 Actuator 的内容。主要的内容来自 spring boot 2.0.1的官方文档 和 源码,还有一些自己的想法,希望多多支持。

spring-boot-quartz, 依赖spring-boot-parent

 

 /** 
  *  
        state的值代表该任务触发器的状态: 
        STATE_BLOCKED   4 // 运行 
        STATE_COMPLETE  2  //完成那一刻,不过一般不用这个判断Job状态 
        STATE_ERROR     3  // 错误 
        STATE_NONE  -1      //未知 
        STATE_NORMAL    0   //正常无任务,用这个判断Job是否在运行 
        STATE_PAUSED    1   //暂停状态 
 */  
import java.util.Date;  
  
import org.quartz.CronTrigger;  
import org.quartz.JobDetail;  
import org.quartz.Scheduler;  
import org.quartz.SchedulerException;  
import org.quartz.SchedulerFactory;  
import org.quartz.SimpleTrigger;  
import org.quartz.Trigger;  
import org.quartz.impl.StdScheduler;  
import org.quartz.impl.StdSchedulerFactory;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.context.ApplicationContext;  
import org.springframework.scheduling.quartz.SchedulerFactoryBean;  
  
import cn.wa8.qweb.extract.action.Extract2DB;  
public class SimpleRun {  
  
    private static Logger log = LoggerFactory.getLogger(SimpleRun.class);  
  
    public void run() throws Exception {  
        SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();  
        Scheduler sched = schedFact.getScheduler();  
        JobDetail jobDetail = new JobDetail("myJob",null,SimpleJob.class);  
        SimpleTrigger trigger = new SimpleTrigger("myTrigger",  
                null,  
                new Date(),  
                null,  
                SimpleTrigger.REPEAT_INDEFINITELY,  
                30L * 1000L);  
          
        sched.scheduleJob(jobDetail, trigger);  
        //sched.addJobListener(new MyTriggerListener());  
        SimpleJob.preDate = new Date();  
        sched.start();  
        System.out.println("starting");  
 /** 
  *  
        state的值代表该任务触发器的状态: 
        STATE_BLOCKED   4 // 运行 
        STATE_COMPLETE  2  //完成那一刻,不过一般不用这个判断Job状态 
        STATE_ERROR     3  // 错误 
        STATE_NONE  -1      //未知 
        STATE_NORMAL    0   //正常无任务,用这个判断Job是否在运行 
        STATE_PAUSED    1   //暂停状态 
 */  
    while (true){  
        if(4 == sched.getTriggerState("myTrigger", null)){  
            System.out.println("running");  
        }else if(0 == sched.getTriggerState("myTrigger", null)){  
            System.out.println("ending");  
        }else {  
            System.out.println("error state:"+sched.getTriggerState("myTrigger", null));  
        }  
        try {  
            Thread.sleep(5*1000);  
        } catch (Exception e) {  
            // TODO: handle exception  
        }  
    }  
}  
  
    public static void main(String[] args)  {  
          
        SimpleRun simpleRun = new SimpleRun();  
          
        try {  
            simpleRun.run();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}  

 

 

import java.util.Date;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
import cn.wa8.qweb.extract.action.Extract2DB;  
  
import org.quartz.Job;  
import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;  
import org.quartz.StatefulJob;  
/*Extract2DB extract2db = new Extract2DB(); 
    extract2db.CommonBaseExtract();*/  
  
public class SimpleJob  implements StatefulJob{  
    public static Date preDate ;  
    public void execute(JobExecutionContext arg0) throws JobExecutionException {  
        System.out.println("into Job");  
        Date currentDate = new Date();  
        Long s = (currentDate.getTime()-preDate.getTime())/1000;  
        try {  
            Thread.sleep(10*1000);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        System.out.println(s);  
        System.out.println("leave Job:"+Thread.currentThread().toString());  
        preDate =currentDate;  
    }  
  
}  

http://blog.csdn.net/u010666884/article/details/51842610




都是执行时间大于间隔时间才会出现的情况,实际做了测试和http://blog.sina.com.cn/s/blog_56d8ea900100cecq.html第2点有点不符,记录如下:

第一种情况:misfire设置时间为600秒;任务每隔2分钟执行一次;任务执行时间为3分钟;
上次执行时间    下次执行时间    状态        解释
20:23:04        20:25:04        正在执行    任务实际要执行到20:26:04,推后两分钟是20:28:04

 

时间到了20:26:04。日志变更为:

20:25:04        20:27:04        正在执行    任务实际要执行的20:29:04,推后两分钟是20:31:04

 

时间到了20:29:04,日志变更为:

20:27:04        20:29:04  

连续三次发现,实际开始的时间减去应该开始的时间差是递增的;上次执行和下次执行时间反映的实际情况都是不准确的,而且会出现下次执行时间小于当前时间的情况。

注意:持续执行的到 实际启动时间 减去 应该开始时间 大于等于misfire时间;奇怪的是,不是开始参考第二种情况继续执行,而是最后一次执行后即开始长时间等待,而且上次以及下次开始时间也不更新,保持原样;直到实际启动时间+misfire时间 时刻开始继续执行,并且更新上次以及下次开始时间,再开始一个上述周期。

第二种情况:misfire设置时间为6秒,任务每隔2分钟执行一次,任务执行时间为3分钟:

上次执行时间    下次执行时间    状态        解释
18:08:12        18:10:12        正在执行    任务实际要执行到18:11:12,推后两分钟是18:13:12

 

时间到了18:11:12,日志变更为:

18:08:12        18:10:12        等待        超出misfire时间;任务还没有更新状态
18:08:12        18:12:12        等待        是18:12:12,而不是18:13:12。

算法描述如下:

本次任务应该开始时间为18:10:12,应该结束时间为18:12:12;实际启动时间为18:11:12;实际启动后结束时间为18:13:12;实际启动时间减去应该开始时间超出了misfire,所以状态为等待,即本次任务不执行,从而上次执行时间不变;
计算下次执行时间:当前时间为18:11:12(或者一个稍微大于该值的值),拿应该结束时间以及实际启动后结束时间和当前时间比较,取当前时间往后的最小值作为下次任务启动时间。(算法兼容下面第2点说法)
18:12:12 18:14:12 正在执行

其他引用:
org.quartz.jobStore.misfireThreshold = 60000 #60秒 默认值
那么执行第一次作业是在10:01秒,这时会设定下一次的执行时间为10:02秒,要等一个作业执行完之后才有可用线程,大概要在10:11秒才能执行前面安排的应该在10:02执行的作业,这时就会用到misfireThreshold, 因为10:11与10:02之间的差值小于6000,所以执行该作业,并以10:02为基准设置下一次执行时间为10:03,这样造成每次实际执行时间与安排时间错位
如果 org.quartz.jobStore.misfireThreshold = 6000 #秒
同样,在10:11计划执行安排在10:02的作业,发现10:11与10:02之间的差值小于6000,那么直接跳过该作业,执行本应在当前时间执行的 作业,这时候会以10:11为基准设定下次作业执行时间为10:12(状态此段区间内一直是等待,只是更改了下次作业时间)

其他情况:
quartz有个全局的参数misfireThreshold设置可以允许的超时时间,超过了就不执行,未超过就执行。
比如设置了misfireThreshold=30分钟,如果一个任务定时在10:30执行,但在10:29服务器挂了,在10:50启动,虽然任务超时了21分钟,但小于misfireThreshold,所以还是可以执行
而如果服务器11:10才启动,那就misfire了。

对于周期性的任务,如果有misfire的情况出现,则会自动更新CronTrigger的时间周期
默认情况下会在当前时间马上执行前一个被misfire的任务
而如果设置MISFIRE_INSTRUCTION_DO_NOTHING,则不对misfire的任务做特殊处理,只从当前时间之后的下一次正常调度时间开始执行

http://blog.sina.com.cn/s/blog_56d8ea900101d2mh.html

 

http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html

spring-boot-quartz, 依赖spring-boot-parent

image

application.properties

# IDENTITY (ContextIdApplicationContextInitializer)
spring.application.index=WebQuartz.v1.1
spring.application.name=WebQuartz

#Server
server.port=80
server.jsp-servlet.class-name=org.apache.jasper.servlet.JspServlet

security.basic.enabled=false
management.security.enabled=false

#MVC
spring.mvc.view.prefix=/WEB-INF/views/
spring.resources.static-locations=classpath:/static/

security.basic.enabled=false
management.security.enabled=false

#LOG
logging.config=classpath:log4j2.xml

configuration

@Configuration
public class QuartzConfig {

    @Bean
    public Scheduler scheduler() throws IOException, SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory(quartzProperties());
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
        return scheduler;
    }

    /**
     * 设置quartz属性
     * @throws IOException
     * 2016年10月8日下午2:39:05
     */
    public Properties quartzProperties() throws IOException {
        Properties prop = new Properties();
        prop.put("quartz.scheduler.instanceName", "ServerScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
        prop.put("org.quartz.scheduler.instanceId", "NON_CLUSTERED");
        prop.put("org.quartz.scheduler.jobFactory.class", "org.quartz.simpl.SimpleJobFactory");
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        prop.put("org.quartz.jobStore.dataSource", "quartzDataSource");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "5");

        prop.put("org.quartz.dataSource.quartzDataSource.driver", "com.mysql.jdbc.Driver");
        prop.put("org.quartz.dataSource.quartzDataSource.URL", "jdbc:mysql://localhost:3306/demo-schema");
        prop.put("org.quartz.dataSource.quartzDataSource.user", "root");
        prop.put("org.quartz.dataSource.quartzDataSource.password", "123456");
        prop.put("org.quartz.dataSource.quartzDataSource.maxConnections", "10");
        return prop;
    }
}

JS

@Service
public class TaskServiceImpl {
    private Logger logger = LogManager.getLogger(getClass());
    @Autowired
    private Scheduler scheduler;

    /**
     * 所有任务列表
     * 2016年10月9日上午11:16:59
     */
    public List<TaskInfo> list(){
        List<TaskInfo> list = new ArrayList<>();

        try {
            for(String groupJob: scheduler.getJobGroupNames()){
                for(JobKey jobKey: scheduler.getJobKeys(GroupMatcher.<JobKey>groupEquals(groupJob))){
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                    for (Trigger trigger: triggers) {
                        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                        JobDetail jobDetail = scheduler.getJobDetail(jobKey);

                        String cronExpression = "", createTime = "";

                        if (trigger instanceof CronTrigger) {
                            CronTrigger cronTrigger = (CronTrigger) trigger;
                            cronExpression = cronTrigger.getCronExpression();
                            createTime = cronTrigger.getDescription();
                        }
                        TaskInfo info = new TaskInfo();
                        info.setJobName(jobKey.getName());
                        info.setJobGroup(jobKey.getGroup());
                        info.setJobDescription(jobDetail.getDescription());
                        info.setJobStatus(triggerState.name());
                        info.setCronExpression(cronExpression);
                        info.setCreateTime(createTime);
                        list.add(info);
                    }                   
                }
            }           
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        return list;
    }

    /**
     * 保存定时任务
     * @param info
     * 2016年10月9日上午11:30:40
     */
    @SuppressWarnings("unchecked")
    public void addJob(TaskInfo info) {
        String jobName = info.getJobName(), 
               jobGroup = info.getJobGroup(), 
               cronExpression = info.getCronExpression(),
               jobDescription = info.getJobDescription(),
               createTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        try {
            if (checkExists(jobName, jobGroup)) {
                logger.info("===> AddJob fail, job already exist, jobGroup:{}, jobName:{}", jobGroup, jobName);
                throw new ServiceException(String.format("Job已经存在, jobName:{%s},jobGroup:{%s}", jobName, jobGroup));
            }

            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
            JobKey jobKey = JobKey.jobKey(jobName, jobGroup);

            CronScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withDescription(createTime).withSchedule(schedBuilder).build();


            Class<? extends Job> clazz = (Class<? extends Job>)Class.forName(jobName);
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(jobKey).withDescription(jobDescription).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException | ClassNotFoundException e) {
            throw new ServiceException("类名不存在或执行表达式错误");
        }
    }

    /**
     * 修改定时任务
     * @param info
     * 2016年10月9日下午2:20:07
     */
    public void edit(TaskInfo info) {
        String jobName = info.getJobName(), 
               jobGroup = info.getJobGroup(), 
               cronExpression = info.getCronExpression(),
               jobDescription = info.getJobDescription(),
               createTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        try {
            if (!checkExists(jobName, jobGroup)) {
                throw new ServiceException(String.format("Job不存在, jobName:{%s},jobGroup:{%s}", jobName, jobGroup));
            }
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
            JobKey jobKey = new JobKey(jobName, jobGroup);
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withDescription(createTime).withSchedule(cronScheduleBuilder).build();

            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            jobDetail.getJobBuilder().withDescription(jobDescription);
            HashSet<Trigger> triggerSet = new HashSet<>();
            triggerSet.add(cronTrigger);

            scheduler.scheduleJob(jobDetail, triggerSet, true);
        } catch (SchedulerException e) {
            throw new ServiceException("类名不存在或执行表达式错误");
        }
    }

    /**
     * 删除定时任务
     * @param jobName
     * @param jobGroup
     * 2016年10月9日下午1:51:12
     */
    public void delete(String jobName, String jobGroup){
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        try {
            if (checkExists(jobName, jobGroup)) {
                scheduler.pauseTrigger(triggerKey);
                scheduler.unscheduleJob(triggerKey);
                logger.info("===> delete, triggerKey:{}", triggerKey);
            }
        } catch (SchedulerException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 验证是否存在
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     * 2016年10月8日下午5:30:43
     */
    private boolean checkExists(String jobName, String jobGroup) throws SchedulerException{
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        return scheduler.checkExists(triggerKey);
    }
}

 

https://github.com/leelance/spring-boot-all/tree/master/spring-boot-quartz

简单的说调度器就是: 

Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息(Scheduler运行时会执行类A的excute方法),

JobDetail描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息。

Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等

Calendar:一个Trigger可以和多个Calendar关联

Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job

SchedulerContext:它类似于ServletContext,保存着Scheduler上下文信息.

JobDataMap : qrtz_job_details表JOB_DATA字段存放了相关数据

 

在前面部分,我们知道Job中定义了实际的业务逻辑,而JobDetail包含Job相关的配置信息。在Quartz中,每次Scheduler执行Job时,在调用其execute()方法之前,它需要先根据JobDetail提供的Job类型创建一个Job class的实例,在任务执行完以后,Job class的实例会被丢弃,Jvm的垃圾回收器会将它们回收。

因此编写Job的具体实现时,需要注意:(1) 它必须具有一个无参数的构造函数;(2) 它不应该有静态数据类型,因为每次Job执行完以后便被回收,因此在多次执行时静态数据没法被维护。

Keep moving,在JobDetail中有这么一个成员JobDataMap,JobDataMap是Java Map接口的具体实现,并添加了一些便利的方法用于存储与读取原生类型数据,里面包含了当Job实例运行时,你希望提供给它的所有数据对象。

可以借助JobDataMap为Job实例提供属性/配置,可以通过它来追踪Job的执行状态等等。对于第一种情况,可以在创建Job时,添加JobDataMap数据,在Job的execute()中获取数据,第二种,则可以在Listener中通过获取JobDataMap中存储的状态数据追踪Job的执行状态。

按例,一个简单的例子:

  1. // 创建Job的实例
  2. JobDetail jobIns = JobBuilder.newJob(SimpleJob.class).withIdentity(
  3. "simpleJob", "group1").usingJobData("domain",
  4. "www.jmatrix.org").usingJobData("rank", "求别提~~~").build();

Job实现:

  1. public void execute(JobExecutionContext context)
  2. throws JobExecutionException {
  3. System.out.println("开始!");
  4.  
  5. //……JobDataMap
  6. JobDataMap dataMap = context.getJobDetail().getJobDataMap();
  7. System.out.println("域名 : "+dataMap.getString("domain"));
  8. System.out.println("排名 : "+dataMap.getString("rank"));
  9.  
  10. System.out.println("结束!");
  11. }

完成了这些工作,还需决定如何存储Job的数据,Quartz提供了JobStore接口来做这件事,如果你决定将Job数据保存在内存中,则可以使用RAMJobStore,它的优点是速度快,缺点是一旦机器挂了,Job相关的数据也丢失了,

如果要采用数据库来存储Job数据,可以使用JobStoreTX或JobStoreCMT,这取决于你采用的事务管理方式,使用RAMJobStore的话配置很简单,只需配置org.quartz.jobStore.class即可,

如果使用数据库存储,则还需要配置
driverDelegate
tablePrefix
dataSource
driverDelegate
一般情况下使用StdJDBCDelegate(MySQL便可使用这个),
特殊的可以使用Quartz提供的相关delegate,请查看jar包,一般命名就说明了一切。
TablePrefix是你的数据库表前缀,创建数据库的sql文件可以在docs\\dbTables目录下找到。
最后的数据源dataSource就有点麻烦,Quartz为用户提供了三种创建dataSource的方式:

  1. 配置相关的数据库属性(driverClass,url,username,password等),让Quartz为你创建dataSource。
  2. 通过jndi使用你应用服务器管理的dataSource。
  3. 通过实现org.quartz.utils.ConnectionProvider定制自己的datasource。

前面两种都是依据datasource的名称为其配置相关的属性,具体有哪些属性可直接参考Quartz的文档。

quartz.properties 配置:

# Main Quartz configuration
#是否跳过联网检查更新
#默认会联网检查是否有更新
org.quartz.scheduler.skipUpdateCheck = true
#调度器的实例名
#可以是你喜欢的任何字符串。它用来在用到多个调度器区分特定的调度器实例
org.quartz.scheduler.instanceName = DatabaseScheduler
#调度器的实例ID
#也允许任何字符串。这个值必须是在所有调度器实例中是唯一的,尤其是在一个集群当中
#如果 Quartz 框架是运行在非集群环境中,那么自动产生的值将会是 NON_CLUSTERED
#假如是在集群环境下使用 Quartz,这个值将会是主机名加上当前的日期和时间。大多情况下,设置为 AUTO 即可
org.quartz.scheduler.instanceId = NON_CLUSTERED
#作业存储方式
#数据库存储
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = quartzDataSource
#org.quartz.dataSource.quartzDataSource.connectionProvider.class = cn.com.quartz.QuartzDataSource
#调度器数据库表前缀
org.quartz.jobStore.tablePrefix = QRTZ_
#线程管理类
#Quartz 自带的线程池实现类
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#处理的线程个数
org.quartz.threadPool.threadCount = 10

#这是项目启动自动到数据库加载调度任务的设置,但是我没加一样可以自动初始化,设置false无效
# org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
# 作业存储数据库配置: JDBC jobStoreTX
#org.quartz.dataSource.quartzDataSource.driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
#org.quartz.dataSource.quartzDataSource.URL = jdbc:sqlserver://192.168.1.69:1433;database=DGWEB
#org.quartz.dataSource.quartzDataSource.user = sa
#org.quartz.dataSource.quartzDataSource.password = matech
#org.quartz.dataSource.quartzDataSource.driver = oracle.jdbc.driver.OracleDriver
#org.quartz.dataSource.quartzDataSource.URL = jdbc:oracle:thin:@127.0.0.1:1521:MATECH
#org.quartz.dataSource.quartzDataSource.user = matech
#org.quartz.dataSource.quartzDataSource.password = matech


#org.quartz.dataSource.quartzDataSource.driver = com.mysql.jdbc.Driver
#org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://127.0.0.1:3306/asdb
#org.quartz.dataSource.quartzDataSource.user = root
#org.quartz.dataSource.quartzDataSource.password = 123


org.quartz.dataSource.quartzDataSource.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://183.60.183.47:8098/asdb
org.quartz.dataSource.quartzDataSource.user = xoops_root
org.quartz.dataSource.quartzDataSource.password = 654321
# 最大的数据库链接数:推荐 threadCount size + 3
#org.quartz.dataSource.quartzDataSource.maxConnections = 8


#在超过它的下次触发时多少毫秒才算是错过触发 
org.quartz.jobStore.misfireThreshold = 60000
#同一时刻能处理错过触发 Trigger 的最大数量
org.quartz.jobStore.maxMisfiresToHandleAtATime = 10

 

http://blog.csdn.net/fupengyao/article/details/51645897

 

 

由于项目使用spring-boot框架,其框架是为了实现零配置文件去做开发的理念,所以在项目中集成Quartz任务调度并不像spring那样直接配置XML.

首先项目需要用到的jar包:

 

[html] view plain copy
 
  1. <dependency>  
  2.             <groupId>org.springframework</groupId>  
  3.             <artifactId>spring-context-support</artifactId>  
  4.             <version>4.1.6.RELEASE</version>  
  5.         </dependency>  
  6. <dependency>  
  7.             <groupId>org.quartz-scheduler</groupId>  
  8.             <artifactId>quartz</artifactId>  
  9.             <version>2.2.1</version>  
  10.         </dependency>  


交给spring管理的bean,代码如下

 

 

[java] view plain copy
 
  1. package com.xxx;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.mybatis.spring.annotation.MapperScan;  
  6. import org.quartz.JobDetail;  
  7. import org.quartz.Trigger;  
  8. import org.quartz.spi.JobFactory;  
  9. import org.springframework.beans.factory.annotation.Qualifier;  
  10. import org.springframework.beans.factory.annotation.Value;  
  11. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;  
  12. import org.springframework.context.ApplicationContext;  
  13. import org.springframework.context.annotation.Bean;  
  14. import org.springframework.context.annotation.ComponentScan;  
  15. import org.springframework.context.annotation.ComponentScan.Filter;  
  16. import org.springframework.context.annotation.Configuration;  
  17. import org.springframework.context.annotation.FilterType;  
  18. import org.springframework.scheduling.annotation.EnableScheduling;  
  19. import org.springframework.scheduling.quartz.CronTriggerFactoryBean;  
  20. import org.springframework.scheduling.quartz.JobDetailFactoryBean;  
  21. import org.springframework.scheduling.quartz.SchedulerFactoryBean;  
  22. import org.springframework.test.context.ContextConfiguration;  
  23. import org.springframework.test.context.web.WebAppConfiguration;  
  24. import org.springframework.transaction.annotation.EnableTransactionManagement;  
  25.   
  26. import com.xxx.base.BaseWebAppConfig;  
  27. import com.xxx.cars.quartz.AutowiringSpringBeanJobFactory;  
  28. import com.xxx.cars.quartz.SampleJob;  
  29.   
  30. @Configuration  
  31. @EnableScheduling  
  32. @ContextConfiguration  
  33. @WebAppConfiguration  
  34. @ComponentScan(basePackages = { "com.xxx" }, excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Configuration.class) })  
  35. @MapperScan("com.xxx.cars.persistence")  
  36. @EnableTransactionManagement  
  37. @EnableAutoConfiguration  
  38. public class WebAppConfig extends BaseWebAppConfig {  
  39.     /** 
  40.      * 配置拦截器 
  41.      *  
  42.      * @author jodie 
  43.      * @param registry 
  44.      */  
  45. //  public void addInterceptors(InterceptorRegistry registry) {  
  46. //      registry.addInterceptor(new UserSecurityInterceptor()).addPathPatterns(  
  47. //              "/**");  
  48. //  }  
  49.       
  50.      @Bean  
  51.         public JobFactory jobFactory(ApplicationContext applicationContext) {  
  52.             AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();  
  53.             jobFactory.setApplicationContext(applicationContext);  
  54.             return jobFactory;  
  55.         }  
  56.   
  57.         /**调度工厂bean  
  58.          * @param jobFactory  
  59.          * @param sampleJobTrigger  
  60.          * @return  
  61.          * @author LDX  
  62.          * @throws IOException  
  63.          */  
  64.         @Bean  
  65.         public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory,  
  66.                                                          @Qualifier("cronJobTrigger") Trigger cronJobTrigger) throws IOException {  
  67.             SchedulerFactoryBean factory = new SchedulerFactoryBean();  
  68.             // this allows to update triggers in DB when updating settings in config file:  
  69.             //用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了  
  70.             factory.setOverwriteExistingJobs(true);  
  71.           //用于quartz集群,加载quartz数据源  
  72. //          factory.setDataSource(dataSource);  
  73.             factory.setJobFactory(jobFactory);  
  74.             //QuartzScheduler 延时启动,应用启动完20秒后 QuartzScheduler 再启动  
  75.             factory.setStartupDelay(20);  
  76.           //用于quartz集群,加载quartz数据源配置  
  77. //          factory.setQuartzProperties(quartzProperties());  
  78.             //注册触发器  
  79.             factory.setTriggers(cronJobTrigger);  
  80.   
  81.             return factory;  
  82.         }  
  83.   
  84.         /**加载quartz数据源配置,quartz集群时用到 
  85.          * @return 
  86.          * @author LDX 
  87.          * @throws IOException 
  88.          */  
  89. //      @Bean  
  90. //      public Properties quartzProperties() throws IOException {  
  91. //          PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();  
  92. //          propertiesFactoryBean.se

    以上是关于Spring boot 2.0 Actuator 的健康检查的主要内容,如果未能解决你的问题,请参考以下文章

    Spring Boot 2.0 Actuator git 属性未添加到 /info

    Spring boot 2.0 Actuator 的健康检查

    关于spring boot启动监控端点的方法(spring-boot-starter-actuator)

    Spring Boot 2.X(十六):应用监控之 Spring Boot Actuator 使用及配置

    无法访问 Spring Boot Actuator“/actuator”端点

    Spring Boot -- actuator