精华内容
下载资源
问答
  • Django 使用定时任务的多种姿势对比

    千次阅读 2020-05-17 18:44:51
    因为django-apscheduler会创建表来存储定时任务的一些信息,所以将app加入之后需要迁移数据 python manage.py migrate 4、完整示例 在views.py中增加你的定时任务代码 注意:如果在其他文件中添加代码是没有效果

    写在前面:
    最近由于工作原因,不得不使用 Windows 系统进行 Django 开发工作,然后原来使用的django-crontab插件没办法在Windows系统上面进行定时任务。因此又想了其他方式来实现定时任务。下面就来说说这些方案的优缺点。
    首先贴上我的目录结构
    在这里插入图片描述

    1、使用django-crontab插件来实现定时任务

    1.1、安装插件

    pip install django-crontab
    

    1.2、使用插件

    在settings.py INSTALLED_APPS引入app

    INSTALLED_APPS = [
        ...
        'django_crontab'
    ]
    

    在settings.py中配置定时任务,增加一下代码

    # 定时任务
    '''
    *    *    *    *    * :分别表示 分(0-59)、时(0-23)、天(1 - 31)、月(1 - 12) 、周(星期中星期几 (0 - 7) (0 7 均为周天))
    crontab范例:
    每五分钟执行    */5 * * * *
    每小时执行     0 * * * *
    每天执行       0 0 * * *
    每周一执行       0 0 * * 1
    每月执行       0 0 1 * *
    每天23点执行   0 23 * * *
    '''
    CRONJOBS = [
        ('*/1 * * * *', 'base.crontabs.confdict_handle', ' >> /tmp/logs/confdict_handle.log'), # 注意:/tmp/base_api 目录要手动创建
    ]
    

    1.3、编写定时任务方法

    在本例中是在apps/base/crontabs.py中增加的定时任务

    from .models import ConfDict # base内的一个model,定时任务多数用来操作数据库,因此给一个示例
    import datetime
    
    # 定时任务 
    def confdict_handle():
        try:
        	objs = CondDict.objects.all()
        	print(obj)
            loca_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            print('本地时间:'+str(loca_time))
        except Exception as e:
            print('发生错误,错误信息为:', e)
    

    1.4、如何使用&运行

    开启定时器

    python manage.py crontab add  
    

    查看开启的定时器

    python manage.py crontab show  
    

    关闭定时器

    python manage.py crontab remove  
    

    1.5、优缺点

    优点:
    简单、方便、易于管理
    和django服务是分离的,不会影响到django对外提供的web服务。
    缺点:
    无法在Windows平台上运行
    就算在Linux系统上,也可能出现运行了没有效果的消息,至今未知原因。

    2、使用django-apscheduler插件实现定时任务

    2.1、安装插件

    pip install django-apscheduler
    

    2.2、使用插件

    修改settings.py增加以下代码

    INSTALLED_APPS = (
      ...
      "django_apscheduler",
    )
    

    2.3、迁移数据库

    因为django-apscheduler会创建表来存储定时任务的一些信息,所以将app加入之后需要迁移数据

    python manage.py migrate
    

    2.4、完整示例 在views.py中增加你的定时任务代码

    注意:如果在其他文件中添加代码是没有效果的

    from apscheduler.schedulers.background import BackgroundScheduler # 使用它可以使你的定时任务在后台运行
    from django_apscheduler.jobstores import DjangoJobStore, register_events, register_job
    import time
    '''
    date:在您希望在某个特定时间仅运行一次作业时使用
    interval:当您要以固定的时间间隔运行作业时使用
    cron:以crontab的方式运行定时任务
    minutes:设置以分钟为单位的定时器
    seconds:设置以秒为单位的定时器
    '''
    
    try:
        scheduler = BackgroundScheduler()
        scheduler.add_jobstore(DjangoJobStore(), "default")
    
        @register_job(scheduler, "interval", seconds=5)
        def test_job():
            # 定时每5秒执行一次
            print(time.strftime('%Y-%m-%d %H:%M:%S'))
    
        register_events(scheduler)
        # 启动定时器
        scheduler.start()
    except Exception as e:
        print('定时任务异常:%s' % str(e))
    

    2.6、如何使用&运行

    apscheduler定时任务会跟随django项目一起运行因此直接启动django即可

    python manage.py runserver
    

    2.7、优缺点

    优点:
    简单、定时方式丰富
    轻量
    缺点:
    定时任务会跟随django项目一起运行,会影响django对外提供的web服务
    使用uwsgi线上运行时,很难启动apscheduler定时任务
    按照文档上的设置--enable-threads依然没有正常启动,具体原因未知,也查了很多方法,都没有解决。并且也不建议将定时任务和uwsgi放在一起运行,这样定时任务在运行过程中可能会影响uwsgi的服务。

    3、使用Celery插件实现定时任务

    3.1、安装依赖

    本例建立在认为你已经知道如何使用Celery实现异步任务的基础上,需要学习的请移步Django使用Celery
    本例使用redis作为Borker和backend

    pip install -U "celery[redis]"
    

    3.2、使用插件

    修改settings.py 增加以下代码

    ....
    # Celery配置
    from kombu import Exchange, Queue
    # 设置任务接受的类型,默认是{'json'}
    CELERY_ACCEPT_CONTENT = ['application/json']
    # 设置task任务序列列化为json
    CELERY_TASK_SERIALIZER = 'json'
    # 请任务接受后存储时的类型
    CELERY_RESULT_SERIALIZER = 'json'
    # 时间格式化为中国时间
    CELERY_TIMEZONE = 'Asia/Shanghai'
    # 是否使用UTC时间
    CELERY_ENABLE_UTC = False
    # 指定borker为redis 如果指定rabbitmq CELERY_BROKER_URL = 'amqp://guest:guest@localhost:5672//'
    CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0'
    # 指定存储结果的地方,支持使用rpc、数据库、redis等等,具体可参考文档 # CELERY_RESULT_BACKEND = 'db+mysql://scott:tiger@localhost/foo' # mysql 作为后端数据库
    CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'
    # 设置任务过期时间 默认是一天,为None或0 表示永不过期
    CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24
    # 设置worker并发数,默认是cpu核心数
    # CELERYD_CONCURRENCY = 12
    # 设置每个worker最大任务数
    CELERYD_MAX_TASKS_PER_CHILD = 100
    # 指定任务的位置
    CELERY_IMPORTS = (
        'base.tasks',
    )
    # 使用beat启动Celery定时任务
    # schedule时间的具体设定参考:https://docs.celeryproject.org/en/stable/userguide/periodic-tasks.html
    CELERYBEAT_SCHEDULE = {
        'add-every-10-seconds': {
            'task': 'base.tasks.cheduler_task',
            'schedule': 10,
            'args': ('hello', )
        },
    }
    ...
    

    3.3、编写定时任务代码

    在本例中是在apps/base/tasks.py中增加的定时任务

    # Create your tasks here
    from __future__ import absolute_import, unicode_literals
    from celery import shared_task
    import time
    
    @shared_task
    def cheduler_task(name):
        print(name)
        print(time.strftime('%X'))
    

    3.4、如何使用&运行

    启动定时任务beat

    celery -A dase_django_api beat -l info
    

    启动Celery worker 用来执行定时任务

    celery -A dase_django_api worker -l -l info 
    

    注意:这里的 dase_django_api 要换成你的Celery APP的名称

    3.5、优缺点

    优点:
    稳定可靠、管理方便
    高性能、支持分布式
    缺点:
    实现复杂
    部署复杂
    非轻量级

    4、自建代码实现定时任务

    4.1、创建定时任务

    在apps/base下创建一个文件名为schedules.py;键入一下内容

    import os, sys, time, datetime
    import threading
    import django
    base_apth = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    # print(base_apth)
    # 将项目路径加入到系统path中,这样在导入模型等模块时就不会报模块找不到了
    sys.path.append(base_apth)
    os.environ['DJANGO_SETTINGS_MODULE'] ='base_django_api.settings' # 注意:base_django_api 是我的模块名,你在使用时需要跟换为你的模块
    django.setup()
    from base.models import ConfDict
    
    def confdict_handle():
        while True:
            try:
                loca_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                print('本地时间:'+str(loca_time))
                time.sleep(10)
            except Exception as e:
                print('发生错误,错误信息为:', e)
                continue
    
    
    def main():
        '''
        主函数,用于启动所有定时任务,因为当前定时任务是手动实现,因此可以自由发挥
        '''
        try:
            # 启动定时任务,多个任务时,使用多线程
            task1 = threading.Thread(target=confdict_handle)
            task1.start()
        except Exception as e:
            print('发生异常:%s' % str(e))
    
    if __name__ == '__main__':
        main()
    

    4.2、如何使用&运行

    直接运行脚本即可

    python apps/base/schedules.py
    

    4.3、优缺点

    优点:
    自定义
    高度自由
    缺点:
    过于简单
    当任务过多时,占用资源也会增加

    展开全文
  • Flask中使用定时任务

    千次阅读 2020-06-21 17:44:17
    定义定时任务配置类3. 启动时加载定时任务4. 定时任务中配置1. cron定时调度2. interval间隔调度3. date定时调度5. 注意事项6. 官方网址 1. 安装所需包 pip install SQLAlchemy pip install flask_apscheduler 2. ...

    1. 安装所需包

    pip install SQLAlchemy
    pip install flask_apscheduler
    

    2. 定义定时任务配置类

    from apscheduler.schedulers.background import BackgroundScheduler
    from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
    
    from schedules.task import task1, task2
    
    
    # 任务配置类
    class SchedulerConfig(object):
        # 配置执行job
        JOBS = [
             {
                'id': 'put_into_queue',
                'func': task1,
                'args': None,
                'trigger': 'interval',
                'seconds': 60 * 3 # 本任务为每3分钟执行一次
            },
            {
                'id': 'scheduler_dev_queueing',
                'func': task2,
                'args': None,
               'trigger':{ # 本任务为每周一五点五十九分四十秒执行一次
                    'type': 'cron',  # 类型
                    'day_of_week': "0", # 可定义具体哪几天要执行
                    'hour': '5',  # 小时数
                    'minute': '59',
                    'second': '40'  # "*/3" 表示每3秒执行一次,单独一个"3" 表示每分钟的3秒。现在就是每一分钟的第3秒时循环执行。
                }
            }
        ]
        # 存储位置
        SCHEDULER_JOBSTORES = {
            'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
        }
        # 线程池配置
        SCHEDULER_EXECUTORS = {
            'default': {'type': 'threadpool', 'max_workers': 20}
        }
        # 配置时区
    	SCHEDULER_TIMEZONE = 'Asia/Shanghai'
        SCHEDULER_JOB_DEFAULTS = {
            'coalesce': False,
            'max_instances': 3
        }
        # 调度器开关
        SCHEDULER_API_ENABLED = True
    

    3. 启动时加载定时任务

    if __name__ == '__main__':
        # 定时任务
        app.config.from_object(SchedulerConfig())
        # 同时指定时区,防止上下时区不一致
        scheduler = APScheduler(BackgroundScheduler(timezone="Asia/Shanghai"))
        # 注册app
        scheduler.init_app(app)
        scheduler.start()
        app.run(host="0.0.0.0", port=5001, use_reloader=False)
    

    4. 定时任务中配置

    1. cron定时调度

    • year (int|str) – 4位数年份
    • month (int|str) – 月(1-12)
    • day (int|str) – 日(1-31)
    • week (int|str) – ISO week (1-53)
    • day_of_week (int|str) – 工作日的编号或名称(0-6或周一、周二、周三、周四、周五、周六、周日)使用名称可能会报错,建议使用数字
    • hour (int|str) – hour (0-23)
    • minute (int|str) – minute (0-59)
    • second (int|str) – second (0-59)
    • start_date (datetime|str) – 最早可能触发的日期/时间(包括)
    • end_date (datetime|str) – 最晚可能触发的日期/时间(包括)
    • timezone (datetime.tzinfo|str) – 用于日期/时间计算的时区(默认为计划程序时区)

    2. interval间隔调度

    • weeks (int) – number of weeks to wait
    • days (int) – number of days to wait
    • hours (int) – number of hours to wait
    • minutes (int) – number of minutes to wait
    • seconds (int) – number of seconds to wait
    • start_date (datetime|str) – 间隔计算的起点
    • end_date (datetime|str) – 最晚可能触发的日期/时间
    • timezone (datetime.tzinfo|str) – 用于日期/时间计算的时区

    3. date定时调度

    最基本的一种调度,作业只会执行一次。它的参数如下:

    • run_date (datetime|str) – the date/time to run the job at
    • timezone (datetime.tzinfo|str) – time zone for run_date if it doesn’t have one already

    5. 注意事项

    1. 根据任务实际场景选择合适的定时任务执行方式
    2. 启动时最好设置use_reloader=False,这样就取消了重新加载机制,防止定时任务重复执行.
    3. 本示例中使用sqlite3,需要的童鞋还可以使用mysql作为定时任务存储库
    4. 定时任务出现was missed by 0:00:04.387763解决办法:
      1. 出现问题原因: 当时定时任务由于某些原因在当时错过了几秒钟才执行该任务,但是它认为已经过去了那个执行时间,所以调度失败
      2. 在SCHEDULER_JOB_DEFAULTS中添加’misfire_grace_time’: 300
      3. misfire_grace_time是进行调度可容忍时间,比如在自定义时间60s后才执行调度,那么我们设置的时间为300,该任务会重新调度.

    6. 官方网址

    flask-apscheduler扩展

    apscheduler文档

    展开全文
  • 定时任务使用,在开发中可谓是家常便饭了,定时发送邮件、短信。 避免数据库,数据表过大,定时将数据转储。通知、对账等等。 当然实现定时任务的方式也有很多,比如使用 linux 下的 contab 脚本,jdk 中自带的 ...

    前言

    定时任务的使用,在开发中可谓是家常便饭了,定时发送邮件、短信。 避免数据库,数据表过大,定时将数据转储。通知、对账等等。

    当然实现定时任务的方式也有很多,比如使用 linux 下的 contab 脚本,jdk 中自带的 Timer 类。Spring Task 或是 Quartz 。

    相信你也有过如下的疑问:

    • Spring Task 的 contab 的表达式 和 linux 下的 contab 有什么区别?
    • crontab 表达式记不住?
    • 定时任务阻塞会有什么影响?
    • 多个定时任务的情况下是如何运行的?
    • 具有相同表达式的定时任务,他们的执行顺序如何?
    • 为什么async异步任务没有生效?

    所以这篇文章,我们来介绍一下,在 Spring Task 中, 定时任务的执行原理及相关问题。演示环境为 Spring Boot 项目。

    SpringBoot 定时任务的原理

    相信绝大部分开发者都使用过Spring Boot 为我们提供的定时任务的 Starter 和定时任务的注解。所以我们来主要介绍一下 Spring Boot 实现定时任务的原理,和其相关注解的作用。

    Spring 在 3.0版本后通过 @Scheduled 注解来完成对定时任务的支持。

    file

    在我们使用时,需要在Application 启动类上加上 @EnableScheduling 注解,它是从Spring 3.1后开始提供的。

    file

    由于现在 Spring3 版本较低,使用得比较少了,可能并不会考虑太多细节,大多只需要关注目标实现,所以我们在配套使用两个注解的时候,并不会出现什么问题。

    在3.0 中 ,是通过

    <!-- 配置任务线性池 -->  
        <!-- 任务执行器线程数量 --> 
        <task:executor id="executor" pool-size="3" />  
        <!-- 任务调度器线程数量 --> 
        <task:scheduler id="scheduler" pool-size="3" />  
        <!-- 启用annotation方式 -->  
        <task:annotation-driven scheduler="scheduler"  
            executor="executor" proxy-target-class="true" /> 
    

    上述的 XML 配置 和 @Scheduled 配合实现定时任务的,而我们这里的 @EnableScheduling 其实类似的和它等价,是用来发现注解了 @Scheduled 的方法,没有这个注解光有 @Scheduled 是无法执行的,大家可以做一个简单案例测试一下,其底层是 Spring 自己实现的一套定时任务的处理逻辑,所以使用起来比较简单。

    任务一直阻塞会怎么样?

    介绍了两个注解的作用后,我们来开始做实验,简单的写一个定时执行的方法。

    file

    每隔 20s 输出一句话,在输出几行记录后,打上了一个断点。

    对后续的任务有什么影响呢?

    file

    可以看到,断点时的后续任务是阻塞着的,从图上,我们还可以看出初始化的名为pool-1-thread-1 的线程池同样证实了我们的想法,线程池中只有一个线程,创建方法是:

    Executors.newSingleThreadScheduledExecutor();
    

    从这个例子来看,断点时,任务会一直阻塞,当阻塞恢复后,会立马执行阻塞的任务。线程池内部时采用 DelayQueue 延迟队列实现的,它的特点是: 无界、延迟、阻塞的一种队列,能按一定的顺序对工作队列中的元素进行排列。
    file

    多个定时任务的执行

    通过上面的实验,我们知道,咋看默认情况下,任务的线程池,只会有一个线程来执行任务,如果有多个定时任务,它们也应该是串行执行的。

    file

    从上图可以看出,一旦线程执行任务1后,就会睡眠2分钟。线程在死循环内部一直处于Running 状态。

    file

    通过观察日志,根本没有任务2的输出,我们知道默认情况下,多个定时任务是串行执行的,类似于多辆车过单行道的桥,如果一个任务出现阻塞,其他的任务都会受到影响。

    那如果线程池包含多个线程的情况下,多个定时任务并发的情况是什么样?

    file

    串行当然很好理解,就是上文说的汽车过桥,依次通过。再来理解并发,区别于并行,并发是指一个处理器同时处理多个任务,而并行是指多个(核)处理器同时处理多个不同的任务。并发不一定同一时间发生,而并行,指的是同一时间。

    具有相同表达式的定时任务,他们的执行顺序如何?

    从上面的实验同样能知道,具有相同表达式的定时任务,还是和调度有关,如果是默认的线程池,那么会串行执行,首先获取到cpu时间片的先执行。在多线程情况下,具体的先后执行顺序和线程池线程数和所用线程池所用队列等等因素有关。

    Spring Task和linux crontab的cron语法区别?

    两者的 cron 表达式其实很相似,需要注意的是 linux 的contab 只为我们提供了最小颗粒度为分钟级的任务,而java中最小的粒度是从秒开始的。具体细节如下图:

    file

    在cron语法中容易犯的错误

    以spring 中的task为例,cron 表达式中 “/” 代表每的意思,“*/10”表示每10个单位。

    在cron 语法 中很多人会犯错误。比如要求写出每十分钟定时执行的 cron 语句,可能会有以下版本的出现:

    file

    所以当我们写完cron 表达式的时候,可以适当的调低执行间隔时间来测试,或是通过一些在线的网站来检测你的cron脚本是否正确。

    @Async异步注解原理及作用

    Spring task中 和异步相关的注解有两个 , 一个是 @EnableAsync ,另一个就是 @Async 。
    file

    首先我们单纯的在方法上引入 @Async 异步注解,并且打印当前线程的名称,实验后发现,方法仍然是由一个线程来同步执行的。

    和@schedule 类似 还是通过@Enable开头的注解来控制执行的。我们在启动类上加入@EnableAsync 后再观察输出内容。

    file

    可以发现,默认情况下,其内部是使用的名为SimpleAsyncTaskExecutor的线程池来执行任务,而且每一次任务调度,都会新建一个线程。

    使用@EnableAsync注解开启了Spring的异步功能,Spring会按照如下的方式查找相应的线程池用于执行异步方法:
    查找实现了TaskExecutor接口的Bean实例。

    如果上面没有找到,则查找名称为taskExecutor并且实现了Executor接口的Bean实例。

    如果还是没有找到,则使用SimpleAsyncTaskExecutor,该实现每次都会创建一个新的线程执行任务。

    并发执行任务如何配置?

    方式一,我们可以将默认的线程池替换为我们自定义的线程池。通过ScheduleConfig配置文件实现SchedulingConfigurer接口,并重写setSchedulerfang方法。

    可实现AsyncConfigurer接口复写getAsyncExecutor获取异步执行器,getAsyncUncaughtExceptionHandler获取异步未捕获异常处理器

    @Configurationpublic
    class ScheduleConfig implements SchedulingConfigurer {
        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
        }
    }
    
    

    方式二:不改变任务调度器默认使用的线程池,而是把当前任务交给一个异步线程池去执行。

      @Scheduled(fixedRate = 1000*10,initialDelay = 1000*20)
      @Async("hyqThreadPoolTaskExecutor")
      public void test(){
          System.out.println(Thread.currentThread().getName()+"--->xxxxx--->"+Thread.currentThread().getId());
      }
    
      //自定义线程池
      @Bean(name = "hyqThreadPoolTaskExecutor")
      public TaskExecutor  getMyThreadPoolTaskExecutor() {
          ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
          taskExecutor.setCorePoolSize(20);
          taskExecutor.setMaxPoolSize(200);
          taskExecutor.setQueueCapacity(25);
          taskExecutor.setKeepAliveSeconds(200);
          taskExecutor.setThreadNamePrefix("hyq-threadPool-");
          taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
          taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
          taskExecutor.setAwaitTerminationSeconds(60);
          taskExecutor.initialize();
          return taskExecutor;
      }
    

    其他问题

    如果是定时任务没有生效,需要检查 @EnableScheduling 注解是否加上。
    如果是异步没有生效,需要检查 @EnableAsync 注解是否加上,并且定义线程池,否则仍然是串行执行的。

    总结

    文章介绍了SpringBoot 定时任务的原理, 3.0版本前后的区别,通过单线程任务阻塞实验,探究了延迟队列及串行、并行、并发的概念。对比了linux下的 contab 和spring的cron表达式区别以及常犯的错误。最后通过实验异步注解,两种方式配置线程池,让任务高效运作,希望本文能让你有所收获。

    邀请您加入java疑难问题攻坚群,推荐阅读

    【实战】 elasticsearch 写入速度提升的案例分享

    用java做一个能赚钱的微信群聊机器人(PC协议)

    Mysql百万量级数据高效导入Redis

    可视化界面在线生成JVM参数

    java线上故障分析+性能调优

    ELK实践攻略

    展开全文
  • 使用Quartz实现定时任务(包含管理界面)

    万次阅读 多人点赞 2019-01-01 18:43:34
    因为我们项目中的定时任务就是使用Spring的@Scheduled(cron = "0 59 23 * * ?")来实现的,至于监控方面的,没有,就是通过在定时任务代码里面打一些日志,特别重要的定时任务,失败了额外发个邮件通知下,人工补偿。...

    引言

    年底出去面试的的时候,被问到如下问题: 定时任务是如何实现的?定时任务是如何监控的?因为我们项目中的定时任务就是使用Spring的@Scheduled(cron = "0 59 23 * * ?")来实现的,至于监控方面的,没有,就是通过在定时任务代码里面打一些日志,特别重要的定时任务,失败了额外发个邮件通知下,人工补偿。然后他又问了下现在需要重构定时任务,你有没有什么想法?然后我就简单的说了下,最好是提供个可视化界面,可以动态的操作定时任务,尤其是监控方面。当晚回来后找了些资料,无意间在"纯洁的微笑"博主分享的一篇博文中讲到了这个,他的博文原文地址找不到了,他上面是基于spring项目实现的,本人根据quartz的api在springboot中重新实现了个,项目地址:https://github.com/simonsfan/springboot-quartz-demo。该项目用springboot+mybatis+mysql+quartz实现的,提供定时任务可视化动态添加、修改、执行,并提供定时任务监控,界面大致如图:

    定时任务列表展示简图
    定时任务执行情况简图
    定时任务执行失败详情简图

    关于这个项目, 还有些地方需要完善,比如定时任务的查找、分页展示、我这里均没有实现,如果你在实际项目中需要使用,请自行完善下,还有比如更新定时任务执行情况记录表quartz_task_records的时机,需要再斟酌斟酌。如有任何疑问,欢迎留言讨论。


    上手Quartz

    关于quartz,可在官网http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/查看,这里着重要说的是其中几个比较重要的:

    1、maven引quartz依赖

    <!--quartz依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    

    2、quartz核心组件包括

    • Scheduler:与quartz schedule交互的主要api
    • Job:Scheduler执行组件需要实现的接口
    • JobDetail:用于定义实现了Job接口的实例
    • Trigger:用于执行给定作业的计划的组件
    • JobBuilder:用于构建JobDetail实例,或者说定义Job的实例
    • TriggerBuilder:用于构建触发器实例

    使用示例代码:

    /**
     * 实现Job接口,重写execute方法
     */
    public class QuartzJobFactory implements Job {
    
    	protected Logger logger = Logger.getLogger(QuartzJobFactory.class);
    	
    	@Override
    	public void execute(JobExecutionContext context) throws JobExecutionException {
    		//TODO 这里是定时任务逻辑
    		logger.info("=================我是定时任务,每隔5秒执行一次==============");
    	}
    }
    /**
     * @ClassName QuartzService
     * @Description 系统初始化触发定时任务
     * @Author simonsfan
     * @Date 2019/1/1
     * Version  1.0
     */
    @Component
    public class QuartzService {
    
        private static final Logger logger = LoggerFactory.getLogger(QuartzService.class);
        private final String CRON_TIME = "*/5 * * * * ?";
        private final String TRIGGER_KEY_NAME = "00000000001";
    
        @PostConstruct
        public void taskInit() {
            logger.info("=========系统初始化加载定时任务开始========");
            try {
                SchedulerFactory schedulerFactory = new StdSchedulerFactory();
                Scheduler scheduler = schedulerFactory.getScheduler();
                TriggerKey triggerKey = TriggerKey.triggerKey(TRIGGER_KEY_NAME, Scheduler.DEFAULT_GROUP);
                JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withDescription("quartz测试定制化定时任务").withIdentity(TRIGGER_KEY_NAME, Scheduler.DEFAULT_GROUP).build();
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(CRON_TIME);
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
                scheduler.start();
                logger.info("=========初始化定时任务加载完成=========");
            } catch (Exception e) {
                logger.info("==============初始化加载定时任务失败==============" + e);
                e.printStackTrace();
            }
        }
    
    }

    1、系统初始化加载这里是使用@PostConstruct注解触发,也可以通过实现InitializingBean接口的方式;

    2、调度器Scheduler实例是通过new构造的,也可以直接注入类SchedulerFactoryBean,如下

    /**
     * @ClassName QuartzService
     * @Description 系统初始化触发定时任务
     * @Author simonsfan
     * @Date 2019/1/1
     * Version  1.0
     */
    @Component
    public class QuartzService implements InitializingBean {
    
        private static final Logger logger = LoggerFactory.getLogger(QuartzService.class);
        private final String CRON_TIME = "*/5 * * * * ?";
        private final String TRIGGER_KEY_NAME = "00000000001";
    
        @Autowired
        private SchedulerFactoryBean schedulerFactoryBean;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            logger.info("=========系统初始化加载定时任务开始========");
            try {
                Scheduler scheduler = schedulerFactoryBean.getScheduler();
                TriggerKey triggerKey = TriggerKey.triggerKey(TRIGGER_KEY_NAME, Scheduler.DEFAULT_GROUP);
                JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withDescription("quartz测试定制化定时任务").withIdentity(TRIGGER_KEY_NAME, Scheduler.DEFAULT_GROUP).build();
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(CRON_TIME);
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
                logger.info("=========初始化定时任务加载完成=========");
            } catch (Exception e) {
                logger.info("==============初始化加载定时任务失败==============" + e);
                e.printStackTrace();
            }
        }
    }

    如果是spring配置文件项目,则需要将SchedulerFactoryBean注入到spring中:

     <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

    核心功能

    下面来详细讲解下这个springboot-quartz-demo里面是如何做的。还是围绕着功能点来看,如开头截图所示,主要有: 定时任务展示、新增、修改、暂停或启动、立刻运行、详情(监控),首先新建了三张表,如下:

    分别用来记录-定时任务基础信息、执行情况、执行出错信息。然后是主要的执行定时任务逻辑编写,写在实现了Job接口的QuartzMainJobFactory类中:

    定时任务主要逻辑类

    这里设置了采用Http或Kafka的方式来执行定时任务里面的具体逻辑,方式可以自己扩展或替换,主要还是针对你的定时任务类型及现有的项目架构来具体选型。

    books 1、展示和新增功能就不用说了,直接对quartz_task_informations表select和insert即可。

    books 2、初始化加载,就是说,每次重启系统,要把之前建的定时任务加载进quartz的Scheduler调度器,他这里采用的就是@PostConstruct注解,在注入service前就先加载:

    初始化加载定时任务代码简图

    books 3、修改功能,这个要注意的是修改前一定要先暂停这个定时任务才能修改,然后就是注意下并发修改的情况,加入了乐观锁控制

    修改定时任务代码简图

    books 4、实时暂停或启动定时任务,启动表示使定时任务立刻生效,就是把该定时任务加入任务调度系统中;而暂停本质就是从任务调度系统中删除此定时任务

    暂停或启动定时任务代码简图

    books 5、立刻运行定时任务,这里使用到了AtomicInteger原子类来标识"立即运行定时任务"这个操作是否出现成功,关于AtomicInteger的一些知识,请移步至https://blog.csdn.net/fanrenxiang/article/details/80623884

    立即运行一次定时任务代码简图

    books 6、监控详情,如上所说,quartz_task_records表和quartz_task_errors表分别用来记录定时任务每次执行情况和执行失败的定时任务信息,每当定时任务跑成功一次(不管成功与否)都持久化到quartz_task_records表,每失败一次(这里的标志就是AtomicInteger)持久化至quartz_task_errors表,这样每次执行的定时任务都能被比较好的"监控",这两个保存操作夹杂在"立即运行一次" 和 "QuartzMainJobFactory类" 代码中。

    定时任务执行情况简图
    定时任务执行失败原因简图

    补充:

    1、有小伙伴反馈说,当定时任务的执行时间超过了任务执行周期,这个正在执行的定时任务是什么状态?被抛弃杀死进程了?还是正常执行?关于这点,我在上面的项目测试了下,把定时任务的执行时间表达式改为"*/3 * * * * ?"每3秒执行一次定时任务,但是在任务执行逻辑中加入Thread.currentThread().sleep(5000);模拟定时任务执行时长要消耗5秒,测试结果是:超过3s的任务会继续执行,而下一个任务开始时间会变成这个任务执行完成后的5秒,依次类推,也就是说类似于时间表达式改为了"*/5 * * * * ?"的执行效果了。

    2、我自己还想到的一个问题是: 当使用http方式调用定时任务逻辑时,如果接口逻辑过于复杂导致处理时间过长,或者可能是IO密集计算型任务,这个时候怎么办?还没验证,后期再补吧。


    来波广告: 推荐个微信公众号:720电影社,里面可以免费获取电影、电视剧资源,欢迎关注。


    books quartz官网:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/

    books springboot版本项目地址:https://github.com/simonsfan/springboot-quartz-demo

    books spring项目版本地址:https://github.com/justdojava/zx-quartz

    books 推荐阅读:elastic search搜索引擎实战demo:https://github.com/simonsfan/springboot-quartz-demo,分支:feature_es

    books 推荐阅读:分布式环境下保证定时任务单点运行:https://blog.csdn.net/fanrenxiang/article/details/79990386

    展开全文
  • linux 系统定时任务 服务 详解

    千次阅读 多人点赞 2021-08-18 13:58:03
    定时任务 配置方法2.1定时任务相关文件2.2定时任务编写格式2.3 编写步骤2.4定时任务编写注意事项:(规范)总结 Centos 7 定时服务详解介绍 在企业中,存在很多数据需要备份,那么我们如何让这些数据,每天晚上23:59 ...
  • Hutool使用CronUtil实现动态定时任务

    千次阅读 2021-05-17 10:39:12
    //支持秒级别定时任务 CronUtil.setMatchSecond(true); //定时服务启动 CronUtil.start(); //使用deamon模式 //CronUtil.start(true); 按钮触发后后台执行逻辑 public String executeTask(String cronString) { ...
  • php程序已经写好了,位置:/data/html/XXX/redis_to_mysql.php,php安装位置:/app/bin/php,查找php安装位置使用 whereis php which php php -v which:这条命令主要是用来查找系统PATH目录下的可执行文件。...
  • C#使用Quartz 实现定时任务

    千次阅读 2020-06-16 23:35:20
    Quartz.net 简介 Quartz.net是一个强大、开源、轻量的作业...将要定时执行的任务代码写到Ijob接口实现的Excute方法中,时间到后会自动执行这个任务。 Demo搭建 1.新建控制台应用程序,nuget添加对Quartz.net 的引用。
  • 文章目录一、功能说明二、快速使用三、实现原理1、动态管理实现(1) 配置管理介绍(2) 使用后处理器拦截SpringBoot原本的定时任务(3) 使用ApplicationRunner初始化自定义的定时任务运行器(4) 进行动态管理2、增强接口...
  • 分布式定时任务对比

    万次阅读 多人点赞 2018-03-07 14:42:26
    1. 什么是分布式定时任务 把分散的,可靠性差的计划任务纳入统一的平台,并实现集群管理调度和分布式部署的一种定时任务的管理方式。叫做分布式定时任务。 2. 常见开源方案  elastic-job , xxl-job ,quartz ,...
  • Spring+Quartz 从数据库中获取定时任务和定时时间,动态实现对定时任务的增删改查,部署到tomcat即可看到定时任务执行效果。本人亲测,可用!
  • 在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合...
  • 前面的章节,用户通过绑定手机号的注册会员,并可以补充完个人信息,比如姓名、生日等信息,拿到用户的生日信息之后,就可以通过会员生日信息进行营销,此处就涉及到定时任务执行营销信息推送的问题。本篇就带你...
  • 分布式定时任务原理以及实现 一、单机指定时间执行定时任务实现方式 Timer运行机制 ScheduledThreadPoolExecutor的运行机制 原理图 Leader/Follower模式正在上传…重新上传取消 Timer和...
  • xxl-job 定时任务简单使用

    千次阅读 2020-11-06 16:34:08
    由于之前一直使用的是Quartz实现定时任务,本次项目中使用了xxl-job,之前没有接触过,就上网了解了下,入门还是比较简单,很快可以上手。 xxl-job 定时任务分析: XXL-JOB是一个轻量级分布式任务调度平台,调度采用...
  • 相信大家在项目开发中几乎都会用到定时任务这一功能,小编在此简单的介绍定时任务schedule的基本使用。话不多说,开始撸代码 第一步:创建一个定时任务类 @Component public class ScheduledTask { protected ...
  • 二、基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就派上用场了。 三、基于注解设定多线程定时任务 一...
  • 定时任务使用以及配置

    千次阅读 2018-02-27 10:31:19
    在我们做java开发中,常常需要用到定时任务,就是在某个特定时间段执行你需要的功能,特别是在第三方支付中,在用户使用你的某些功能如:提现,转账等等,会用到T+1来给用户做交易,这里就需要定时任务。 T+1:T指的...
  • Quartz 是个开源的作业调度框架,在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz框架包含了调度器监听、...并且还能和Spring配置整合使用。Quartz在功能上远远超越了JDK自带的Timer,很好很强大!
  • 新手, 如题,我配置完成了spring+Quartz,但是单元测试的时候只执行了一遍就退出了,请问是为什么? ![图片说明](https://img-ask.csdn.net/upload/201810/23/1540274512_264535.png) ![图片说明]...
  • 分布式定时任务

    千次阅读 2019-08-02 16:46:10
    2,为什么要采用分布式定时任务;3,怎么样设计实现一个分布式定时任务;4,当前比较流行的分布式定时任务框架; 1,什么是分布式定时任务: 首先,我们要了解计划任务这个概念,计划任务是指由计划的定时运行...
  • java定时执行多任务和quartz定时执行多任务
  • 创建Oracle定时任务

    千次阅读 2019-05-24 10:08:42
    PLSQL创建Oracle定时任务 用语句创建 1、创建任务执行的存储过程,如名称testJob,向测试表中插入数据,如文章开头,此处省略。 2、创建一个 定时任务 job declare job number; BEGIN DBMS_JOB.SUBMIT( ...
  • celery定时任务简单使用

    万次阅读 2018-07-06 14:38:30
    我们通常使用它来实现异步任务( async task )和定时任务( crontab )。 异步任务比如是发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作 ,定时任务是需要在特定时间执行的任务。它的架构组成如下图: ...
  • 开关配置springboot定时任务

    千次阅读 2019-05-31 11:22:17
    定时任务启动注解引发的开关配置思考
  • laravel 定时任务

    千次阅读 2019-03-01 14:46:08
    //对定时任务的描述并没有什么用的 protected $description = 'Command description'; /** * Create a new command instance. * * @return void */ //这个是laravel自带的构造方法。初始状态下是空的 ...
  • 定时任务Java实现

    万次阅读 2019-02-12 17:38:56
    定时任务Java实现的几种基本方法。 方法一:Thread 是我们最容易想到的,利用while循环,在其中加入sleep方法来实现定时功能。具体代码实现如下 代码示例: public class TestTimeOrder { public static void ...
  • Mac创建定时任务

    千次阅读 2020-06-07 21:57:33
    Mac 有两种方式可以添加定时任务: crontab 命令 launchctl 定时任务 crontab 命令 通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本。时间间隔的单位可以是分钟、小时、日、...
  • 安装配置 pip install django-crontab 再在settings.py中添加app: ...开始创建定时任务 在app内新建一个py文件,我在这里新建一个util.py def task(): print('我会被每分钟执行一次,并且将内容...
  • linux 定时任务实战 本文将以实例学习,如何在 linux 中定时执行脚本任务。 添加定时任务 执行命令: crontab -e 进入编辑模式,添加定时任务:  //每隔1分钟执行一次 */1 * * * * /root/test_demo.sh //...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 363,519
精华内容 145,407
关键字:

为什么使用定时任务