前情提要

最近离职在家休息,手里的资金又比较有限,水费,电费,燃气费都比较头疼,有时候电费欠费断电了才去交,然后要等5-10分钟才重新送电,再加上家里有电压保护器,就更久了,水费,燃气亦是如此。

经历定时任务事故,我学到了什么?一个案例的全面回顾

事发突然

对于我这种一般不会一次性充很多或者每月固定缴费的人来说,我没办法做到按时固定查看,可以说我有点懒。于是就想起家里有台服务器,只挂了一个NAS服务在上面,感觉到有点浪费,于是就看到宝塔面板上有定时任务管理器,前期用的感觉还不错,但是!!问题出现了,我有一次出远门直接拉闸,结果回家之后合闸听见服务器风扇狂转……

经历定时任务事故,我学到了什么?一个案例的全面回顾

于是我立刻打开电脑去看宝塔面板,首先是要我登录账号,我就有点汗流浃背了。登录之后立刻点到定时任务面板里去看,结果全没了,我以为是宝塔没了,但是思索片刻之后发现,宝塔面板的定时任务是设置到Linux的crontab命令中的。接着我抱着试试看的心态登录SSH查询了一下,确实有那么几条不认识的(看着完全不像我的)定时任务在控制台

我想:既然有记录,那不是能正常执行? 果然猜的没错,可以运行,直到我想调整定时周期,给我整暴躁了。但有人可能就说:“你为什么不直接用命令控制台呢?”,“你为什么要用图形化界面?”,“你Linux命令都不熟,怎么做开发的?”诸如此类,可是我用图形化的东西不就是图个方便么?

思考

为什么宝塔面板的定时任务查不到?

设计缺陷?容错设计?我并不清楚

为什么SSH查询的定时任务我一个都不认识?

宝塔做了一次唯一编码转换

在Linux中的定时任务是怎么保存的?

我在宝塔面板的www目录下找到了一个cron的文件夹,并发现了成对出现的定时文件,名称和SSH界面查询出来的一模一样,用文本编辑器打开,果不其然,就是我设置的定时脚本内容

既然在特定目录下,为何宝塔不识别?

我尝试添加新的定时任务,cron文件夹中又出现了新的文件。猜测是宝塔的数据和文件是分开的,就意味着不是根据动态扫描配置来实现,而是单独储存数据映射


我想到一件事,既然Linux有crontab,那Windows是不是也有类似的东西可以支持?

确实是这样

微软提供了一个图形化操作界面来管理定时任务:

经历定时任务事故,我学到了什么?一个案例的全面回顾

经历定时任务事故,我学到了什么?一个案例的全面回顾

但是,这里又有一个问题回归本质。

我现在既需要定时任务功能帮我定时查询水电燃气费,但我又得省电,用过Win的都非常清楚,一旦超过24H不关机或重启,系统就会出点小毛病,就像安卓,但我服务器又是Linux,所以我得找个解决办法……

于是,我想到了另一个问题,既然crontab系统提供的这么方便,为什么软件开发不用?(脑子抽了) 因为:集成度不高且不方便定制

经历定时任务事故,我学到了什么?一个案例的全面回顾

解决之路

于是我就开始看定时任务框架,想到了之前面试经常提到的Quartz框架。

马上就下载源码看了起来。

看了一圈发现,Quartz框架使用了多线程技术来实现任务调度。

又回归到多线程,好好好!

经历定时任务事故,我学到了什么?一个案例的全面回顾

那就顺带狠狠的让我康康!

