当前位置: 首页 > news >正文

域名购买之后怎么做网站设计相关的网站有哪些内容

域名购买之后怎么做网站,设计相关的网站有哪些内容,麻将app软件开发价格,WordPress自动修改标签别名Quartz 数据结构 quartz采用完全二叉树#xff1a;除了最后一层每一层节点都是满的#xff0c;而且最后一层靠左排列。 二叉树节点个数规则#xff1a;每层从左开始#xff0c;第一层只有一个#xff0c;就是2的0次幂#xff0c;第二层两个就是2的1次幂#xff0c;第三…Quartz 数据结构 quartz采用完全二叉树除了最后一层每一层节点都是满的而且最后一层靠左排列。 二叉树节点个数规则每层从左开始第一层只有一个就是2的0次幂第二层两个就是2的1次幂第三层4个就是2的2次幂… quartz采用二叉树的数据结构因为二叉树有小顶堆与大顶堆的特性即把最小或者最大的节点放到最上面而quartz总是要先执行最快到时间的所以quartz去小顶堆的顶点去拿最快到期的任务去执行。 java没有支持二叉树的代码quartz将二叉树放入数组从顶点开始依照自上而下从左到右的方式存入数组中。 quartz创建新的定时任务时会放入数组最后也就是二叉树最下层然后会将这个任务节点与父节点作比较比父节点小就上浮直到不小于父节点为止 quartz执行了顶点最快到期的任务后会将顶点删除然后将最下面的节点放到顶点然后与相邻下一层的最小节点比较大于它则下沉直到沉到没有小于它的节点 整体架构 Job 定义 定时任务业务类用于执行业务逻辑你可以只创建一个job类然后创建多个与该job关联的JobDetail实例每一个实例都有自己的属性集和JobDataMap最后将所有的实例都加到scheduler中。 Job分为有状态(保存数据)和无状态(不保存数据)有状态的Job为StatefulJob接口无状态的为Job接口。 使用 需要实现Job接口重写execute方法 import org.quartz.*;public class MyJob implements Job {Overridepublic void execute(JobExecutionContext context) throws JobExecutionException { //业务代码} } 生命周期每次在调度器在执行job的时候他是在execute()方法前创建一个新的job实例JobDetail。当调用完之后关联的job对象实例会被释放释放之后将会被垃圾回收机制回收 JobDetail 定义 job的实例封装job并描述job的细节job为实际执行的业务一个job可对应多个jobdetail 使用 //MyJob为实际业务类可以从 jobDetail1 中获取任务信息 JobDetail jobDetail1 JobBuilder.newJob(MyJob.class).build();JobDataMap 定义 存储数据对象用于定时任务执行时使用在job实例对象被执行时调用可同时用于JobDetail与Trigger 使用 通过 .usingJobData(key,value)或 .JobDataMap.Put(myclass, myclass)方法赋值 还可以用此方法直接给Job业务类成员变量赋值 JobDetail jobDetail JobBuilder.newJob(MyJob.class)/**可以在业务类MyJob中通过context.getJobDetail().getJobDataMap()获取*/.usingJobData(job,jobDetail)/**可以直接赋值到业务类MyJob的name属性中*/.usingJobData(name,jobDetail).usingJobData(count1,0).build();//也可以用 jobDetail.JobDataMap.Put(myclass, myclass);Trigger trigger TriggerBuilder.newTrigger()/**可以在业务类MyJob中通过context.getTrigger().getJobDataMap()获取*/.usingJobData(trigger,trigger)/**会覆盖JobDetail中对业务类MyJob的name属性的赋值*/.usingJobData(name,trigger).startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();在业务Job类中通过JobDetailMap的getString(key)获取 public class MyJob implements Job {private String name;public void setName(String name) {this.name name;}Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println(name:name);//从触发器获取JobDataMap triggerMap context.getTrigger().getJobDataMap();//从任务获取JobDataMap jobDetailMap context.getJobDetail().getJobDataMap();System.out.println(jobDetailMap:jobDetailMap.getString(job));System.out.println(triggerMap:triggerMap.getString(trigger));} }/*** 获取JobDetail与Trigger的JobDataMap并拼到一个map中但是key重复会覆盖* */ JobDataMap mergeMap context.getMergedJobDataMap(); 序列化问题 如果你使用的是持久化的存储机制JDBCJobStore在决定JobDataMap中存放什么数据的时候需要小心因为JobDataMap中存储的对象都会被序列化因此很可能会导致类的版本不一致的问题Java的标准类型都很安全如果你已经有了一个类的序列化后的实例某个时候别人修改了该类的定义此时你需要确保对类的修改没有破坏兼容性更多细节参考下方描述。另外你也可以配置JDBC-JobStore和JobDataMap使得map中仅允许存储基本类型和String类型的数据这样可以避免后续的序列化问题。 Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较如果相同就认为是一致的可以进行反序列化否则就会出现序列化版本不一致的异常。当实现java.io.Serializable接口的实体类没有显式地定义一个名为serialVersionUID类型为long的变量时Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用这种情况下只有同一次编译生成的class才会生成相同的serialVersionUID 。即我们没有显式指定一个版本号serialVersionUID在修改序列化的类后就会反序列化失败。我们应该总是显式指定一个版本号这样做的话我们不仅可以增强对序列化版本的控制而且也提高了代码的可移植性。因为不同的JVM有可能使用不同的策略来计算这个版本号那样的话同一个类在不同的JVM下也会认为是不同的版本。Trigger 触发器定义定时任务触发规则即时间。 Trigger的公共属性 trigger的公共属性有 jobKey属性当trigger触发时被执行的job的身份startTime属性设置trigger第一次触发的时间该属性的值是java.util.Date类型表示某个指定的时间点有些类型的trigger会在设置的startTime时立即触发有些类型的trigger表示其触发是在startTime之后开始生效。比如现在是1月份你设置了一个trigger–“在每个月的第5天执行”然后你将startTime属性设置为4月1号则该trigger第一次触发会是在几个月以后了(即4月5号)。endTime属性表示trigger失效的时间点。比如”每月第5天执行”的trigger如果其endTime是7月1号则其最后一次执行时间是6月5号。 优先级(priority) 如果你的trigger很多(或者Quartz线程池的工作线程太少)Quartz可能没有足够的资源同时触发所有的trigger这种情况下你可能希望控制哪些trigger优先使用Quartz的工作线程要达到该目的可以在trigger上设置priority属性。比如你有N个trigger需要同时触发但只有Z个工作线程优先级最高的Z个trigger会被首先触发。如果没有为trigger设置优先级trigger使用默认优先级值为5priority属性的值可以是任意整数正数、负数都可以。 注意只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。 注意如果trigger是可恢复的在恢复后再调度时优先级与原trigger是一样的。 错过触发(misfire Instructions) trigger还有一个重要的属性misfire如果scheduler关闭了或者Quartz线程池中没有可用的线程来执行job此时持久性的trigger就会错过(miss)其触发时间即错过触发(misfire)。 导致misfire有三个原因 所有工作线程都忙于运行其他作业可能具有更高的优先级调度程序本身已关闭该作业是在过去的开始时间安排的可能是编码错误 不同类型的trigger有不同的misfire机制。**它们默认都使用“智能机制(smart policy)”**即根据trigger的类型和配置动态调整行为。当scheduler启动的时候查询所有错过触发(misfire)的持久性trigger。然后根据它们各自的misfire机制更新trigger的信息。当你在项目中使用Quartz时你应该对各种类型的trigger的misfire机制都比较熟悉这些misfire机制在JavaDoc中有说明。关于misfire机制的细节会在讲到具体的trigger时作介绍。 所有的trigger都有一个Trigger.MISFIRE_INSTRUCTION_SMART_POLICY 智能机制(smart policy)策略可以使用该策略也是所有trigger的默认策略。如果使用smart policySimpleTrigger会根据实例的配置及状态在所有MISFIRE策略中动态选择一种Misfire策略。 日历示例(calendar) Quartz的Calendar对象(不是java.util.Calendar对象)可以在定义和存储trigger的时候与trigger进行关联。Calendar用于从trigger的调度计划中排除时间段。比如可以创建一个trigger每个工作日的上午9:30执行然后增加一个Calendar排除掉所有的商业节日。 org.quartz.impl.calendar包下BaseCalendar 为高级的 Calendar 实现了基本的功能实现了 org.quartz.Calendar 接口AnnualCalendar 排除年中一天或多天CronCalendar 日历的这种实现排除了由给定的CronExpression表达的时间集合。 例如您可以使用此日历使用表达式“* * 0-7,18-23* *”每天排除所有营业时间上午8点至下午5点。 如果CronTrigger具有给定的cron表达式并且与具有相同表达式的CronCalendar相关联则日历将排除触发器包含的所有时间并且它们将彼此抵消。DailyCalendar 您可以使用此日历来排除营业时间上午8点 - 5点每天。 每个DailyCalendar仅允许指定单个时间范围并且该时间范围可能不会跨越每日边界即您不能指定从上午8点至凌晨5点的时间范围。 如果属性invertTimeRange为false默认则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。HolidayCalendar 特别的用于从 Trigger 中排除节假日MonthlyCalendar 排除月份中的指定数天例如可用于排除每月的最后一天WeeklyCalendar 排除星期中的任意周几例如可用于排除周末默认周六和周日使用Calendar的步骤较为简单 第一步创建Calendar, 并添加到Scheduler中。 DailyCalendar calendar new DailyCalendar(9:22:00,9:30:00); scheduler.addCalendar(calendar, calendar, false, false);第二步使用TriggerBuilder方法时添加modifiedbyCalendar参数为calendar的名称。 return TriggerBuilder.newTrigger().withIdentity(test trigger, test).startNow().withSchedule(simpleSchedule().repeatSecondlyForTotalCount(6).withIntervalInMinutes(1)).modifiedByCalendar(calendar).build();Trigger种类 五种类型的 Trigger(2.3.2版本)SimpleTriggerCronTrigger,CalendarIntervalTriggerDailyTimeIntervalTriggerMutableTrigger,OperableTrigger 最常用的 SimpleTrigger(简单触发器)进行简单的触发仅需触发一次或者以固定时间间隔周期执行如每日的5点执行一次每分钟执行一次CronTrigger表达式触发器进行复杂的触发如每月的第几周第几天什么时候执行 /**StartAt() 表示触发器的时间表首次被触发的时间。它的值的类型是java.util.Date。EndAt() 指定触发器的不再被触发的时间。它的值是java.util.Date。 */ Trigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1,trigger1)/**立即执行*/.startNow()/**简单调度每秒执行一次*/.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build(); SimpleTrigger SimpleTrigger可以满足的调度需求是在具体的时间点执行一次或者在具体的时间点执行并且以指定的间隔重复执行若干次。 SimpleTrigger的属性包括开始时间、结束时间、重复次数以及重复的间隔。重复次数可以是0、正整数以及常量SimpleTrigger.REPEAT_INDEFINITELY。重复的间隔必须是0或者long型的正数表示毫秒。注意如果重复间隔为0trigger将会以重复次数并发执行(或者以scheduler可以处理的近似并发数)。 指定时间开始触发不重复(执行一次) SimpleTrigger trigger (SimpleTrigger) newTrigger() .withIdentity(trigger1, group1).startAt(myStartTime) // some Date .forJob(job1, group1) // identify job with name, group strings.build();指定时间触发每隔10秒执行一次重复10次 trigger newTrigger().withIdentity(trigger3, group1).startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), now is implied.withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings.forJob(myJob) // identify job with handle to its JobDetail itself .build();5分钟以后开始触发仅执行一次 trigger (SimpleTrigger) newTrigger() .withIdentity(trigger5, group1).startAt(futureDate(5, IntervalUnit.MINUTE)) // use DateBuilder to create a date in the future.forJob(myJobKey) // identify job with its JobKey.build();立即触发每个5分钟执行一次直到22:00 trigger newTrigger().withIdentity(trigger7, group1).withSchedule(simpleSchedule().withIntervalInMinutes(5).repeatForever()).endAt(dateOf(22, 0, 0)).build();建立一个触发器将在下一个小时的整点触发然后每2小时重复一次 trigger newTrigger().withIdentity(trigger8) // because group is not specified, trigger8 will be in the default group.startAt(evenHourDate(null)) // get the next even-hour (minutes and seconds zero (00:00)).withSchedule(simpleSchedule().withIntervalInHours(2).repeatForever())// note that in this example, forJob(..) is not called which is valid // if the trigger is passed to the scheduler along with the job .build();scheduler.scheduleJob(trigger, job);请查阅TriggerBuilder和SimpleScheduleBuilder提供的方法以便对上述示例中未提到的选项有所了解。 TriggerBuilder(以及Quartz的其它builder)会为那些没有被显式设置的属性选择合理的默认值。比如如果你没有调用withIdentity(..)方法TriggerBuilder会为trigger生成一个随机的名称如果没有调用startAt(..)方法则默认使用当前时间即trigger立即生效。SimpleTrigger的Misfire策略常量 MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT在使用SimpleTrigger构造trigger时misfire策略作为基本调度(simple schedule)的一部分进行配置(通过SimpleSchedulerBuilder设置) trigger newTrigger().withIdentity(trigger7, group1).withSchedule(simpleSchedule().withIntervalInMinutes(5).repeatForever().withMisfireHandlingInstructionNextWithExistingCount()).build();CronTrigger CronTrigger通常比Simple Trigger更有用如果您需要基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行重新启动的作业启动计划。 使用CronTrigger您可以指定号时间表例如“每周五中午”或“每个工作日和上午9:30”甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。 即使如此和SimpleTrigger一样CronTrigger有一个startTime它指定何时生效以及一个可选的endTime用于指定何时停止计划。 cron表达式生成器https://cron.qqe2.com/ 建立一个触发器每隔两分钟每天上午8点至下午5点之间 trigger newTrigger().withIdentity(trigger3, group1).withSchedule(cronSchedule(0 0/2 8-17 * * ?)).forJob(myJob, group1).build();建立一个触发器将在上午10:42每天发射 trigger newTrigger().withIdentity(trigger3, group1).withSchedule(dailyAtHourAndMinute(10, 42)).forJob(myJobKey).build();或者 trigger newTrigger().withIdentity(trigger3, group1).withSchedule(cronSchedule(0 42 10 * * ?)).forJob(myJobKey).build();建立一个触发器将在星期三上午10:42在TimeZone系统默认值之外触发 trigger newTrigger().withIdentity(trigger3, group1).withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42)).forJob(myJobKey).inTimeZone(TimeZone.getTimeZone(America/Los_Angeles)).build();或者 trigger newTrigger().withIdentity(trigger3, group1).withSchedule(cronSchedule(0 42 10 ? * WED)).inTimeZone(TimeZone.getTimeZone(America/Los_Angeles)).forJob(myJobKey).build();CronTrigger的Misfire指令常数 MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_DO_NOTHING MISFIRE_INSTRUCTION_FIRE_NOW在构建CronTriggers时您可以将misfire指令指定为简单计划的一部分通过CronSchedulerBuilder trigger newTrigger().withIdentity(trigger3, group1).withSchedule(cronSchedule(0 0/2 8-17 * * ?).withMisfireHandlingInstructionFireAndProceed()).forJob(myJob, group1).build();Scheduler 定义 调度器通过线程池进行任务调度按照Trigger定义的时间执行Job它是单例的。 Scheduler 中的方法主要分为三大类: 操作调度器本身例如调度器的启动 start()、调度器的关闭 shutdown()。操作 Trigger例如 pauseTriggers()、resumeTrigger()。操作 Job例如 scheduleJob()、unscheduleJob()、rescheduleJob() 使用 默认情况下StdSchedulerFactory从当前工作目录加载“quartz.properties”文件。如果加载失败那么就会尝试加载org/quartz包下的“quartz.properties”文件。如果不想用默认的文件你可以定义一个系统属性“org.quartz.properties”指向你想要的文件。 try {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();//加载quartz自带的org.quartz.properties//结合了jobDetail与trigger进行调度scheduler.scheduleJob(jobDetail,trigger);scheduler.start();} catch (SchedulerException e) {e.printStackTrace();}Scheduler 的生命期 Scheduler 的生命期从 SchedulerFactory 创建它时开始到 Scheduler 调用shutdown() 方法时结束Scheduler 被创建后可以增加、删除和列举 Job 和 Trigger以及执行其它与调度相关的操作如暂停 Trigger。但是Scheduler 只有在调用 start() 方法后才会真正地触发 trigger即执行 job Scheduler 创建 Scheduler接口有两个实现类分别为StdScheduler标准默认调度器和RemoteScheduler远程调度器 常用的是StdSchedulerFactory 1.通过DirectSchedulerFactory创建一个实例 public static void main(String[] args) {try {DirectSchedulerFactory schedulerFactory DirectSchedulerFactory.getInstance();// 表示以3个工作线程初始化工厂schedulerFactory.createVolatileScheduler(3);Scheduler scheduler schedulerFactory.getScheduler(); } catch (SchedulerException e) {e.printStackTrace();}}创建步骤  1、通过DirectSchedulerFactory的getInstance方法得到拿到实例  2、调用createXXX方法初始化工厂  3、调用工厂实例的getScheduler方法拿到调度器实例 可以看出DirectSchedulerFactory是通过createXXX方法传递配置参数来初始化工厂这种初始化方式是一种硬编码在工作中用到的情况会很少。 2.使用StdSchedulerFactory工厂创建 此工厂是依赖一系列的属性来决定如何创建调度器实例的。 属性提供的方式有三种  1、通过java.util.Properties属性实例  2、通过外部属性文件提供  3、通过有属性文件内容的 java.io.InputStream 文件流提供 public static void main(String[] args) {try {StdSchedulerFactory schedulerFactory new StdSchedulerFactory();// 第一种方式 通过Properties属性实例创建Properties props new Properties();props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, org.quartz.simpl.SimpleThreadPool);props.put(org.quartz.threadPool.threadCount, 5);schedulerFactory.initialize(props);// 第二种方式 通过传入文件名// schedulerFactory.initialize(my.properties);// 第三种方式 通过传入包含属性内容的文件输入流// InputStream is new FileInputStream(new File(my.properties));// schedulerFactory.initialize(is);// 获取调度器实例Scheduler scheduler schedulerFactory.getScheduler();} catch (Exception e) {e.printStackTrace();}}JobStore数据库连接 Jobstore用来存储任务和触发器相关的信息例如所有任务的名称、数量、状态等等。Quartz中有两种存储任务的方式一种在在内存一种是在数据库。详细见下方。 其他 QuartzSchedulerThread负责执行向QuartzScheduler注册的触发Trigger的工作的线程。 ThreadPoolScheduler使用一个线程池作为任务运行的基础设施任务通过共享线程池中的线程提供运行效率。 QuartzSchedulerResources包含创建QuartzScheduler实例所需的所有资源JobStoreThreadPool等。 SchedulerFactory 用于获取调度器实例。 JobStore 通过类实现的接口这些类要为org.quartz.core.QuartzScheduler的使用提供一个org.quartz.Job和org.quartz.Trigger存储机制。作业和触发器的存储应该以其名称和组的组合为唯一性。 QuartzScheduler 这是Quartz的核心它是org.quartz.Scheduler接口的间接实现包含调度org.quartz.Jobs注册org.quartz.JobListener实例等的方法。 Scheduler 这是Quartz Scheduler的主要接口代表一个独立运行容器。调度程序维护JobDetails和触发器的注册表。 一旦注册调度程序负责执行作业当他们的相关联的触发器触发当他们的预定时间到达时。 Trigger 具有所有触发器通用属性的基本接口描述了job执行的时间出发规则。 - 使用TriggerBuilder实例化实际触发器。 JobDetail 传递给定作业实例的详细信息属性。 JobDetails将使用JobBuilder创建/定义。 Job要由表示要执行的“作业”的类实现的接口。只有一个方法 void execute(jobExecutionContext context) (jobExecutionContext 提供调度上下文各种信息运行时数据保存在jobDataMap中) Job有个子接口StatefulJob ,代表有状态任务。有状态任务不可并发前次任务没有执行完后面任务处于阻塞等到。 一个job可以被多个Trigger 绑定但是一个Trigger只能绑定一个job Scheduler可以同时调度多组Trigger 及JobDetail Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();scheduler.scheduleJob(jobDetail,trigger);scheduler.scheduleJob(jobDetail,trigger);scheduler.start();Job并发及持久化 DisallowConcurrentExecution 将该注解加到job类上告诉Quartz不要并发地执行同一个job定义这里指特定的job类的多个实例。 此注解加在Job类上但实际运行生效的是JobDetail scheduler是默认多线程并发访问资源的 可以避免争抢及定时任务堵塞. 比如前一个任务没执行完间隔时间就过了又来了下一个此时下一个正常执行不等上一个执行完再执行除非使用DisallowConcurrentExecution注解 此注解会保证必须上一个任务执行完成后在执行下一个即使超过了间隔时间如果超时会在执行完立刻执行下一次不会再等过了间隔时间再执行. 比如间隔为1秒上个任务执行了3秒3秒后会立即执行下一个任务而不是等4秒再执行PersistJobDataAfterExecution Job分为有状态(保存数据)和无状态(不保存数据)有状态的Job为StatefulJob接口无状态的为Job接口。 无状态任务在执行时拥有自己的JobDataMap拷贝对JobData的更改不会影响下次的执行。而有状态任务共享同一个JobDataMap实例每次任务执行对JobDataMap所做的更改都会保存下来后面的执行可以看到这个更改。也就是每次执行任务后都会对后面的执行发生影响。 正因为这个原因无状态的Job可以并发执行而有状态的StatefulJob不能并发执行这意味着如果前次的StatefulJob还没有执行完毕下一次的任务将阻塞等待直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素程序往往拥有更高的复杂度因此除非必要应该尽量使用无状态的Job。 在quartz的2.3.2版本中StatefulJob已取消可以使用PersistJobDataAfterExecution实现有状态 PersistJobDataAfterExecution告诉Quartz在成功执行了Job实现类的execute方法后没有发生任何异常更新JobDetail中JobDataMap的数据使得该JobDetail实例在下一次执行的时候JobDataMap中是更新后的数据而不是更新前的旧数据。 而有状态任务共享共享同一个JobDataMap实例每次任务执行对JobDataMap所做的更改会保存下来后面的执行可以看到这个更改也即每次执行任务后都会对后面的执行发生影响。以下代码示例中JobDetail的count会累加Trigger不会 public class TestJob {public static void main(String[] args) {JobDetail jobDetail JobBuilder.newJob(MyJob.class).withIdentity(job1,group1).usingJobData(count1,0).build();int count0;Trigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1,trigger1).usingJobData(count,count)/**立即执行*/.startNow()/**简单调度每秒执行一次*/.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();try {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();scheduler.scheduleJob(jobDetail,trigger);scheduler.start();} catch (SchedulerException e) {e.printStackTrace();}} }PersistJobDataAfterExecution //只对JobDetail有持久化作用对Trigger没有 public class MyJob implements Job {private String name;public void setName(String name) {this.name name;}Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {JobDataMap triggerMap context.getTrigger().getJobDataMap();JobDataMap jobDetailMap context.getJobDetail().getJobDataMap();triggerMap.put(count,triggerMap.getInt(count)1);jobDetailMap.put(count1,jobDetailMap.getInt(count1)1);System.out.println(triggerMap count:triggerMap.getInt(count));System.out.println(jobDetailMap count:jobDetailMap.getInt(count1));} }使用建议 如果你使用了PersistJobDataAfterExecution注解则强烈建议你同时使用DisallowConcurrentExecution注解因为当同一个jobJobDetail的两个实例被并发执行时由于竞争JobDataMap中存储的数据很可能是不确定的。 JobStore数据库连接 Jobstore用来存储任务和触发器相关的信息例如所有任务的名称、数量、状态等等。Quartz中有两种存储任务的方式一种在在内存一种是在数据库。 RAMJobStore Quartz默认的 JobStore是 RAMJobstore也就是把任务和触发器信息运行的信息存储在内存中用到了 HashMap、TreeSet、HashSet等等数据结构。 如果程序崩溃或重启所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。 JDBCJobStore JDBCJobStore可以通过 JDBC接口将任务运行数据保存在数据库中。 DataSource设置有两种方法 一种方法是让Quartz创建和管理DataSource即在quartz.properties中配置数据源 另一种是由Quartz正在运行的应用程序服务器管理的DataSource通过应用管理数据源比如springboot应用在yml中设置数据库连接在quartz中注入DataSource使用。 示例为quartz.properties #数据库中 quartz表的表名前缀 org.quartz.jobStore.tablePrefix:QRTZ_ #数据源名字,要与下方配置名字一致 org.quartz.jobStore.dataSource:myDS #配置数据源(此处是否可以不定义而是定义在application.properties中? 待试验) org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/test?useUnicodetruecharacterEncodingutf8 org.quartz.dataSource.myDS.user:root org.quartz.dataSource.myDS.password:123456 org.quartz.dataSource.myDS.validationQueryselect 0 from dual配置JDBCJobStore以使用DriverDelegate即数据库代理 org.quartz.jobStore.driverDelegateClass org.quartz.impl.jdbcjobstore.StdJDBCDelegate接下来您需要通知JobStore您正在使用的表前缀如上所述。 使用表前缀配置JDBCJobStore org.quartz.jobStore.tablePrefix QRTZ_JDBC的实现方式有两种JobStoreSupport类的两个子类 JobStoreTX在独立的程序中使用自己管理事务不参与外部事务。 org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX JobStoreCMT(Container Managed Transactions (CMT)如果需要容器管理事务时使用它。使用 JDBCJobSotre时需要配置数据库信息 org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreCMT注意问题 后台报错Table seata_order.qrtz_locks doesnt exist 检查两处地方第一处是application中的 spring.datasource.urljdbc:mysql://42.193.104.62:3306/***? # ***应为对应数据库的名字第二处是数据库的配置文件是否开启了不区分大小写 quartz表 在官网的 Downloads链接中提供了 11张表的建表语句 quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables 2.3的版本在这个路径下src\org\quartz\impl\jdbcjobstore 表名与作用 配置文件详解 线程池配置 #是要使用的ThreadPool实现的名称。Quartz附带的线程池是“org.quartz.simpl.SimpleThreadPool”并且几乎能够满足几乎每个用户的需求。它有非常简单的行为并经过很好的测试。它提供了一个固定大小的线程池。 org.quartz.threadPool.classorg.quartz.simpl.SimpleThreadPool#可用于并发执行作业的线程数至少为1无默认值 org.quartz.threadPool.threadCount5#设置线程的优先级可以是Thread.MIN_PRIORITY即1和Thread.MAX_PRIORITY这是10之间的任何int 。默认值为Thread.NORM_PRIORITY5。 org.quartz.threadPool.threadPriority1#使池中的线程创建为守护进程线程。默认为“false” org.quartz.threadPool.makeThreadsDaemonsfalse#在工作池中的线程名称的前缀将被附加一个数字。 org.quartz.threadPool.threadNamePrefix1#可以是java线程的有效名称的任何字符串。如果未指定此属性线程将接收调度程序的名称“org.quartz.scheduler.instanceName”加上附加的字符#串“_QuartzSchedulerThread”。 org.quartz.scheduler.threadName _QuartzSchedulerThreadJobStore配置 # 数据保存方式为数据库持久化并由quartz自己管理事务 org.quartz.jobStore.class org.quartz.impl.jdbcjobstore.JobStoreTX# 数据保存方式为数据库持久化并由容器管理事务 org.quartz.jobStore.class org.quartz.impl.jdbcjobstore.JobStoreCMT# 数据库代理类一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库 #用于完全符合JDBC的驱动程序可用于oracle、mysql org.quartz.jobStore.driverDelegateClass org.quartz.impl.jdbcjobstore.StdJDBCDelegate #对于Microsoft SQL Server和Sybase org.quartz.jobStore.driverDelegateClass org.quartz.impl.jdbcjobstore.MSSQLDelegate #其他参见官方文档#数据库中 quartz表的表名前缀 org.quartz.jobStore.tablePrefix:QRTZ_ #数据源名字,要与下方配置名字一致 org.quartz.jobStore.dataSource:myDS #配置数据源 org.quartz.dataSource.myDS.drivercom.mysql.jdbc.Driver org.quartz.dataSource.myDS.URLjdbc:mysql://localhost:3306/test?useUnicodetruecharacterEncodingutf8 org.quartz.dataSource.myDS.userroot org.quartz.dataSource.myDS.password123456 #是可选的SQL查询字符串用于检测连接是否失败。 org.quartz.dataSource.myDS.validationQueryselect 0 from dual#当调查器检测到JobStore中的连接丢失例如数据库时调度程序重试等待时间以毫秒为单位 org.quartz.scheduler.dbFailureRetryInterval 6000 其他配置 #使JobDataMaps中的所有值都将是“字符串”避免了序列化问题 org.quartz.jobStore.usePropertiesfalse#定义了触发器应该多长时间才被认为触发失败默认为60000一分钟 org.quartz.jobStore.misfireThreshold 60000#Scheduler一次获取trigger的最大数量。默认值为1。这个数字越大触发效率越高在有许多trigger需要同时触发的场景下但是在集群节点之间可能会有负 #载均衡的代价。如果这个属性的值大于1且使用JDBCJobStore那么属性“org.quartz.jobStore.acquireTriggersWithinLock”必须设置true以避免数据损 #坏。 org.quartz.scheduler.batchTriggerAcquisitionMaxCount 1#防止多个线程同时拉取相同的trigger的情况也就避免的重复调度的危险 org.quartz.jobStore.acquireTriggersWithinLock true集群配置 #是否加入集群 true是 false否 org.quartz.jobStore.isClustered true# 调度标识名 集群中每一个实例都必须使用相同的名称 org.quartz.scheduler.instanceName ClusterQuartz# 调度器ID设置为自动获取 每一个必须不同 org.quartz.scheduler.instanceId AUTO#仅当org.quartz.scheduler.instanceId设置为“AUTO” 时才使用。默认为“org.quartz.simpl.SimpleInstanceIdGenerator”它根据主机名和时间戳生成实例#ID。其他IntanceIdGenerator实现包括SystemPropertyInstanceIdGenerator它从系统属性“org.quartz.scheduler.instanceId”获取实例ID#HostnameInstanceIdGenerator使用本地主机名 org.quartz.scheduler.instanceIdGenerator.class org.quartz.simpl.SimpleInstanceIdGenerator其他参考 https://blog.csdn.net/bobozai86/article/details/123777036 核心机制 流程 Quartz的核心流程大致分为三个阶段: 获取调度实例阶段 通过getScheduler 方法根据配置文件加载配置和初始化创建线程池 ThreadPool(默认是SimpleThreadPool用来执行Quartz调度任务)创建调度器 QuartzScheduler创建调度线程 QuartzSchedulerThread并将调度线程初始状态设置为暂停状态。 绑定JobDetail和Trigger阶段 Scheduler将任务添加到JobStore中如果是使用数据库存储信息这时候会把任务持久化到Quartz核心表中同时也会对实现JobListener的监听者通知任务已添加 启动调度器阶段 Scheduler会调用QuartzScheduler的Start()方法这时候会把调度线程从暂停切为启动状态通知QuartzSchedulerThread正式干活。QuartzSchedulerThread会从SimpleThreadPool查看下有多少可用工作线程然后找JobStore去拿下一批符合条件的待触发的Trigger任务列表包装成FiredTriggerBundle。通过JobRunShellFactory创建FiredTriggerBundle的执行线程实例JobRunShell然后把JobRunShell实例交给SimpleThreadPool的工作线程去执行。SimpleThreadPool会从可用线程队列拿出对应数量的线程去调用JobRunShell的run()方法此时会执行任务类的execute方法 : job.execute(JobExecutionContext context)。 线程模型 SimpleThreadPool包工头管理所有 WorkerThreadWorkerThread工人 执行JobRunShellJobRunShell任务任务中有run()方法会执行业务类的execute方法 : job.execute(JobExecutionContext context)。QuartSchedulerThread项目经理获取即将触发的 Trigger将JobRunShell交给SimpleThreadPool由SimpleThreadPool调用WorkerThread执行JobRunShell Quartz集群进程间如何通信 Quartz集群之间是通过数据库几张核心的Quartz表进行通信 Quartz集群如何保证高并发下不重复跑 Quartz有多个节点同时在运行而任务是共享的这时候肯定存在资源竞争问题容易造成并发问题Quartz节点之间是否存在分布式锁去控制 Quartz是通过数据库去作为分布式锁来控制多进程并发问题Quartz加锁的地方很多Quartz是使用悲观锁的方式进行加锁让在各个instance操作Trigger任务期间串行这里挑选核心的代码来看看它是符合利用数据库防止并发的。 使用数据库锁需要在quartz.properties中加以下配置让集群生效Quartz才会对多个instance进行并发控制 org.quartz.jobStore.isClustered trueQRTZ_LOCKS 表它会为每个调度器创建两行数据获取 Trigger 和触发 Trigger 是两把锁加锁入口在JobStoreSupport类中Quartz提供的锁表为多个节点调度提供分布式锁实现分布式调度默认有2个锁 SCHED_NAMELOCK_NAMEMyschedulerSTATE_ACCESSMyschedulerTRIGGER_ACCESS STATE_ACCESS主要用在scheduler定期检查是否失效的时候保证只有一个节点去处理已经失效的scheduler TRIGGER_ACCESS主要用在TRIGGER被调度的时候保证只有一个节点去执行调度 Quartz集群如何保证高并发下不漏跑 有时候Quartz可能会错过我们的调度任务: 服务重启没能及时执行任务就会misfire工作线程去运行优先级更高的任务就会misfire任务的上一次运行还没结束下一次触发时间到达就会misfire Quartz可提供了一些补偿机制应对misfire情况用户可以根据需要选择对应的策略 Quartz常见问题 服务器始终不一致问题 常见异常: This scheduler instance (SchedulerName) is still active but was recovered by another instance in the cluster解决: 同步所有集群节点的时间然后重启服务 Quartz集群负载不均衡 Quartz集群是采用抢占式加锁方式去处理任务因此你会看到每个节点的任务处理日志并不是均衡分配的很可能一个节点会抢占大量任务导致负载过重但是这一点官方并没有解决。 错过预定触发时间 常见异常: Handling 1 trigger(s) that missed their scheduled fire-time解决: 很可能是你线程数设置太少而任务执行时间太长超过的misfire阈值导致线程池没有可用线程而错过了触发事件。尝试把配置文件线程数调大org.quartz.threadPool.threadCount 或者把misfire阈值调大org.quartz.jobStore.misfireThreshold 简单使用 写业务类job import org.quartz.*;DisallowConcurrentExecution PersistJobDataAfterExecution //只对JobDetail有持久化作用对Trigger没有 public class MyJob implements Job {private String name;public void setName(String name) {this.name name;}Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println(name:name);//从触发器中获取数据JobDataMap triggerMap context.getTrigger().getJobDataMap();//从任务中获取数据JobDataMap jobDetailMap context.getJobDetail().getJobDataMap();System.out.println(jobDetailMap:jobDetailMap.getString(job));System.out.println(triggerMap:triggerMap.getString(trigger));/*** 获取JobDetail与Trigger的JobDataMap并拼到一个map中但是key重复会覆盖* */JobDataMap mergeMap context.getMergedJobDataMap();triggerMap.put(count,triggerMap.getInt(count)1);jobDetailMap.put(count1,jobDetailMap.getInt(count1)1);System.out.println(triggerMap count:triggerMap.getInt(count));System.out.println(jobDetailMap count:jobDetailMap.getInt(count1));} }定义触发器与业务实例并调度 import org.quartz.*; import org.quartz.impl.StdSchedulerFactory;public class TestJob {public static void main(String[] args) {JobDetail jobDetail JobBuilder.newJob(MyJob.class).withIdentity(job1,group1)/**可以在业务类MyJob中通过context.getJobDetail().getJobDataMap()获取*/.usingJobData(job,jobDetail)/**可以直接赋值到业务类MyJob的name属性中*/.usingJobData(name,jobDetail).usingJobData(count1,0).build();int count0;Trigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1,trigger1)/**可以在业务类MyJob中通过context.getTrigger().getJobDataMap()获取*/.usingJobData(trigger,trigger).usingJobData(count,count)/**会覆盖JobDetail中对业务类MyJob的name属性的赋值*///.usingJobData(name,trigger)/**立即执行*/.startNow()/**简单调度每秒执行一次*/.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();try {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();scheduler.scheduleJob(jobDetail,trigger);/*** scheduler是默认多线程并发访问资源的 可以避免争抢及定时任务堵塞* 比如前一个任务没执行完间隔时间就过了又来了下一个此时下一个正常执行不等上一个执行完再执行** 除非使用DisallowConcurrentExecution注解* 此注解会保证必须上一个任务执行完成后在执行下一个即使超过了间隔时间如果超时会在执行完立刻执行下一次不会再等过了间隔时间* 再执行比如间隔为1秒执行了3秒3秒后会立即执行而不是等4秒再执行* */scheduler.start();} catch (SchedulerException e) {e.printStackTrace();}} }quartz整合springboot pom dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-autoconfigure/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-quartz/artifactId/dependency/dependencies业务类定义 import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; import org.springframework.scheduling.quartz.QuartzJobBean;import java.util.Date;PersistJobDataAfterExecution DisallowConcurrentExecution public class QuartzJob extends QuartzJobBean {Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {try {Thread.sleep(2000);System.out.println(context.getScheduler().getSchedulerInstanceId());System.out.println(tasknamecontext.getJobDetail().getKey().getName());System.out.println(执行时间new Date());} catch (Exception e) {e.printStackTrace();}} }配置类 import org.quartz.Scheduler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.quartz.SchedulerFactoryBean;import javax.sql.DataSource; import java.io.IOException; import java.util.Properties; import java.util.concurrent.Executor;Configuration public class SchedulerConfig {/*** 注入应用的数据源* */Autowiredprivate DataSource dataSource;/*** 配置线程池* Runtime.getRuntime().availableProcessors() 获取的是cpu核心线程数也就是计算资源。* */Beanpublic Executor schedulerThreadPool(){ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor();executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());executor.setQueueCapacity(Runtime.getRuntime().availableProcessors());return executor;}/*** 从自定义的properties加载quartz配置* */Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource(/spring-quartz.properties));propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}/*** 创建schedulerFactoryBean工厂实例用于获取scheduler* */Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory new SchedulerFactoryBean();factory.setSchedulerName(cluster_scheduler);factory.setDataSource(dataSource);factory.setApplicationContextSchedulerContextKey(application);factory.setQuartzProperties(quartzProperties());factory.setTaskExecutor(schedulerThreadPool());factory.setStartupDelay(0);return factory;}/*** 从工厂实例获取scheduler* */Beanpublic Scheduler scheduler() throws IOException {return schedulerFactoryBean().getScheduler();} }创建调度及触发 import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component;/*** ApplicationListener ContextRefreshedEvent 一般被用于在项目初始化动作完成后执行的自己业务拓展动作* 实现onApplicationEvent(ContextRefreshedEvent event)方法应用一启动就会执行此方法* */ Component public class StartApplicationListener implements ApplicationListenerContextRefreshedEvent {Autowiredprivate Scheduler scheduler;Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {try {TriggerKey triggerKey TriggerKey.triggerKey(trigger1,group1);Trigger trigger scheduler.getTrigger(triggerKey);if(trigger null){trigger TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(0/10 * * * * ?)).startNow().build();JobDetail jobDetail JobBuilder.newJob(QuartzJob.class).withIdentity(job1,group1).build();scheduler.scheduleJob(jobDetail,trigger);}TriggerKey triggerKey2 TriggerKey.triggerKey(trigger2,group2);Trigger trigger2 scheduler.getTrigger(triggerKey2);if(trigger2 null) {trigger2 TriggerBuilder.newTrigger().withIdentity(triggerKey2).withSchedule(CronScheduleBuilder.cronSchedule(0/10 * * * * ?)).startNow().build();JobDetail jobDetail2 JobBuilder.newJob(QuartzJob.class).withIdentity(job2, group2).build();scheduler.scheduleJob(jobDetail2, trigger2);}scheduler.start();} catch (SchedulerException e) {e.printStackTrace();}} }
http://www.dnsts.com.cn/news/97135.html

