1、传统的定时任务
    线程

    1. public class Demo01 {
    2. static long count = 0;
    3. public static void main(String[] args) {
    4. Runnable runnable = new Runnable() {
    5. @Override
    6. public void run() {
    7. while (true) {
    8. try {
    9. Thread.sleep(1000);
    10. count++;
    11. System.out.println(count);
    12. } catch (Exception e) {
    13. // TODO: handle exception
    14. }
    15. }
    16. }
    17. };
    18. Thread thread = new Thread(runnable);
    19. thread.start();
    20. }
    21. }

    TimeTask

    1. /**
    2. * 使用TimerTask类实现定时任务
    3. */
    4. public class Demo02 {
    5. static long count = 0;
    6. public static void main(String[] args) {
    7. TimerTask timerTask = new TimerTask() {
    8. @Override
    9. public void run() {
    10. count++;
    11. System.out.println(count);
    12. }
    13. };
    14. Timer timer = new Timer();
    15. // 天数
    16. long delay = 0;
    17. // 秒数
    18. long period = 1000;
    19. timer.scheduleAtFixedRate(timerTask, delay, period);
    20. }
    21. }

    线程池

    public class Demo003 {
        public static void main(String[] args) {
            Runnable runnable = new Runnable() {
                public void run() {
                    // task to run goes here
                    System.out.println("Hello !!");
                }
            };
            ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
            // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间
            service.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.SECONDS);
        }
    }
    

    Quartz

        <dependencies>
            <!-- quartz -->
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>2.2.1</version>
            </dependency>
        </dependencies>
    

    任务调度类

    public class MyJob implements Job {
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("quartz MyJob date:" + new Date().getTime());
        }
    }
    

    启动类

      //1.创建Scheduler的工厂
          SchedulerFactory sf = new StdSchedulerFactory();
          //2.从工厂中获取调度器实例
          Scheduler scheduler = sf.getScheduler();
    
    
          //3.创建JobDetail
          JobDetail jb = JobBuilder.newJob(MyJob.class)
                  .withDescription("this is a ram job") //job的描述
                  .withIdentity("ramJob", "ramGroup") //job 的name和group
                  .build();
    
          //任务运行的时间,SimpleSchedle类型触发器有效
          long time=  System.currentTimeMillis() + 3*1000L; //3秒后启动任务
          Date statTime = new Date(time);
    
          //4.创建Trigger
              //使用SimpleScheduleBuilder或者CronScheduleBuilder
          Trigger t = TriggerBuilder.newTrigger()
                      .withDescription("")
                      .withIdentity("ramTrigger", "ramTriggerGroup")
                      //.withSchedule(SimpleScheduleBuilder.simpleSchedule())
                      .startAt(statTime)  //默认当前时间启动
                      .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) //两秒执行一次
                      .build();
    
          //5.注册任务和定时器
          scheduler.scheduleJob(jb, t);
    
          //6.启动 调度器
          scheduler.start();
    

    TelecomJobRegister.java TelecomJob.javaSpringBeanJobFactory.java

    <bean id="schedulerFactoryBean"   class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="true">  
             <property name="jobFactory">
                <bean class="com.cdvcloud.cc.service.schedule.SpringBeanJobFactory" />  
            </property>
            <property name="configLocation" value="classpath:quartz.properties"></property>  
        </bean>
    

    这此项目中如果向容器中注入SpringBeanJobFactory,则可以将实现job接口的实现类的初始化工作有quartz框架交由spring管理,则可实现自动装配,如果不重写,则可在需要执行的方法中想spring容器中获取需要的service实现类就可以了(ApplicationContext)

    Quartz表达式:http://cron.qqe2.com/

    2、分布式job

    由于在集群环境下,每个jvm都独立运行着同一份代码,连接着同一个数据库,这样就会在job触发的同一时刻,每个jvm中都会运行job任务,这样就造成了job任务的重复执行,所以为了避免job任务的重复执行,所以我们应用分布式job;

    过程:
    部署xxl-job-admin 作为注册中心与调度中心;
    创建执行器,其实就是配置执行任务时的服务器ip+port,可以是单台,也可以是多台;
    在任务管理里新建一个相应的任务,可以配置路由策略,决定如果执行器配置是集群,触发时是调用那台服务器的策略,配置Cron配置触发时机,JobHandler中配置我们缩写的job中@JobHandler()中的value值,当触发的时候回根据value值去相应的ip服务器的job容器中去去除相应的IJobHandler实现类,执行他的execute方法;
    我们在项目中配置xxl-job-admin注册中心的地址;
    配置相应的执行器name同时也创建一个相应的netty连接端口号
    需要执行的job类,继承IJobHandler抽象类,注册到job容器中;
    execute方法中编写具体需要执行的任务;

    搭建xxl-job-admin https://gitee.com/xuxueli0323/xxl-job xxl-job.zip
    2020-12-16_173033.png
    我们将sql初始化到我们的数据库中,然后在admin配置文件中修改数据库相关信息,然后就可以启动了;如果需要打包的话直接在xxl-job文件夹下执行命令就ok了: mvn clean compile package install;

    项目集成

    
    
        <properties>
            <spring-boot.version>2.4.0</spring-boot.version>
        </properties>
    
    <dependencyManagement>
            <dependencies>
                <dependency>
                    <!-- Import dependency management from Spring Boot (依赖管理:继承一些默认的依赖,工程需要依赖的jar包的管理,申明其他dependency的时候就不需要version) -->
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-parent</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <dependencies>
            <!-- spring-boot-starter-web (spring-webmvc + tomcat) -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- xxl-job-core -->
            <dependency>
                <groupId>com.xuxueli</groupId>
                <artifactId>xxl-job-core</artifactId>
                 <version>2.3.0-SNAPSHOT</version>
            </dependency>
    
        </dependencies>
    

    或者

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.1.7.RELEASE</version>
    </parent>
    <!-- xxl-job-core -->
    <dependency>
      <groupId>com.xuxueli</groupId>
      <artifactId>xxl-job-core</artifactId>
      <version>2.1.2</version>
    </dependency>
    
    # web port
    server.port=8081
    # no web
    #spring.main.web-environment=false
    # log config
    logging.config=classpath:logback.xml
    
    ### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
    xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
    ### xxl-job, access token
    xxl.job.accessToken=
    ### xxl-job executor appname
    xxl.job.executor.appname=demo
    ### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
    xxl.job.executor.address=
    ### xxl-job executor server-info
    xxl.job.executor.ip=
    xxl.job.executor.port=9999
    ### xxl-job executor log-path
    xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
    ### xxl-job executor log-retention-days
    xxl.job.executor.logretentiondays=30
    
    @Configuration
    public class XxlJobConfig {
        private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
    
        @Value("${xxl.job.admin.addresses}")
        private String adminAddresses;
    
        @Value("${xxl.job.accessToken}")
        private String accessToken;
    
        @Value("${xxl.job.executor.appname}")
        private String appname;
    
        @Value("${xxl.job.executor.address}")
        private String address;
    
        @Value("${xxl.job.executor.ip}")
        private String ip;
    
        @Value("${xxl.job.executor.port}")
        private int port;
    
        @Value("${xxl.job.executor.logpath}")
        private String logPath;
    
        @Value("${xxl.job.executor.logretentiondays}")
        private int logRetentionDays;
    
    
        @Bean
        public XxlJobSpringExecutor xxlJobExecutor() {
            logger.info(">>>>>>>>>>> xxl-job config init.");
            XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
            xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
            xxlJobSpringExecutor.setAppname(appname);
            xxlJobSpringExecutor.setAddress(address);
            xxlJobSpringExecutor.setIp(ip);
            xxlJobSpringExecutor.setPort(port);
            xxlJobSpringExecutor.setAccessToken(accessToken);
            xxlJobSpringExecutor.setLogPath(logPath);
            xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
    
            return xxlJobSpringExecutor;
        }
    
        /**
         * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
         *
         *      1、引入依赖:
         *          <dependency>
         *             <groupId>org.springframework.cloud</groupId>
         *             <artifactId>spring-cloud-commons</artifactId>
         *             <version>${version}</version>
         *         </dependency>
         *
         *      2、配置文件,或者容器启动变量
         *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
         *
         *      3、获取IP
         *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
         */
    
    
    }
    
    /**
     * XxlJob开发示例(Bean模式)
     *
     * 开发步骤:
     *      1、任务开发:在Spring Bean实例中,开发Job方法;
     *      2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
     *      3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
     *      4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
     *
     * @author xuxueli 2019-12-11 21:52:51
     */
    @Component
    public class SampleXxlJob {
        private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
    
    
        /**
         * 1、简单任务示例(Bean模式)
         */
        @XxlJob("demoJobHandler")
        public void demoJobHandler() throws Exception {
            XxlJobHelper.log("XXL-JOB, Hello World.");
    
            for (int i = 0; i < 5; i++) {
                XxlJobHelper.log("beat at:" + i);
                TimeUnit.SECONDS.sleep(2);
            }
            // default success
        }
    
    
        /**
         * 2、分片广播任务
         */
        @XxlJob("shardingJobHandler")
        public void shardingJobHandler() throws Exception {
    
            // 分片参数
            int shardIndex = XxlJobHelper.getShardIndex();
            int shardTotal = XxlJobHelper.getShardTotal();
    
            XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
    
            // 业务逻辑
            for (int i = 0; i < shardTotal; i++) {
                if (i == shardIndex) {
                    XxlJobHelper.log("第 {} 片, 命中分片开始处理", i);
                } else {
                    XxlJobHelper.log("第 {} 片, 忽略", i);
                }
            }
    
        }
    
    
        /**
         * 3、命令行任务
         */
        @XxlJob("commandJobHandler")
        public void commandJobHandler() throws Exception {
            String command = XxlJobHelper.getJobParam();
            int exitValue = -1;
    
            BufferedReader bufferedReader = null;
            try {
                // command process
                ProcessBuilder processBuilder = new ProcessBuilder();
                processBuilder.command(command);
                processBuilder.redirectErrorStream(true);
    
                Process process = processBuilder.start();
                //Process process = Runtime.getRuntime().exec(command);
    
                BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream());
                bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream));
    
                // command log
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    XxlJobHelper.log(line);
                }
    
                // command exit
                process.waitFor();
                exitValue = process.exitValue();
            } catch (Exception e) {
                XxlJobHelper.log(e);
            } finally {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            }
    
            if (exitValue == 0) {
                // default success
            } else {
                XxlJobHelper.handleFail("command exit value("+exitValue+") is failed");
            }
    
        }
    
    
        /**
         * 4、跨平台Http任务
         *  参数示例:
         *      "url: http://www.baidu.com\n" +
         *      "method: get\n" +
         *      "data: content\n";
         */
        @XxlJob("httpJobHandler")
        public void httpJobHandler() throws Exception {
    
            // param parse
            String param = XxlJobHelper.getJobParam();
            if (param==null || param.trim().length()==0) {
                XxlJobHelper.log("param["+ param +"] invalid.");
    
                XxlJobHelper.handleFail();
                return;
            }
    
            String[] httpParams = param.split("\n");
            String url = null;
            String method = null;
            String data = null;
            for (String httpParam: httpParams) {
                if (httpParam.startsWith("url:")) {
                    url = httpParam.substring(httpParam.indexOf("url:") + 4).trim();
                }
                if (httpParam.startsWith("method:")) {
                    method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase();
                }
                if (httpParam.startsWith("data:")) {
                    data = httpParam.substring(httpParam.indexOf("data:") + 5).trim();
                }
            }
    
            // param valid
            if (url==null || url.trim().length()==0) {
                XxlJobHelper.log("url["+ url +"] invalid.");
    
                XxlJobHelper.handleFail();
                return;
            }
            if (method==null || !Arrays.asList("GET", "POST").contains(method)) {
                XxlJobHelper.log("method["+ method +"] invalid.");
    
                XxlJobHelper.handleFail();
                return;
            }
            boolean isPostMethod = method.equals("POST");
    
            // request
            HttpURLConnection connection = null;
            BufferedReader bufferedReader = null;
            try {
                // connection
                URL realUrl = new URL(url);
                connection = (HttpURLConnection) realUrl.openConnection();
    
                // connection setting
                connection.setRequestMethod(method);
                connection.setDoOutput(isPostMethod);
                connection.setDoInput(true);
                connection.setUseCaches(false);
                connection.setReadTimeout(5 * 1000);
                connection.setConnectTimeout(3 * 1000);
                connection.setRequestProperty("connection", "Keep-Alive");
                connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
                connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");
    
                // do connection
                connection.connect();
    
                // data
                if (isPostMethod && data!=null && data.trim().length()>0) {
                    DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
                    dataOutputStream.write(data.getBytes("UTF-8"));
                    dataOutputStream.flush();
                    dataOutputStream.close();
                }
    
                // valid StatusCode
                int statusCode = connection.getResponseCode();
                if (statusCode != 200) {
                    throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
                }
    
                // result
                bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
                StringBuilder result = new StringBuilder();
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    result.append(line);
                }
                String responseMsg = result.toString();
    
                XxlJobHelper.log(responseMsg);
    
                return;
            } catch (Exception e) {
                XxlJobHelper.log(e);
    
                XxlJobHelper.handleFail();
                return;
            } finally {
                try {
                    if (bufferedReader != null) {
                        bufferedReader.close();
                    }
                    if (connection != null) {
                        connection.disconnect();
                    }
                } catch (Exception e2) {
                    XxlJobHelper.log(e2);
                }
            }
    
        }
    
        /**
         * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑;
         */
        @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
        public void demoJobHandler2() throws Exception {
            XxlJobHelper.log("XXL-JOB, Hello World.");
        }
        public void init(){
            logger.info("init");
        }
        public void destroy(){
            logger.info("destory");
        }
    
    
    }
    

    3、xxl-job-admin应该是分布式的,应该部署成多台服务器集群版的,为保证每次只有一台服务器执行,需要应用ngix来实现

    upstream  backServer{
        server 127.0.0.1:8080 weight=1;
        server 127.0.0.1:8081 weight=1;
        }
        server {
            listen       80;
            server_name  www.itmayiedu.com;
            #charset koi8-r;
            #access_log  logs/host.access.log  main;
            location / {
                proxy_pass   http://backServer;
                index  index.html index.htm;
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    

    分布式解决方案_03.docx shangkeziliao.zip上课代码.zip

    xxl-job.zip