以下是Quartz框架的一些核心组成部分及其实现原理:

  1. Scheduler(调度器) :负责整个定时任务系统的调度工作。内部通过线程池来进行任务的执行和调度管理。
  2. Trigger(触发器) :定义了调度任务的时间规则,决定何时触发任务执行。Quartz支持多种类型的触发器,如SimpleTrigger、CronTrigger等。
  3. Job(任务) :实际执行的工作单元,通常实现了特定的接口以定义任务内容。
  4. JobDetail(任务详情) :保存了Job的实例和相关的配置信息。
  5. 线程池:Quartz使用线程池来管理和执行任务,这样可以有效地复用线程资源,提高系统性能。
  6. 数据存储:Quartz允许将Trigger和Job的相关信息存储在数据库中,以实现任务的持久化,确保即使在系统宕机后,任务也能恢复执行。
  7. 集群支持:Quartz还支持集群环境下的任务调度,能够在多个节点之间协调任务的执行。
  8. 容错机制:Quartz框架提供了一些容错机制,比如在任务执行过程中发生异常时,可以记录日志并尝试重新执行任务。
  9. 负载均衡:在集群环境中,Quartz可以通过一定的策略进行负载均衡,确保任务在各个节点上均匀分配。

综上所述,Quartz框架通过这些组件和机制,提供了一个强大而灵活的任务调度平台,广泛应用于需要定时或周期性执行任务的Java应用程序中。

好嘛,这里问题又来了,多线程。如果我的定时任务体量足够大,或者说我就是喜欢玩变态的,纯靠定时任务执行逻辑,是不是又遇到了面试的经典场景?

经历定时任务事故,我学到了什么?一个案例的全面回顾

那么,来回顾一下吧!

多线程应用在CPU占用中通常通过抢占时间片来执行任务的。

在多线程环境中,CPU的时间被分割成许多小的时间片,每个线程轮流使用这些时间片来执行任务。这种机制称为时间片轮转(Time Slice Scheduling) 。以下是多线程执行的一些关键点:

  1. 线程状态:线程可以处于就绪状态、运行状态或阻塞状态。在就绪状态下,线程准备好执行并等待CPU时间片。一旦抢到时间片,线程就会进入运行状态。
  2. 抢占式多任务:为了防止线程独占CPU,操作系统采用抢占式多任务策略,允许其他线程公平地分享CPU执行时间。这意味着即使一个线程仍在运行,CPU也可能强制中断它,让其他线程执行。
  3. 线程优先级:线程的优先级影响它们抢占时间片的概率。高优先级的线程更有可能被调度执行,但这并不意味着低优先级的线程永远不会执行。
  4. 多核CPU:在多核CPU的情况下,单进程的多线程可以并发执行,而多进程的线程也可以并行执行。每个核心上的线程按照时间片轮转,但一个线程在同一时间只能运行在一个核心上。

综上所述,多线程应用确实依赖于时间片轮转机制来实现多任务并行处理,这是现代操作系统中实现多线程并发执行的基础。通过这种方式,操作系统能够有效地管理多个线程,确保CPU资源的合理分配和充分利用。

线程过多会引发什么问题呢?

线程过多确实可能导致操作系统性能的下降。当系统中存在大量线程时,可能会引发以下问题:

  • 上下文切换开销增大:操作系统需要更频繁地在线程之间切换,这种上下文切换会消耗CPU时间,降低整体的CPU利用率。
  • 内存占用增加:每个线程都有自己的栈空间,大量的线程意味着需要更多的内存来存储这些栈空间,这可能导致内存资源紧张,甚至出现内存不足的情况。
  • 垃圾回收压力增大:在Java等环境中,过多的线程会增加垃圾回收器的工作压力,进一步影响程序性能。
  • 系统稳定性降低:过多的线程竞争CPU资源时可能产生其他性能开销,严重时可能导致系统不稳定,甚至出现OutOfMemoryError异常。

为了解决这些问题,可以采取以下措施:

  • 使用线程池:线程池可以有效地管理线程资源,避免频繁创建和销毁线程的开销,同时可以控制线程数量和任务队列,提高系统性能和可靠性。
  • 合理配置线程数:根据系统的硬件配置和应用需求,合理设置线程池的核心线程数和最大线程数,以达到最优的系统吞吐量和响应时间。
  • 动态调整参数:根据实际情况动态调节线程池的参数,确保线程池处于合适的状态,避免任务堆积导致死锁或长时间停滞。