相关文章:

  • 张店网站建设定制网站分为哪些结构
  • 苏州信网网站建设技术有限公司wordpress 识别pc手机版
  • 做网站的职业叫什么培训网站制作
  • 镇江网站制作价格官方网站建设的四个步骤
  • 购物网站的建设费用网站备案初审时间
  • 网站建设捌金手指花总二五怎样做一个企业的网站建站
  • 重庆微信网站开发做景区网站建设的公司
  • 辽宁网站建设多少钱域名经纪公司推荐
  • 医院网站建设思路短网址压缩
  • 重庆市建设厅网站首页wordpress手机软件
  • 创业过程中网站建设按城市亭湖建设局网站
  • 无锡网站建站公司安卓程序开发用什么软件
  • 开发一个网站多少钱啊wordpress 广告联盟
  • 周口集团网站建设房间设计图软件
  • 海南平台网站建设平台七牛 wordpress
  • 网站建设管理实训报告wordpress改dz
  • 免费的舆情网站运维有限公司
  • 网站开发主要做哪些怎么做网站网站不被发现
  • 镇平建设局网站wordpress获取文章标签
  • 小木桥路建设工程招投标网站wordpress 点点主题
  • 网站维护一般怎么做建网站需多少钱
  • 云平台网站开发安顺做网站
  • 企业网站开发项目策划书写文章的网站
  • 如何在虚拟机中建设网站网站建设需要域名服务器
  • 做铝材哪些网站招聘宜兴建设局 网站
  • 东莞网站建设工作企业网站案例欣赏
  • 随州网站建设多少钱忘记了wordpress登录密码怎么办?
  • 烟台做网站哪里好酒店网站规划建设方案书
  • 网站表单点击切换广州多少网络科技有限公司
  • 菜单设计制作网站网站建设与开发大作业