精华内容
下载资源
问答
  • SpringBoot2 与 Quartz 整合的Demo。 后台可添加、修改、移除 定时任务。 也可查看当前任务的状态 灵活的定时任务
  • C#实现的自定义定时任务 可定时运行 多任务运行
  • C#Quartz定时任务

    2017-10-24 13:22:56
    C#编写的定时任务, 开发环境vs2010, demo可以直接运行, 所需的dll依赖库在bin目录下
  • java定时任务,每天定时执行任务,每天到这个时间点都会执行
  • 应用quartz实现了多任务并发执行,定时时间从数据库获取,并且能够在更改数据库执行时间后重新执行定时任务,实现了quartz定时任务的动态管理。本次为了实现功能,代码并未作优化,仅作参考借鉴之用。
  • 此工具稳定,能在所有win系列,linux系列执行定时,而且不用安装.net等工具,是代替系统定时任务的理想工具!
  • app定位+定时提交坐标信息到服务器
  • 在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合...
  • android定时开关机服务,通过广播设置重启/开关机服务,在国内某大型公司设备中有应用
  • 基于thinkphp框架的定时器(定时执行任务)
  • Spring+Quartz 从数据库中获取定时任务和定时时间,动态实现对定时任务的增删改查,部署到tomcat即可看到定时任务执行效果。本人亲测,可用!
  • ThinkPHP实现定时执行任务DEMO1_v3.2.1
  • maven工程-基于springboot定时任务

    千次下载 热门讨论 2016-07-21 18:18:01
    基于springboot的定时任务实现
  • android 每日定时功能

    千次下载 热门讨论 2013-11-26 11:07:28
    android 每日定时功能,简单闹铃实现,重复闹铃实现。
  • AlarmManager实现精准定时任务

    千次下载 热门讨论 2014-11-03 17:11:22
    app进程中,需要实现一个定时任务,只要设备处于开机状态,每隔半个小时,就需要定时向服务器上传一次位置信息,并且只要是有网络和获取到GPS信号,进程不能死,如果进程死掉了,需要自动重启。
  • 采用Quartz.NET开源作业调度框架,实现简单的定时任务。 配合Topshelf.NET,直接安装Windows服务。
  • 前面的章节,用户通过绑定手机号的注册为会员,并可以补充完个人信息,比如姓名、生日等信息,拿到用户的生日信息之后,就可以通过会员生日信息进行营销,此处就涉及到定时任务执行营销信息推送的问题。本篇就带你...

    前面的章节,用户通过绑定手机号的注册为会员,并可以补充完个人信息,比如姓名、生日等信息,拿到用户的生日信息之后,就可以通过会员生日信息进行营销,此处就涉及到定时任务执行营销信息推送的问题。本篇就带你走入微服务下的定时任务的构建问题。

    定时任务选型

    常见的定时任务的解决方案有以下几种:

    右半部分基于 Java 或 Spring 框架即可支持定时任务的开发运行,左侧部分需要引入第三方框架支持。针对不同方案,作个简单介绍

    • XXL-JOB 是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。任务调度与任务执行分离,功能很丰富,在多家公司商业产品中已有应用。官方地址:https://www.xuxueli.com/xxl-job/
    • Elastic-Job 是一个分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和 Elastic-Job-Cloud 组成。Elastic-Job-Lite 定位为轻量级无中心化解决方案,依赖 Zookeeper ,使用 jar 包的形式提供分布式任务的协调服务,之前是当当网 Java 应用框架 ddframe 框架中的一部分,后分离出来独立发展。
    • Quartz 算是定时任务领域的老牌框架了,出自 OpenSymphony 开源组织,完全由 Java 编写,提供内存作业存储和数据库作业存储两种方式。在分布式任务调度时,数据库作业存储在服务器关闭或重启时,任务信息都不会丢失,在集群环境有很好的可用性。
    展开全文
  • 定时/计数器实现功能: (1)定时功能: 定时/计数器说明(最高单次计时时间是71毫秒=65535*1.085us,1.085us是机器周期): 使用51定时/计数器步骤: 定时/计算器控制寄存器: 设置定时寄存器的工作模式: 将想...

    定时/计数器实现功能:

    (1)定时功能:

    定时/计数器说明(最高单次计时时间是71毫秒=65535*1.085us,1.085us是机器周期):
    在这里插入图片描述
    使用51定时/计数器步骤:
    在这里插入图片描述
    定时/计算器控制寄存器:
    在这里插入图片描述
    设置定时寄存器的工作模式:
    在这里插入图片描述
    将想计时的时间(这个时间意思是想让定时/计数器经过多长时间溢出,溢出后就说明达到了你设定的时间)转化为微秒级(50毫秒=50000微妙)再用转化后的数除以1.085(机器周期时间)得到的数就是要加的次数 ,定时/计数器总共加65535次就会溢出,提前有一个初始值就可减少达到溢出的的次数。

    例子:想50毫秒后定时/计数器溢出,50毫秒=50000微妙
    (第一种方式)50000除以1.085得到46082(次),然后就可以将高八位TH0=46082/256(意思是有多少个256放入高八位中),低八位TL0=46082%256(意思是将剩余的256放到低八位中)。
    (第二种方式)也可以用65535减46082得到初值,再将十进制初值转化为十六进制,将前两位给TH0后两位给TL0

    计时器代码示例

    #include<reg52.h>
    #include<intrins.h>
    
    #define uint unsigned int
    #define uchar unsigned char
    
    sbit beep=P2^3;
    sbit DUAN =P2^6;//数码管段选
    sbit WEI=P2^7; //数码管位选
    uchar temp;
    //共阴极数码管段选表0——9,code表示数组中的数据在程序执行过程中不能被修改
    //加code存放的是ROM区不加code存放的是RAM区
    uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
    
    void delay(void)   //误差 -0.000000000001us
    {
        unsigned char a,b;
        for(b=15;b>0;b--)
            for(a=152;a>0;a--);
    } 
    
    void display(uint i)
    {   
    	uint bai,shi,ge;
    	bai=i/100;
    	shi=i%100/10;
    	ge=i%10;
    
        P0=0xff;//清除断码
    	WEI=1;//打开位选锁存器
    	P0 = 0xfe;//1111 1110 给第一个数码管接地,让它工作
      	WEI =0;//关闭位选锁存器,进行段选(显示数字)
    	DUAN=1;//打开段选锁存器
        P0 =table[bai];
        DUAN =0;//锁存段选锁存器
    
    	delay();
    
    	P0=0xff;//清除断码
    	WEI=1;
    	P0 = 0xfd;
    	WEI =0;
    	DUAN=1;
    	P0 = table[shi];
    	DUAN =0;
    
    	delay();
    
    	P0=0xff;//清除断码
    	WEI=1;
    	P0 = 0xfb;
    	WEI =0;
    	DUAN=1;
    	P0 = table[ge];
    	DUAN =0; 
    			
    	delay(); 
    }
    //定时器0初始化
    void timeInit()
    {
    	TR0=1;//启动定时器0
    	TMOD=0x01;//定时器工作模式一,16位定时计时模式
    	//TH0=0x4b;
    	//TL0=0xfd;//定时50毫秒;
    	TH0=(65535-46082)/256;
    	TL0=(65535-46082)%256;//定时50毫秒;
    }
    
    void main()
    {
    	uchar msec,sec;//声明毫秒和秒储存变量
    	timeInit();
    	while(1)
    	{
    		if(TF0==1)
    		{
    			TF0=0;//软件清0溢出标志位
    			TH0=0x4b;
    			TL0=0xfd;//重放初值
    		 	msec++;//50毫秒到
    			if(msec==20)
    			{
    			 	msec=0;
    				sec++;//一秒时间到
    				if(sec>100)
    					sec=0;//计时到100秒从1开始				
    			}		
    		
    		}
    	  display(sec);	//数码管显示时间
    	}
    }
    

    (2)计数功能:

    #include<reg52.h>
    #include<intrins.h>
    
    #define uint unsigned int
    #define uchar unsigned char
    
    sbit DUAN =P2^6;//数码管段选
    sbit WEI=P2^7; //数码管位选
    sbit LED1=P1^0;
    uchar temp;
    //共阴极数码管段选表0——9,code表示数组中的数据在程序执行过程中不能被修改
    //加code存放的是ROM区不加code存放的是RAM区
    uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
    
    void delay(void)   //误差 -0.000000000001us
    {
        unsigned char a,b;
        for(b=15;b>0;b--)
            for(a=152;a>0;a--);
    } 
    
    void display(uint i)
    {   
    	uint bai,shi,ge;
    	bai=i/100;
    	shi=i%100/10;
    	ge=i%10;
    
        P0=0xff;//清除断码
    	WEI=1;//打开位选锁存器
    	P0 = 0xfe;//1111 1110 给第一个数码管接地,让它工作
      	WEI =0;//关闭位选锁存器,进行段选(显示数字)
    	DUAN=1;//打开段选锁存器
        P0 =table[bai];
        DUAN =0;//锁存段选锁存器
    
    	delay();
    
    	P0=0xff;//清除断码
    	WEI=1;
    	P0 = 0xfd;
    	WEI =0;
    	DUAN=1;
    	P0 = table[shi];
    	DUAN =0;
    
    	delay();
    
    	P0=0xff;//清除断码
    	WEI=1;
    	P0 = 0xfb;
    	WEI =0;
    	DUAN=1;
    	P0 = table[ge];
    	DUAN =0; 
    	delay(); 
    }
    //计数器0初始化
    void jishuInit()
    {
    	TR0=1;//启动计数器0
    	TMOD|=0x05;//定时器工作模式一,16位计数模式
    	TH0=0;
    	TL0=0;
    }
    //定时器1初始化
    void timeInit()
    {
    	TR1= 1;//启动定时器1
    	TMOD|=0x10;//定时器工作模式一,16位定时模式
    	TH1=0x4b;
    	TL1=0xfd;
    }
    
    void main()
    {
    	uchar msec;//声明毫秒和秒储存变量
    	jishuInit();//计数器0初始化
    	timeInit();//定时器1初始化
    	while(1)
    	{
    		if(TF1==1)
    		{
    			TF1=0;//软件清0溢出标志位
    			TH1=0x4b;
    			TL1=0xfd;//重放初值
    		 	msec++;//50毫秒到
    			if(msec==10)//定时500毫秒到
    			{
    			 	msec=0;
    				LED1=~LED1;//产生方波
    			}
    		}
    		display(TL0);	//数码管显示时间
    	}
    }
    

    定时器中断

    中断情况下TF由硬件清0,若无中断则有软件清0。
    在这里插入图片描述
    代码示例:

    #include<reg52.h>
    #include<intrins.h>
    
    #define uint unsigned int
    #define uchar unsigned char
    
    sbit beep=P2^3;
    sbit DUAN =P2^6;//数码管段选
    sbit WEI=P2^7; //数码管位选
    
    uchar temp;
    uchar msec,sec;//声明毫秒和秒储存变量
    //共阴极数码管段选表0——9,code表示数组中的数据在程序执行过程中不能被修改
    //加code存放的是ROM区不加code存放的是RAM区
    uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
    
    void delay(void)   //误差 -0.000000000001us
    {
        unsigned char a,b;
        for(b=15;b>0;b--)
            for(a=152;a>0;a--);
    } 
    
    void display(uint i)
    {   
    	uint bai,shi,ge;
    	bai=i/100;
    	shi=i%100/10;
    	ge=i%10;
    
        P0=0xff;//清除断码
    	WEI=1;//打开位选锁存器
    	P0 = 0xfe;//1111 1110 给第一个数码管接地,让它工作
      	WEI =0;//关闭位选锁存器,进行段选(显示数字)
    	DUAN=1;//打开段选锁存器
        P0 =table[bai];
        DUAN =0;//锁存段选锁存器
    
    	delay();
    
    	P0=0xff;//清除断码
    	WEI=1;
    	P0 = 0xfd;
    	WEI =0;
    	DUAN=1;
    	P0 = table[shi];
    	DUAN =0;
    
    	delay();
    
    	P0=0xff;//清除断码
    	WEI=1;
    	P0 = 0xfb;
    	WEI =0;
    	DUAN=1;
    	P0 = table[ge];
    	DUAN =0; 
    			
    	delay(); 
    }
    //定时器0初始化
    void timeInit()
    {
    	EA=1;//打开总中断
        ET0=1;//打开中断允许位
    	TR0=1;//启动定时器0
    	TMOD=0x01;//定时器工作模式一,16位定时计时模式
    	//TH0=0x4b;
    	//TL0=0xfd;//定时50毫秒;
    	TH0=(65535-46082)/256;
    	TL0=(65535-46082)%256;//定时50毫秒;
    }
    
    void time0() interrupt 1//中断函数 
    {
    	TH0=0x4b;
    	TL0=0xfd;//重放初值
    	msec++;//50毫秒到
    	if(msec==20)//1s时间到
    	{
    		msec=0;
    		sec++;//一秒时间到
    		if(sec>100)
    		sec=0;//计时到100秒从1开始				
    	}			
    
    }
    
    void main()
    {
    	timeInit();
    	while(1)
    	{
    	  display(sec);	//数码管显示时间
    	}
    }
    

    定时器中断进行数码管动态扫描:

    #include <reg52.h>
    #include <intrins.h>
    
    #define uint unsigned int
    #define uchar unsigned char
    
    sbit DU = P2^6;//数码管段选
    sbit WE = P2^7;//数码管位选
    sbit key_s2 = P3^0;//独立按键S2
    sbit key_s3 = P3^1;//独立按键S3
    uchar num;//数码管显示的值
    
    //共阴数码管段选表
    uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
    //数码管位选码
    uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};
    //毫秒级延时函数定义
    void delay(uint z)
    {
    	uint x,y;
    	for(x = z; x > 0; x--)
    		for(y = 114; y > 0 ; y--); 		
    } 
    
    void display(uchar i)
    {
    	static uchar wei; 		
    	P0 = 0XFF;//清除断码
    	WE = 1;//打开位选寄存器
    	P0 = SMGwei[wei];
    	WE = 0;//锁存位选寄存器
    	switch(wei)
    	{
    		case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
    		case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;	
    		case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;		
    	}
    	wei++;
    	if(wei == 3)
    		wei = 0;
    }
    //定时器0初始化
    void timer0Init()
    {
    	EA = 1;	//打开总中断
    	ET0 = 1;//打开定时器0中断
    	TR0 = 1;	 //启动定时器0
    	TMOD = 0X01; //定时器工作模式,16位定时模式
    	TH0 = 0xED;
    	TL0 = 0xFF; //定时5ms
    }
    
    void main()//main函数自身会循环
    {	
    	timer0Init();//?¨ê±?÷03?ê??ˉ
    	while(1)
    	{
    		if(key_s2 == 0)//判断S2是否被按下
    		{
    			delay(20);//按键消抖
    			if(key_s2 == 0)
    			{
    				if(num != 120)
    				num++;
    				while(!key_s2);//松手检测
    			}	
    		}
    		if(key_s3 == 0)//判断S3是否被按下
    		{
    			delay(20);//按键消抖
    			if(key_s3 == 0)
    			{
    				if(num > 0)
    					num--;
    				while(!key_s3);//松手检测
    			}	
    		}
    	}	
    } 
    
    //定时器中断函数
    void timer0() interrupt 1
    {
    	TH0 = 0xED;
    	TL0 = 0xFF; //定时5ms
    	display(num); //数码管显示函数	
    } 
    
    展开全文
  • Spring Quartz 动态暂停、恢复、修改定时任务的一个demo,使用maven构建,框架采用spring springmvc jpa,数据库mysql,含数据库脚本,运行可用,页面访问地址http://localhost:8080/quartz_demo/quartz/list
  • spring+quartz实现动态设置定时任务

    热门讨论 2014-10-28 15:43:11
    spring+quartz实现动态设置定时任务,自己的例子+心得+sql语句
  • 很小巧的软件定时器,用于定时关机,定时开启关闭任意指定的软件,非常方便~~
  • C#定时调度任务服务

    热门讨论 2014-02-05 22:11:08
    Quartz.net做为任务调度核心,利用Log4Net做为日志输出,C#完成WINDOWS服务在后台定时执行任务;
  • 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、优缺点

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

    展开全文
  • 在不停服务的情况下,动态修改Spring定时任务的执行周期,即动态修改定时任务的cron参数。
  • asp.net定时任务,只要运行页面或者发布到iis(启动iis)之后每隔10秒执行一次
  • 文章目录一、功能说明二、快速使用三、实现原理1、动态管理实现(1) 配置管理介绍(2) 使用后处理器拦截SpringBoot原本的定时任务(3) 使用ApplicationRunner初始化自定义的定时任务运行器(4) 进行动态管理2、增强接口...

    一、功能说明

      SpringBoot的定时任务的加强工具,实现对SpringBoot原生的定时任务进行动态管理,完全兼容原生@Scheduled注解,无需对原本的定时任务进行修改

    二、快速使用

    具体的功能已经封装成SpringBoot-starter即插即用

    <dependency>
        <groupId>com.github.guoyixing</groupId>
        <artifactId>spring-boot-starter-super-scheduled</artifactId>
        <version>0.3.1</version>
    </dependency>
    

    使用方法和源码:
    码云:https://gitee.com/qiaodaimadewangcai/super-scheduled
    github:https://github.com/guoyixing/super-scheduled

    三、实现原理

    1、动态管理实现

    (1) 配置管理介绍

    @Component("superScheduledConfig")
    public class SuperScheduledConfig {
        /**
         * 执行定时任务的线程池
         */
        private ThreadPoolTaskScheduler taskScheduler;
    
        /**
         * 定时任务名称与定时任务回调钩子  的关联关系容器
         */
        private Map<String, ScheduledFuture> nameToScheduledFuture = new ConcurrentHashMap<>();
    
        /**
         * 定时任务名称与定时任务需要执行的逻辑  的关联关系容器
         */
        private Map<String, Runnable> nameToRunnable = new ConcurrentHashMap<>();
    
        /**
         * 定时任务名称与定时任务的源信息  的关联关系容器
         */
        private Map<String, ScheduledSource> nameToScheduledSource = new ConcurrentHashMap<>();
    	/* 普通的get/sets省略 */
    }
    

    (2) 使用后处理器拦截SpringBoot原本的定时任务

    1、实现ApplicationContextAware接口拿到SpringBoot的上下文
    2、实现BeanPostProcessor接口,将这个类标记为后处理器,后处理器会在每个bean实例化之后执行
    3、使用@DependsOn注解强制依赖SuperScheduledConfig类,让SpringBoot实例化SuperScheduledPostProcessor类之前先实例化SuperScheduledConfig类
    4、主要实现逻辑在postProcessAfterInitialization()方法中

    逐一处理
    无法获取到Scheduled注解
    获取到Scheduled注解
    所有方法处理完毕
    获取配置管理器SuperScheduledConfig
    获取当前实例化完成的bean的所有方法,并逐一处理
    尝试在该方法上获取Scheduled注解
    跳过当前方法
    创建定时任务的源属性,并保存到配置管理器中
    将原本SpringBoot的定时任务取消掉
    正常返回bean对象
    @DependsOn({"superScheduledConfig"})
    @Component
    @Order
    public class SuperScheduledPostProcessor implements BeanPostProcessor, ApplicationContextAware {
        protected final Log logger = LogFactory.getLog(getClass());
    
        private ApplicationContext applicationContext;
    
        /**
         * 实例化bean之前的操作
         * @param bean bean实例
         * @param beanName bean的Name
         */
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        /**
         * 实例化bean之后的操作
         * @param bean bean实例
         * @param beanName bean的Name
         */
        @Override
        public Object postProcessAfterInitialization(Object bean,
                                                     String beanName) throws BeansException {
            //1.获取配置管理器
            SuperScheduledConfig superScheduledConfig = applicationContext.getBean(SuperScheduledConfig.class);
    
            //2.获取当前实例化完成的bean的所有方法
            Method[] methods = bean.getClass().getDeclaredMethods();
            //循环处理对每个方法逐一处理
            if (methods.length > 0) {
                for (Method method : methods) {
                	//3.尝试在该方法上获取@Scheduled注解(SpringBoot的定时任务注解)
                    Scheduled annotation = method.getAnnotation(Scheduled.class);
                    //如果无法获取到@Scheduled注解,就跳过这个方法
                    if (annotation == null) {
                        continue;
                    }
                    //4.创建定时任务的源属性
                    //创建定时任务的源属性(用来记录定时任务的配置,初始化的时候记录的是注解上原本的属性)
                    ScheduledSource scheduledSource = new ScheduledSource(annotation, method, bean);
                    //对注解上获取到源属性中的属性进行检测
                    if (!scheduledSource.check()) {
                        throw new SuperScheduledException("在" + beanName + "Bean中" + method.getName() + "方法的注解参数错误");
                    }
                    //生成定时任务的名称(id),使用beanName+“.”+方法名
                    String name = beanName + "." + method.getName();
                    //将以key-value的形式,将源数据存入配置管理器中,key:定时任务的名称 value:源数据
                    superScheduledConfig.addScheduledSource(name, scheduledSource);
                    try {
                    	//5.将原本SpringBoot的定时任务取消掉
                        clearOriginalScheduled(annotation);
                    } catch (Exception e) {
                        throw new SuperScheduledException("在关闭原始方法" + beanName + method.getName() + "时出现错误");
                    }
                }
            }
            //最后bean保持原有返回
            return bean;
        }
    
        /**
         * 修改注解原先的属性
         * @param annotation 注解实例对象
         * @throws Exception
         */
        private void clearOriginalScheduled(Scheduled annotation) throws Exception {
            changeAnnotationValue(annotation, "cron", Scheduled.CRON_DISABLED);
            changeAnnotationValue(annotation, "fixedDelay", -1L);
            changeAnnotationValue(annotation, "fixedDelayString", "");
            changeAnnotationValue(annotation, "fixedRate", -1L);
            changeAnnotationValue(annotation, "fixedRateString", "");
            changeAnnotationValue(annotation, "initialDelay", -1L);
            changeAnnotationValue(annotation, "initialDelayString", "");
        }
    
    
        /**
         * 获取SpringBoot的上下文
         * @param applicationContext SpringBoot的上下文
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    (3) 使用ApplicationRunner初始化自定义的定时任务运行器

    1、实现ApplicationContextAware接口拿到SpringBoot的上下文
    2、使用@DependsOn注解强制依赖threadPoolTaskScheduler类
    3、实现ApplicationRunner接口,在所有bean初始化结束之后,运行自定义逻辑
    4、主要实现逻辑在run()方法中

    逐一处理
    所有方法处理完毕
    定时任务配置管理器中缓存 定时任务执行线程
    获取所有定时任务源数据,逐一处理定时任务
    获取定时任务源数据
    获取所有增强类
    创建并初始化执行控制器
    处理增强类
    将执行逻辑封装并缓存到定时任务配置管理器中
    启动定时任务,将线程回调钩子存到任务配置管理器中
    正常返回bean对象
    @DependsOn("threadPoolTaskScheduler")
    @Component
    public class SuperScheduledApplicationRunner implements ApplicationRunner, ApplicationContextAware {
        protected final Log logger = LogFactory.getLog(getClass());
        private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        private ApplicationContext applicationContext;
    	
    	/**
         * 定时任务配置管理器
         */
        @Autowired
        private SuperScheduledConfig superScheduledConfig;
        /**
         * 定时任务执行线程
         */
        @Autowired
        private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    
        @Override
        public void run(ApplicationArguments args) {
        	//1.定时任务配置管理器中缓存  定时任务执行线程
            superScheduledConfig.setTaskScheduler(threadPoolTaskScheduler);
            //2.获取所有定时任务源数据
            Map<String, ScheduledSource> nameToScheduledSource = superScheduledConfig.getNameToScheduledSource();
            //逐一处理定时任务
            for (String name : nameToScheduledSource.keySet()) {
                //3.获取定时任务源数据
                ScheduledSource scheduledSource = nameToScheduledSource.get(name);
                //4.获取所有增强类
                String[] baseStrengthenBeanNames = applicationContext.getBeanNamesForType(BaseStrengthen.class);
                //5.创建执行控制器
                SuperScheduledRunnable runnable = new SuperScheduledRunnable();
                //配置执行控制器
                runnable.setMethod(scheduledSource.getMethod());
                runnable.setBean(scheduledSource.getBean());
                //6.逐一处理增强类(增强器实现原理后面具体分析)
                List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length);
                for (String baseStrengthenBeanName : baseStrengthenBeanNames) {
                	//7.将增强器代理成point
                    Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName);
                    //创建代理
                    Point proxy = ProxyUtils.getInstance(Point.class, new RunnableBaseInterceptor(baseStrengthenBean, runnable));
                    proxy.setSuperScheduledName(name);
                    //8.所有的points连成起来
                    points.add(proxy);
                }
    			//将point形成调用链
                runnable.setChain(new Chain(points));
                //将执行逻辑封装并缓存到定时任务配置管理器中
                superScheduledConfig.addRunnable(name, runnable::invoke);
                try {
                	//8.启动定时任务
                    ScheduledFuture<?> schedule = ScheduledFutureFactory.create(threadPoolTaskScheduler
                            , scheduledSource, runnable::invoke);
                    //将线程回调钩子存到任务配置管理器中
                    superScheduledConfig.addScheduledFuture(name, schedule);
                    logger.info(df.format(LocalDateTime.now()) + "任务" + name + "已经启动...");
    
                } catch (Exception e) {
                    throw new SuperScheduledException("任务" + name + "启动失败,错误信息:" + e.getLocalizedMessage());
                }
            }
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    (4) 进行动态管理

    @Component
    public class SuperScheduledManager {
        protected final Log logger = LogFactory.getLog(getClass());
        private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
        @Autowired
        private SuperScheduledConfig superScheduledConfig;
    
        /**
         * 修改Scheduled的执行周期
         *
         * @param name scheduled的名称
         * @param cron cron表达式
         */
        public void setScheduledCron(String name, String cron) {
            //终止原先的任务
            cancelScheduled(name);
            //创建新的任务
            ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name);
            scheduledSource.clear();
            scheduledSource.setCron(cron);
            addScheduled(name, scheduledSource);
        }
    
        /**
         * 修改Scheduled的fixedDelay
         *
         * @param name       scheduled的名称
         * @param fixedDelay 上一次执行完毕时间点之后多长时间再执行
         */
        public void setScheduledFixedDelay(String name, Long fixedDelay) {
            //终止原先的任务
            cancelScheduled(name);
            //创建新的任务
            ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name);
            scheduledSource.clear();
            scheduledSource.setFixedDelay(fixedDelay);
            addScheduled(name, scheduledSource);
        }
    
        /**
         * 修改Scheduled的fixedRate
         *
         * @param name      scheduled的名称
         * @param fixedRate 上一次开始执行之后多长时间再执行
         */
        public void setScheduledFixedRate(String name, Long fixedRate) {
            //终止原先的任务
            cancelScheduled(name);
            //创建新的任务
            ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name);
            scheduledSource.clear();
            scheduledSource.setFixedRate(fixedRate);
            addScheduled(name, scheduledSource);
        }
    
        /**
         * 查询所有启动的Scheduled
         */
        public List<String> getRunScheduledName() {
            Set<String> names = superScheduledConfig.getNameToScheduledFuture().keySet();
            return new ArrayList<>(names);
        }
    
        /**
         * 查询所有的Scheduled
         */
        public List<String> getAllSuperScheduledName() {
            Set<String> names = superScheduledConfig.getNameToRunnable().keySet();
            return new ArrayList<>(names);
        }
    
        /**
         * 终止Scheduled
         *
         * @param name scheduled的名称
         */
        public void cancelScheduled(String name) {
            ScheduledFuture scheduledFuture = superScheduledConfig.getScheduledFuture(name);
            scheduledFuture.cancel(true);
            superScheduledConfig.removeScheduledFuture(name);
            logger.info(df.format(LocalDateTime.now()) + "任务" + name + "已经终止...");
        }
    
        /**
         * 启动Scheduled
         *
         * @param name            scheduled的名称
         * @param scheduledSource 定时任务的源信息
         */
        public void addScheduled(String name, ScheduledSource scheduledSource) {
            if (getRunScheduledName().contains(name)) {
                throw new SuperScheduledException("定时任务" + name + "已经被启动过了");
            }
            if (!scheduledSource.check()) {
                throw new SuperScheduledException("定时任务" + name + "源数据内容错误");
            }
    
            scheduledSource.refreshType();
    
            Runnable runnable = superScheduledConfig.getRunnable(name);
            ThreadPoolTaskScheduler taskScheduler = superScheduledConfig.getTaskScheduler();
    
    
            ScheduledFuture<?> schedule = ScheduledFutureFactory.create(taskScheduler, scheduledSource, runnable);
            logger.info(df.format(LocalDateTime.now()) + "任务" + name + "已经启动...");
    
            superScheduledConfig.addScheduledSource(name, scheduledSource);
            superScheduledConfig.addScheduledFuture(name, schedule);
        }
    
        /**
         * 以cron类型启动Scheduled
         *
         * @param name scheduled的名称
         * @param cron cron表达式
         */
        public void addCronScheduled(String name, String cron) {
            ScheduledSource scheduledSource = new ScheduledSource();
            scheduledSource.setCron(cron);
    
            addScheduled(name, scheduledSource);
        }
    
        /**
         * 以fixedDelay类型启动Scheduled
         *
         * @param name         scheduled的名称
         * @param fixedDelay   上一次执行完毕时间点之后多长时间再执行
         * @param initialDelay 第一次执行的延迟时间
         */
        public void addFixedDelayScheduled(String name, Long fixedDelay, Long... initialDelay) {
            ScheduledSource scheduledSource = new ScheduledSource();
            scheduledSource.setFixedDelay(fixedDelay);
            if (initialDelay != null && initialDelay.length == 1) {
                scheduledSource.setInitialDelay(initialDelay[0]);
            } else if (initialDelay != null && initialDelay.length > 1) {
                throw new SuperScheduledException("第一次执行的延迟时间只能传入一个参数");
            }
    
            addScheduled(name, scheduledSource);
        }
    
        /**
         * 以fixedRate类型启动Scheduled
         *
         * @param name         scheduled的名称
         * @param fixedRate    上一次开始执行之后多长时间再执行
         * @param initialDelay 第一次执行的延迟时间
         */
        public void addFixedRateScheduled(String name, Long fixedRate, Long... initialDelay) {
            ScheduledSource scheduledSource = new ScheduledSource();
            scheduledSource.setFixedRate(fixedRate);
            if (initialDelay != null && initialDelay.length == 1) {
                scheduledSource.setInitialDelay(initialDelay[0]);
            } else if (initialDelay != null && initialDelay.length > 1) {
                throw new SuperScheduledException("第一次执行的延迟时间只能传入一个参数");
            }
    
            addScheduled(name, scheduledSource);
        }
    
        /**
         * 手动执行一次任务
         *
         * @param name scheduled的名称
         */
        public void runScheduled(String name) {
            Runnable runnable = superScheduledConfig.getRunnable(name);
            runnable.run();
        }
    }
    

    2、增强接口实现

    增强器实现的整体思路与SpringAop的思路一致,实现没有Aop复杂

    (1) 增强接口

    @Order(Ordered.HIGHEST_PRECEDENCE)
    public interface BaseStrengthen {
        /**
         * 前置强化方法
         *
         * @param bean   bean实例(或者是被代理的bean)
         * @param method 执行的方法对象
         * @param args   方法参数
         */
        void before(Object bean, Method method, Object[] args);
    
        /**
         * 后置强化方法
         * 出现异常不会执行
         * 如果未出现异常,在afterFinally方法之后执行
         *
         * @param bean   bean实例(或者是被代理的bean)
         * @param method 执行的方法对象
         * @param args   方法参数
         */
        void after(Object bean, Method method, Object[] args);
    
        /**
         * 异常强化方法
         *
         * @param bean   bean实例(或者是被代理的bean)
         * @param method 执行的方法对象
         * @param args   方法参数
         */
        void exception(Object bean, Method method, Object[] args);
    
        /**
         * Finally强化方法,出现异常也会执行
         *
         * @param bean   bean实例(或者是被代理的bean)
         * @param method 执行的方法对象
         * @param args   方法参数
         */
        void afterFinally(Object bean, Method method, Object[] args);
    }
    

    (2) 代理抽象类

    public abstract class Point {
        /**
         * 定时任务名
         */
        private String superScheduledName;
    
        /**
         * 抽象的执行方法,使用代理实现
         * @param runnable 定时任务执行器
         */
        public abstract Object invoke(SuperScheduledRunnable runnable);
        
        /* 普通的get/sets省略 */
    }
    

    (3) 调用链类

    public class Chain {
        private List<Point> list;
        private int index = -1;
        /**
         * 索引自增1
         */
        public int incIndex() {
            return ++index;
        }
    
        /**
         * 索引还原
         */
        public void resetIndex() {
            this.index = -1;
        }
    }
    

    (4) cglib动态代理实现

    使用cglib代理增强器,将增强器全部代理成调用链节点Point

    public class RunnableBaseInterceptor implements MethodInterceptor {
        /**
         * 定时任务执行器
         */
        private SuperScheduledRunnable runnable;
        /**
         * 定时任务增强类
         */
        private BaseStrengthen strengthen;
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object result;
            //如果执行的是invoke()方法
            if ("invoke".equals(method.getName())) {
            	//前置强化方法
                strengthen.before(obj, method, args);
                try {
                	//调用执行器中的invoke()方法
                    result = runnable.invoke();
                } catch (Exception e) {
                	//异常强化方法
                    strengthen.exception(obj, method, args);
                    throw new SuperScheduledException(strengthen.getClass() + "中强化执行时发生错误", e);
                } finally {
                	//Finally强化方法,出现异常也会执行
                    strengthen.afterFinally(obj, method, args);
                }
                //后置强化方法
                strengthen.after(obj, method, args);
    
            } else {
            	//直接执行方法
                result = methodProxy.invokeSuper(obj, args);
            }
            return result;
        }
    
        public RunnableBaseInterceptor(Object object, SuperScheduledRunnable runnable) {
            this.runnable = runnable;
            if (BaseStrengthen.class.isAssignableFrom(object.getClass())) {
                this.strengthen = (BaseStrengthen) object;
            } else {
                throw new SuperScheduledException(object.getClass() + "对象不是BaseStrengthen类型");
            }
        }
    
        public RunnableBaseInterceptor() {
    
        }
    }
    

    (5) 定时任务执行器实现

    public class SuperScheduledRunnable {
        /**
         * 原始的方法
         */
        private Method method;
        /**
         * 方法所在的bean
         */
        private Object bean;
        /**
         * 增强器的调用链
         */
        private Chain chain;
    
    
        public Object invoke() {
            Object result;
            //索引自增1
            if (chain.incIndex() == chain.getList().size()) {
                //调用链中的增强方法已经全部执行结束
                try {
                    //调用链索引初始化
                    chain.resetIndex();
                    //增强器全部执行完毕,执行原本的方法
                    result = method.invoke(bean);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new SuperScheduledException(e.getLocalizedMessage());
                }
            } else {
                //获取被代理后的方法增强器
                Point point = chain.getList().get(chain.getIndex());
                //执行增强器代理
                //增强器代理中,会回调方法执行器,形成调用链,逐一运行调用链中的增强器
                result = point.invoke(this);
            }
            return result;
        }
        
        /* 普通的get/sets省略 */
    }
    

    (6) 增强器代理逻辑

    com.gyx.superscheduled.core.SuperScheduledApplicationRunner类中的代码片段

    //创建执行控制器
    SuperScheduledRunnable runnable = new SuperScheduledRunnable();
    runnable.setMethod(scheduledSource.getMethod());
    runnable.setBean(scheduledSource.getBean());
    //用来存放 增强器的代理对象
    List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length);
    //循环所有的增强器的beanName
    for (String baseStrengthenBeanName : baseStrengthenBeanNames) {
    	//获取增强器的bean对象
        Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName);
        //将增强器代理成Point节点
        Point proxy = ProxyUtils.getInstance(Point.class, new RunnableBaseInterceptor(baseStrengthenBean, runnable));
        proxy.setSuperScheduledName(name);
        //增强器的代理对象缓存到list中
        points.add(proxy);
    }
    //将增强器代理实例的集合生成调用链
    //执行控制器中设置调用链
    runnable.setChain(new Chain(points));
    
    展开全文
  • PostgreSQL 实现定时任务的 4 种方法

    千次阅读 多人点赞 2021-01-31 22:26:54
    数据库定时任务可以用于实现定期的备份、统计信息采集、数据汇总、数据清理与优化等。本文介绍了在 PostgreSQL 数据库中实现定时任务的 4 种方法,包括操作系统定时任务(cron)、pgAgent 代理、pg_cron 插件以及 pg...
  • android后台定时定位

    热门讨论 2014-04-15 17:00:42
    android后台服务定时定位,获取位置信息,手机开机自动开启服务。
  • Oracle 定时删除数据 并释放空间 ,创建存储过程并使用job完成。
  • 使用Quartz实现定时任务(包含管理界面)

    万次阅读 多人点赞 2019-01-01 18:43:34
    ")来实现的,至于监控方面的,没有,就是通过在定时任务代码里面打一些日志,特别重要的定时任务,失败了额外发个邮件通知下,人工补偿。然后他又问了下现在需要重构定时任务,你有没有什么想法?然后我就简单的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 679,767
精华内容 271,906
关键字:

定时