综上所述,虽然多线程可以提高程序的并发性能,但是线程数量过多确实会给操作系统带来额外的负担,可能导致性能下降。因此,合理配置和管理线程是提高系统性能的关键。

所以Quartz用的就是线程池,那线程池怎么玩?

这道题的核心就是:任务密集型和CPU密集型分别如何设置线程池

经历定时任务事故,我学到了什么?一个案例的全面回顾

先写一个解,解代表人的自信

解: 针对CPU密集型任务,线程池的设置应侧重于核心数匹配;而针对任务密集型(通常指IO密集型),线程池可配置更多的线程以利用IO等待时间。具体设置如下:

  1. CPU密集型任务
  • 线程数量:一般建议将核心线程数 (corePoolSize) 和最大线程数 (maximumPoolSize) 设置为与CPU的核心数相等。这样可以避免过多的上下文切换,因为CPU密集型任务会持续占用CPU资源进行计算。
  • 存活时间:对于CPU密集型任务,线程的存活时间不需要设置太长,因为线程通常会一直忙碌。
  1. 任务密集型(IO密集型)任务
  • 线程数量:可以设置为核心数的两倍,即如果机器有N个CPU,那么线程数可以设置为2N。这是因为在执行IO操作时,线程会经常处于等待状态,此时可以处理其他任务,所以增加线程数可以更充分地利用CPU资源。
  • 存活时间:对于IO密集型任务,可以根据实际情况适当增加线程的存活时间,以保证在需要时能够快速响应。

此外,如果任务既包含计算工作又包含IO工作,可以考虑使用两个线程池分别处理不同类型的任务,以避免相互干扰。 综上所述,合理设置线程池参数可以帮助系统高效运行,减少资源争用和性能瓶颈。

是不是一下就清晰明了,面试题也不用死记硬背了?

那回归到上面说的,我是一个变态,我就是喜欢用定时任务去执行所有逻辑,就是喜欢定时任务多到离谱,那么这个时候因为任务多到离谱,所以任务执行会有时间差,但我又要精准执行怎么办?

答:买个线程撕裂者(笑)

哥们要是那么有钱,我为什么不直接挂Win,然后再多搞几台电脑?

解决方案

手搓一个定时任务执行系统 文件系统 MySQL5 SpringBoot2.x Quartz Linux

后续如果大家也有这需求,我看情况开源给大家用

引申思考

在实际生产中,由于都是分布式的架构,那么Quartz自然就慢慢的没办法满足需求了。

甚至有些系统需要专门为定时服务准备一台专用服务器

为了解决这一问题,众多定时框架应运而生,例如:XXL-job

相比之下他们之间有什么差异呢?

Quartz XXL-job
优点 支持集群部署,能够实现高可用性和负载均衡。 是Java生态中广泛使用的定时任务标准,社区活跃,文档齐全。 可以通过数据库实现作业的高可用性。 提供了可视化的管理界面,便于任务的监控和管理。 支持集群部署,且维护成本低,提供错误预警功能。 支持分片、故障转移等分布式场景下的关键特性。 相对Quartz来说,上手更容易,适用于分布式环境。
缺点 缺少自带的管理界面,对用户而言不够直观便捷。 调度逻辑和执行任务耦合在一起,维护时需要重启服务,影响系统的连续性。 相对于其他分布式调度框架,如elastic-job,缺少分布式并行调度的功能。 需要单独部署调度中心,相对于Quartz来说,增加了部署的复杂性。

不过在现代几乎都是容器开发的方式,部署的复杂程度已经没有那么高了。

结尾

至此

祝各位工作顺利,钱多事少离家近!!!

祝各位jy们清明安康!!!

经历定时任务事故,我学到了什么?一个案例的全面回顾