java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起

  • 之前做项目时,系统因为种种原因容易网页崩溃,上不来数据,后经检查,重启一下阿里云服务器就可以了。SO,写了个重启脚本,自动重启比咱们手动重启不香多了:
    • 实现步骤:
      • 步骤一:创建两个脚本文件:tomcat_shutdown.sh和tomcat_startup.sh,文件放哪里都行,暂放在/home/Slove_Tomcat_AutoStart下
        具体教程见此文章:https://blog.csdn.net/lupangdelu/article/details/45563085
        这个文章最后让修改XXX.sh文件的权限,改成可执行的,命令为:chmod 777 XXX.sh
      • 步骤二:运行crontab -e,可以编辑定时器:编辑启动.sh文件的具体时间等参数:52 20 * * * /home/Slove_Tomcat_AutoStart/tomcat_shutdown.sh 意思是20点52关tomcat;54 20 * * * /home/Slove_Tomcat_AutoStart/tomcat_startup.sh 意思是20点54重新开tomcat
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第1张图片
        • :wq保存,退出
        • Linux crontab是用来定期执行程序的命令。当安装完成操作系统之后,默认便会启动此crontab任务调度命令crond命令每分钟会定期检查是否有要执行的工作,如果有要执行的工作便会自动执行该工作。【Linux crontab 是用来定期执行程序的命令,当安装完成操作系统之后,默认便会启动此任务调度命令。】
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第2张图片
          • 而linux任务调度的工作主要分为以下两类:
            • 系统执行的工作:系统周期性所要执行的工作,如备份系统数据、清理缓存
            • 个人执行的工作:某个用户定期要做的工作,例如每隔10分钟检查邮件服务器是否有新信,这些工作可由每个用户自行设置。或者像咱们这个文章中要干的三件事
      • 步骤三:crontab -l查询刚编辑好的定时器成功了吗
        • 在线Cron表达式生成器,可以在线生成参数比较方便
      • 步骤四:编辑完脚本后,重启一下脚本服务:systemctl restart crond service或者/bin/systemctl restart crond service
    • 除了这个阿里云重启脚本,tomcat自动重启脚本书写,以及pg数据库的自动备份脚本。自己有如下体验:
      • 线上系统日志清理:我们一般对线上 ZooKeeper 等服务器日志进行维护的主要方式是备份和清理。几乎所有的生产系统都会产生日志文件,用来记录服务的运行状态,在服务发生异常的时候,可以用来作为分析问题原因的依据。【备份是为了之后对系统的运行情况进行排查和优化,而清理主要因为随着系统日志的增加,日志会逐渐占用系统的存储空间,如果一直不进行清理,可能耗尽系统的磁盘存储空间,并最终影响服务的运行。】但在实际工作中,我们不能 24 小时监控系统日志情况。所以可以用定时任务来自动清理和备份Zookeeper等服务运行产生的相关日志。
  • 另外,我感觉重要的点包含以下几个:
    • 1.写脚本文件时,一般放在root底下,当时感觉是为了齐整一点,给root地下建一些二级三级啥的目录,放各种脚本文件
    • 2.在脚本文件中,有作者时间等注释,然后的话就是正常的逻辑判断,数据库备份(pg_dump 从哪个IP哪个端口备份到哪个IP哪个端口,什么名,后缀是什么。)或者启动阿里云(主要是/sbin/reboot命令的执行)
    • 3.主要是将自动重启或者自动备份过程中的某些操作输出到,当时是在/mnt/数据盘地下有一些二级目录,将产生的操作日志文件以及数据备份文件放到这里面
    • 4.再就是碰到有的情况,比如说备份数据库的命令直接执行./…sh脚本文件可以产生备份文件和日志文件,但是放到crontab地下,就只能产生日志文件或者都不产生,当时的解决方法就是脚本文件中或者crontab文件中的路径要用绝对路径,另外就是因为crontab执行计划任务时crontab不会从用户的profile文件中读取环境变量,所以会导致命令执行失败,所以要在脚本文件中加上一个source /etc/profile,大概就是上面两种情况。
    • 5.加一个crontab定时任务:
      • 定时清理tomcat的日志文件中的catalina.out:0 3 * * * root echo > /usr/local/java/apache-tomcat-7.0.57/logs/catalina.out。保存后systemctl reload crond.service重启一下cron就行
    • 6.有时需要临时统计线上的数据,然后导出到excel表格中。这种需求有时较为复杂,光靠写sql语句是无法满足需求的,这就需要写java代码了。然后将该程序打成一个jar包,在线上环境执行,最后将生成的excel文件下载到本地
      • 按照上面的经验或者说思路,可以利用linux系统的crontab,运行crontab -e,可以编辑定时器,然后加入如下命令:0 2 * * * /usr/local/java/jdk1.8/bin/java -jar /data/app/tool.jar > /logs/tool.log &。就可以 在每天凌晨2点,定时执行tool.jar程序,并且把日志输出到tool.log文件中。当然你也可以把后面的执行java程序的命令写成shell脚本,更方便维护
  • Shell脚本:学这个Shell,身边怎么能没有一本菜鸟教程呢!!!
    • 在项目中也就是上面那三板斧,用到:令人监视服务器状态,如果服务器停止了,自动半夜三点重启、数据库的数据备份、定期删除日志文件
    • Shell编程就是 对一堆Linux命令的逻辑化处理
      java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第3张图片
      • 目前 Linux 系统下最流行的运维自动化语言就是 Shell 和 Python 了:
        • Shell 几乎是 IT 企业必须使用的运维自动化编程语言,特别是在运维工作中的服务监控、业务快速部署、服务启动停止、数据备份及处理、日志分析等环节里,shell 是不可缺的。Shell 是一个命令解释器,解释执行用户所输入的命令和程序。一输入命令,就立即回应的交互的对话方式
        • Python 更适合处理复杂的业务逻辑,以及开发复杂的运维软件工具,实现通过 web 访问等。
    • Shell 变量&Shell运算符&Shell流程控制&Shell函数
      • Shell 变量
        • Shell 编程中一般分为三种变量:
          • 我们自己定义的变量(自定义变量): 仅在当前 Shell 实例中有效,其他 Shell 启动的程序不能访问局部变量。
            在这里插入图片描述
          • Linux 已定义的环境变量(环境变量, 例如:PATH, ​HOME 等…, 这类变量我们可以直接使用),使用 env 命令可以查看所有的环境变量,而 set 命令既可以查看环境变量也可以查看自定义变量。
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第4张图片
            • 比如我们要看当前用户目录可以使用:echo $HOME命令;如果我们要看当前用户 Shell 类型 可以使用echo $SHELL命令。可以看出,使用方法非常简单。
          • Shell 变量 :Shell 变量是由 Shell 程序设置的特殊变量。Shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 Shell 的正常运行
        • Shell 字符串:字符串可以用单引号,也可以用双引号【在单引号中所有的特殊符号,如 和反引号都没有特殊含义。在双引号中,除了 " 和反引号都没有特殊含义。在双引号中,除了" 和反引号都没有特殊含义。在双引号中,除了"“,”"和反引号,其他的字符没有特殊含义。】
          在这里插入图片描述
          在这里插入图片描述
          • 拼接字符串:
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第5张图片
          • 获取字符串长度:
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第6张图片
          • 截取子字符串:
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第7张图片
        • Shell 数组:bash 支持一维数组(不支持多维数组),并且没有限定数组的大小。主要就是如何创建数组、获取数组长度、获取/删除特定位置的数组元素、删除整个数组以及遍历数组
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第8张图片
      • 菜鸟教程上的Shell 运算符
      • 菜鸟教程上的Shell 流程控制
      • 菜鸟教程上的Shell 函数
    • 实战一下,输出一个Hello World:
      java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第9张图片
      • (1)新建一个文件 helloworld.sh :touch helloworld.sh,扩展名为 sh(sh代表Shell)(扩展名并不影响脚本执行
      • (2) 使脚本具有执行权限:chmod +x helloworld.sh
      • (3) 使用 vim 命令修改helloworld.sh文件
        • (vim 文件------>进入文件----->命令模式------>按 i 进入编辑模式----->编辑文件 ------->按 Esc 进入底行模式----->输入:wq/q!输入 wq 代表写入内容并退出,即保存;输入 q!代表强制退出不保存。))
      • (4) 运行脚本:./helloworld.sh 。(注意,一定要写成 ./helloworld.sh ,而不是 helloworld.sh ,运行其它二进制的程序也一样,直接写 helloworld.sh ,linux 系统会去 PATH 里寻找有没有叫 helloworld.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 helloworld.sh 是会找不到命令的,要用./helloworld.sh 告诉系统说,就在当前目录找。)
        • 我们要看当前用户目录可以使用:echo $HOME命令;如果我们要看当前用户Shell类型 可以使用echo $SHELL命令。。
  • Java定时任务:
    • 非常常见的业务场景:要求我们在某个特定的时间去做某个事情。除了上面项目中涉及到的之外
      java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第10张图片
      • SpringBoot 实现定时任务:
        • 方法一: 借助cron表达式且都提前定义好放在配置文件里,不能在项目运行中动态修改任务执行时间实在不太灵活
          • 第一步 ,在pom.xml文件中引入spring-context相关依赖。
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第11张图片
          • 第二步 ,在springboot启动类上加上@EnableScheduling注解
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第12张图片
          • 第三步 ,使用@Scheduled注解定义定时规则。
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第13张图片
          • 第四步 ,在applicationContext.properties文件中配置参数【sue.spring.task.cron=*/10 * * * * ?】:每隔10秒执行一次fun方法了
        • 方法二:基于注解(@Scheduled): 基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第14张图片
          • 使用@Scheduled 注解很方便,但缺点是当我们调整了执行周期的时候,需要重启应用才能生效,这多少有些不方便。为了达到实时生效的效果,可以使用接口来完成定时任务。
        • 方法三:基于接口(SchedulingConfigurer): 用于实现从数据库获取指定时间来动态执行定时任务
          • 使用时,第一步肯定还是 导入依赖包
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第15张图片
          • 添加数据库记录:开启本地数据库mysql,顺便打开查询窗口,然后执行脚本内容,后面就可以然后打开Navicat ,比如说将执行周期修改为每6秒执行一次,不需要我们重启应用,十分方便
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第16张图片
            • 然后在项目中的application.yml 添加数据源
              java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第17张图片
          • 创建定时器:数据库准备好数据后,开始编写定时任务,注意这里添加的是TriggerTask,目的是循环读取我们在数据库设置好的执行周期,以及执行相关定时任务的内容
            @Configuration      //1.主要用于标记配置类,兼备Component的效果。
            @EnableScheduling   // 2.开启定时任务
            public class DynamicScheduleTask implements SchedulingConfigurer {
             
                @Mapper
                public interface CronMapper {
                    @Select("select cron from cron limit 1")
                    public String getCron();
                }
             
                @Autowired      //注入mapper
                @SuppressWarnings("all")
                CronMapper cronMapper;
             
                /**
                 * 执行定时任务.
                 */
                @Override
                public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
             
                    taskRegistrar.addTriggerTask(
                            //1.添加任务内容(Runnable)
                            () -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
                            //2.设置执行周期(Trigger)
                            triggerContext -> {
                                //2.1 从数据库获取执行周期
                                String cron = cronMapper.getCron();
                                //2.2 合法性校验.
                                if (StringUtils.isEmpty(cron)) {
                                    // Omitted Code ..
                                }
                                //2.3 返回执行周期(Date)
                                return new CronTrigger(cron).nextExecutionTime(triggerContext);
                            }
                    );
                }
             
            }
            
        • 方法四:基于注解设定多线程定时任务:
          • 创建多线程定时任务
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第18张图片
      • SpringBoot 设置动态定时任务:上面有时候由于要借助cron表达式且都提前定义好放在配置文件里,不能在项目运行中动态修改任务执行时间,实在不太灵活。
        • 第一步:避免不了的还是引入需要的依赖:
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第19张图片
        • 第二步:启动类的编写
          @EnableScheduling
          @SpringBootApplication
          public class DemoByHuApplication {
           
              public static void main(String[] args) {
                  SpringApplication.run(DemoByHuApplication.class, args);
                  System.out.println("xxx");
              }
          }
          
        • 第三步:各种配置文件的编写:
          • application.yml,只定义了服务端口:
            在这里插入图片描述
          • 定时任务执行时间配置文件:task-config.ini:printTime.cron=0/10 * * * * ?
        • 第四步:各种执行类以及工具类的编写:
          • 定时任务执行类:
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第20张图片
            • 除了上面的借助cron表达式的方法,还有另一种触发器,区别于CronTrigger触发器,该触发器可随意设置循环间隔时间,不像cron表达式只能定义小于等于间隔59秒。
              @Data
              @Slf4j
              @Component
              @PropertySource("classpath:/task-config.ini")
              public class ScheduleTask implements SchedulingConfigurer {
               
                  @Value("${printTime.cron}")
                  private String cron;
               
                  private Long timer = 10000L;
               
                  @Override
                  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
                      // 动态使用cron表达式设置循环间隔
                      taskRegistrar.addTriggerTask(new Runnable() {
                          @Override
                          public void run() {
                              log.info("Current time: {}", LocalDateTime.now());
                          }
                      }, new Trigger() {
                          @Override
                          public Date nextExecutionTime(TriggerContext triggerContext) {
                              // 使用CronTrigger触发器,可动态修改cron表达式来操作循环规则
              //                CronTrigger cronTrigger = new CronTrigger(cron);
              //                Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
               
                              // 使用不同的触发器,为设置循环时间的关键,区别于CronTrigger触发器,该触发器可随意设置循环间隔时间,单位为毫秒
                              PeriodicTrigger periodicTrigger = new PeriodicTrigger(timer);
                              Date nextExecutionTime = periodicTrigger.nextExecutionTime(triggerContext);
                              return nextExecutionTime;
                          }
                      });
                  }
              }
              
          • 编写一个接口,使得可以通过调用接口动态修改该定时任务的执行时间:
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第21张图片
    • 单机定时任务技术选型【适用于比较简单的定时任务场景比如每天凌晨备份一次数据】【不论是使用 Timer 还是 ScheduledExecutorService 都无法使用 Cron 表达式指定任务执行的具体时间
      • jdk自带的定时任务中Thread类真的能做定时任务。一些定时任务框架的底层也会使用Thread类。
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第22张图片
        • 使用场景:比如项目中有时需要每隔10分钟去下载某个文件,或者每隔5分钟去读取模板文件生成静态html页面等等,一些简单的周期性任务场景不支持指定某个时间点执行任务,不支持延迟执行等操作,功能过于单一,无法应对一些较为复杂的场景
      • java.util.Timer是 JDK 1.3 开始就已经支持的一种定时任务的实现方式。Timer类是jdk专门提供的定时器工具,用来在后台线程计划执行指定任务,在java.util包下,要跟TimerTask一起配合使用
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第23张图片
        • Timer 内部使用一个叫做 TaskQueue 的类存放定时任务,它是一个基于最小堆实现的优先级队列。TaskQueue 会按照任务距离下一次执行时间的大小将任务排序,保证在堆顶的任务最先执行。这样在需要执行任务时,每次只需要取出堆顶的任务运行即可
          • 先实例化一个Timer类,然后调用它的schedule方法,在该方法中实例化TimerTask类,业务逻辑写在run方法中。schedule方法最后的两次参数分别表示:延迟时间 和 间隔时间,单位是毫秒。比如,设置的定时任务是每隔1秒执行一次,延迟2秒执行
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第24张图片
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第25张图片
        • 比如创建一个 1s 之后执行的定时任务。
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第26张图片
      • ScheduledThreadPoolExecutor 支持多线程执行定时任务并且功能更强大,是 Timer 的替代品。自从JDK1.5之后,提供了ScheduledExecutorService代替TimerTask来执行定时任务,提供了不错的可靠性
        • ScheduledExecutorService 是一个接口,有多个实现类,比较常用的是 ScheduledThreadPoolExecutor 。
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第27张图片
        • ScheduledThreadPoolExecutor 本身就是一个线程池,支持任务并发执行。并且,其内部使用 DelayQueue 作为任务队列
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第28张图片
      • Spring Task:直接通过 Spring 提供的 @Scheduled 注解即可定义定时任务【Spring Task 底层是基于 JDK 的 ScheduledThreadPoolExecutor 线程池来实现的。】,非常方便!
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第29张图片
        • Spring Task 还是支持 Cron 表达式 的。Cron 表达式主要用于定时作业(定时任务)系统定义执行时间或执行频率的表达式,非常厉害,你可以通过 Cron 表达式进行设置定时任务每天或者每个月什么时候执行等等操作。Spring Framework自带定时任务,提供了cron表达式来实现丰富定时任务配置。
          • 咱们要学习定时任务的话,Cron 表达式是一定是要重点关注的。一个在线 Cron 表达式生成器以及这个网站可以用来匹配你的cron表达式。
        • Spring自带的定时调度优点是简单,轻量,支持 Cron 表达式。但是,Spring 自带的定时调度只支持单机,并且提供的功能比较单一单点的定时服务在目前微服务的大环境下,应用场景越来越局限,所以尝鲜一下分布式定时任务吧。
      • 基于 Redis 实现:这种基于Redis的实现可以通过多点来增加定时任务多点消费。但是要做好防范重复消费的准备
        • 通过ZSet的方式:定时任务存放到ZSet集合中,并且将过期时间存储到ZSet的Score字段中,然后通过一个循环来判断当前时间内是否有需要执行的定时任务,如果有则进行执行
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第30张图片
        • 键空间通知的方式:我们可以通过Redis的键空间通知来实现定时任务,它的实现思路是给所有的定时任务设置一个过期时间,等到了过期之后,我们通过订阅过期消息就能感知到定时任务需要被执行了,此时我们执行定时任务即可。默认情况下Redis是不开启键空间通知的,需要我们通过config set notify-keyspace-events Ex的命令手动开启
      • 时间轮(HashedWheelTimer)
        • Kafka、Dubbo、ZooKeeper、Netty 、Caffeine 、Akka 中都有对时间轮的实现。
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第31张图片
          • 那当我们需要创建一个 13s 后执行的定时任务怎么办呢?这个时候可以引入一叫做 圈数/轮数 的概念,也就是说这个任务还是放在下标为 3 的时间格中, 不过它的圈数为 2
          • 除了增加圈数这种方法之外,还有一种 多层次时间轮 (类似手表),Kafka 采用的就是这种方案
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第32张图片
          • 实现时间轮:Netty中已经有了一个时间轮的实现, HashedWheelTimer.java,可以参考它的源代码。时间轮的优点是性能高,插入和删除的时间复杂度都是O(1)。Linux 内核中的定时器采用的就是这个方案。
    • 分布式定时任务技术选型【如果我们需要一些高级特性比如支持任务在分布式场景下的分片和高可用的话,我们就需要用到分布式任务调度框架了。】。也可以是这样:引入分布式定时任务组件or中间件。将定时任务作为单独的服务,遏制了重复消费,独立的服务也有利于扩展和维护
      • 通常情况下,一个定时任务的执行往往涉及到下面这些角色:
        • 任务 : 首先肯定是要执行的任务,这个任务就是具体的业务逻辑比如定时发送文章。
        • 调度器 :其次是调度中心,调度中心主要负责任务管理,会分配任务给执行器。
        • 执行器 : 最后就是执行器,执行器接收调度器分派的任务并执行
      • Quartz 是一个完全由Java写出来的开源任务调度框架,可以说是 Java 定时任务领域的老大哥或者说参考标准,其他的任务调度框架基本都是基于 Quartz 开发的,比如当当网的elastic-job就是基于quartz二次开发之后的分布式调度解决方案。
        • 使用 Quartz 可以很方便地与 Spring 集成,并且支持动态添加任务和集群。但是,Quartz 使用起来也比较麻烦【Quartz 依赖于MySQL,使用相对简单,可多节点部署,通过竞争数据库锁来保证只有一个节点执行任务。但是Quartz 没有图形化管理页面,使用相对麻烦。】,API 繁琐。另外,Quartz 虽然也支持分布式任务。但是,它是在数据库层面,通过数据库的锁机制做的,有非常多的弊端比如系统侵入性严重、节点负载不均衡。有点伪分布式的味道
        • springboot集成quartz
          • 第一步 ,在pom.xml文件中引入quartz相关依赖。
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第33张图片
          • 第二步 ,创建真正的定时任务执行类,该类继承QuartzJobBean
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第34张图片
          • 第三步 ,创建调度程序JobDetail和调度器Trigger。
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第35张图片
          • 第四步 ,在applicationContext.properties文件中配置参数:【sue.spring.quartz.cron=*/5 * * * * ?】,每隔5秒执行一次QuartzTestJob类的executeInternal方法了
            java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第36张图片
      • Elastic-Job 是当当网开源的一个基于Quartz和ZooKeeper的分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和 Elastic-Job-Cloud 组成,一般我们只要使用 Elastic-Job-Lite 就好。ElasticJob 支持任务在分布式场景下的分片和高可用、任务可视化管理等功能。
        • Elastic-Job依赖于Zookeeper,通过zookeeper的注册与发现,可以动态的添加服务器
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第37张图片
      • LTS:依赖于Zookeeper,集群部署,可以动态的添加服务器。可以手动增加定时任务,启动和暂停任务。
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第38张图片
      • xxl-job:国产,依赖于MySQL,基于竞争数据库锁保证只有一个节点执行任务,支持水平扩容。可以手动增加定时任务,启动和暂停任务。微服务下,推荐使用xxl-job这一类组件服务将定时任务合理有效的管理起来。而单点的定时任务有其局限性,适用于规模较小、对未来扩展要求不高的服务
        java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第39张图片
        • xxl-job的难度主要体现在集成和调试上。无论是什么样的定时任务,你都需要确保:任务不会因为集群部署而被多次执行、任务发生异常得到有效的处理、任务的处理过慢导致大量积压、任务应该在预期的时间点执行【中间件可以将服务解耦,但增加了复杂度】
      • 如何设计一个分布式的定时任务调度器:Redis ZSet, RabbitMQ
  • 那如果咱们自己考虑实现一个定时任务调度器该怎么搞。有很多任务,每个任务都有一个时间戳,任务会在该时间点开始执行
    • 生活中的例子很多:
      • 就像排队答到一样,原理差不多嘛,A七点整答到,B七点零一答到,C起点零二答到…
      • 淘宝购物15天后默认好评
      • Uber打车48小时后自动好评
    • 实现方案们如下:
      • 方案一:PriorityBlockingQueue + Polling
        • 用一个java.util.concurrent.PriorityBlockingQueue来作为优先队列。因为我们需要一个优先队列,又需要线程安全,用PriorityBlockingQueue再合适不过了。【也可以手工实现一个自己的PriorityBlockingQueue,用java.util.PriorityQueue + ReentrantLock,用一把锁把这个队列保护起来,就是线程安全的啦】
        • 对于生产者,可以用一个while(true),造一些随机任务塞进去
        • 对于消费者,起一个线程,在 while(true)里每隔几秒检查一下队列,轮询(polling)嘛,如果有任务,则取出来执行
          • 轮询通常有个很大的缺点,就是时间间隔不好设置,间隔太长,任务无法及时处理,间隔太短,会很耗CPU。
      • 方案二:PriorityBlockingQueue + 时间差
        • 把方案1改进一下,while(true)里的逻辑变成:
          • 偷看一下堆顶的元素,但并不取出来,如果该任务过期了,则取出来
          • 如果没过期,则计算一下时间差,然后开始sleep掉这段时间差,也就是装作不知道有这个时间差。但是方案二缺点就是,假设当前堆顶的任务在100秒后执行,消费者线程peek()偷看到了后,开始sleep 100秒,这时候一个新的任务插了进来,该任务在50秒后应该执行,但是由于消费者线程要睡眠100秒,这个新任务无法及时处理
      • 方案三:Java中的DelayQueue
        • DelayQueue可以看做是一个特化版的PriorityBlockingQueue,它把计算时间差并让消费者等待该时间差的功能集成进了队列,消费者不需要关心时间差的事情了,消费者直接在while(true)里不断take()就行了
        • DelayQueue的实现原理:
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第40张图片
        • Task对象:JDK中有一个接口java.util.concurrent.Delayed,可以用于表示具有过期时间的元素,刚好可以拿来表示任务这个概念
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第41张图片
        • 生产者:就是一个死循环,不断地产生一些是时间随机的任务
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第42张图片
        • 消费者:当 DelayQueue 里没有任务时,TaskConsumer会无限等待,直到被唤醒,因此它不会消耗CPU。
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第43张图片
        • 定时任务调度器:
          java基础巩固-宇宙第一AiYWM:为了维持生计,做项目经验之~SSM项目错误集锦Part1(自动定时重启tomcat+阿里云+pg数据库自动备份脚本、Java定时任务->Shell脚本)~整起_第44张图片
      • 方案四:时间轮(HashedWheelTimer),往上翻翻

巨人的肩膀:
https://blog.csdn.net/lupangdelu/article/details/45563085
https://www.cnblogs.com/shineman-zhang/articles/15165981.html
javaGuide老师写的关于Java定时任务的详细文章

你可能感兴趣的:(java,数据库,tomcat)