SpringBoot 2.x (11):定時任務與異步任務


定時任務:有時候我們需要做定時的一些操作,比如統計信息,定時發送郵件等

在SpringBoot中如何進行整合和使用呢?

 

有哪些方式可以實現定時任務呢?

Java自帶的java.util.timer:

優點:Java自帶,無需導包

缺點:配置復雜,時間延后等問題

 

Quartz框架:

優點:配置簡單,使用方便

缺點:需要導包

 

@EnableSchedule:

優點:SpringBoot自帶,高兼容,無需導包

 

使用:

 在啟動類加入@EnableScheduling注解

package org.dreamtech.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

 

定義Task:

package org.dreamtech.demo.task;

import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class TestTask {
    @Scheduled(fixedRate = 2000)
    public void test() {
        System.out.println("[ " + "當前時間 : " + new Date() + " ]");
    }
}

 

效果:控制台每過兩秒打印一次當前時間

[ 當前時間 : Fri May 10 14:01:42 CST 2019 ]
[ 當前時間 : Fri May 10 14:01:44 CST 2019 ]
[ 當前時間 : Fri May 10 14:01:46 CST 2019 ]
[ 當前時間 : Fri May 10 14:01:48 CST 2019 ]
[ 當前時間 : Fri May 10 14:01:50 CST 2019 ]
[ 當前時間 : Fri May 10 14:01:52 CST 2019 ]
[ 當前時間 : Fri May 10 14:01:54 CST 2019 ]

 

使用CRON表達式:

每兩秒執行一次的另一種表達方式:

    @Scheduled(cron = "*/2 * * * * *")

 關於CRON表達式的百度即可,一堆介紹,我就不多寫了

 

 

異步任務:適用於發送短信、郵件、處理Log等問題

為什么需要異步任務?

比如電商網站用戶下單,要做的事情有這幾樣:庫存查詢、用戶校驗、余額校驗等

SpringBoot使用異步任務:@EnableAsync注解

基本實現:

啟動類加入@EnableAsync注解

package org.dreamtech.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

 

定義任務類:這里模擬耗時的操作

package org.dreamtech.demo.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Async
public class AsyncTask {
    public void test1() throws InterruptedException {
        long begin = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("[ " + "任務1耗時 : " + (end - begin) + " ]");
    }

    public void test2() throws InterruptedException {
        long begin = System.currentTimeMillis();
        Thread.sleep(2000);
        long end = System.currentTimeMillis();
        System.out.println("[ " + "任務2耗時 : " + (end - begin) + " ]");
    }

    public void test3() throws InterruptedException {
        long begin = System.currentTimeMillis();
        Thread.sleep(3000);
        long end = System.currentTimeMillis();
        System.out.println("[ " + "任務2耗時 : " + (end - begin) + " ]");
    }
}

 

Controller層做測試:

package org.dreamtech.demo.controller;

import org.dreamtech.demo.task.AsyncTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @Autowired
    private AsyncTask task;

    @GetMapping("/test")
    private Object test() {
        long begin = System.currentTimeMillis();
        try {
            task.test1();
            task.test2();
            task.test3();
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("[ " + "Controller耗時 : " + (end - begin) + " ]");
        return "test";
    }
}

 

訪問localhost:8080/test打印如下:

[ Controller耗時 : 4 ]
[ 任務1耗時 : 1000 ]
[ 任務2耗時 : 2000 ]
[ 任務2耗時 : 3000 ]

 

結論:異步任務的執行類似多線程,可以用來做一些耗時操作

 

有時候需要某個任務確定執行完畢才能繼續其他操作

如何獲取任務的執行結果呢?

package org.dreamtech.demo.task;

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

@Component
@Async
public class AsyncTask {

    public Future<String> test1() throws InterruptedException {
        long begin = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("[ " + "任務1耗時 : " + (end - begin) + " ]");
        return new AsyncResult<String>("任務1");
    }

    public Future<String> test2() throws InterruptedException {
        long begin = System.currentTimeMillis();
        Thread.sleep(2000);
        long end = System.currentTimeMillis();
        System.out.println("[ " + "任務2耗時 : " + (end - begin) + " ]");
        return new AsyncResult<String>("任務2");
    }

    public Future<String> test3() throws InterruptedException {
        long begin = System.currentTimeMillis();
        Thread.sleep(3000);
        long end = System.currentTimeMillis();
        System.out.println("[ " + "任務3耗時 : " + (end - begin) + " ]");
        return new AsyncResult<String>("任務3");
    }
}

 

Controller:

package org.dreamtech.demo.controller;

import java.util.concurrent.Future;

import org.dreamtech.demo.task.AsyncTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @Autowired
    private AsyncTask task;

    @GetMapping("/test")
    private Object test() {
        long begin = System.currentTimeMillis();
        try {
            Future<String> task1Result = task.test1();
            Future<String> task2Result = task.test2();
            Future<String> task3Result = task.test3();
            while (true) {
                if (task1Result.isDone() && task2Result.isDone() && task3Result.isDone()) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("[ " + "Controller耗時 : " + (end - begin) + " ]");
        return "test";
    }
}

 

訪問localhost:8080/test

等待3秒之后,打印如下:

[ 任務1耗時 : 1001 ]
[ 任務2耗時 : 2001 ]
[ 任務3耗時 : 3001 ]
[ Controller耗時 : 3007 ]

 只有三個任務都完成,Controller層代碼才會執行完畢

 

總結:異步任務大大地提高了開發的效率

 

順便來記錄Logback的使用:

Logback:一款優秀的日志框架

SpringBoot的Starter默認日志框架:Logback,默認級別:INFO

如果我們想以DEBUG方式啟動:java -jar springboot.jar --debug

配置文件:官方推薦名稱logback-spring.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <appender name="consoleApp"
        class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method:%L -%msg%n
            </pattern>
        </layout>
    </appender>

    <appender name="fileInfoApp"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <encoder>
            <pattern>
                %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method:%L -%msg%n
            </pattern>
        </encoder>
        <!-- 滾動策略 -->
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 路徑 -->
            <fileNamePattern>app_log/log/app.info.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>

    <appender name="fileErrorApp"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>
                %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method:%L -%msg%n
            </pattern>
        </encoder>
        <!-- 設置滾動策略 -->
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 路徑 -->
            <fileNamePattern>app_log/log/app.err.%d.log</fileNamePattern>
            <!-- 控制保留的歸檔文件的最大數量,超出數量就刪除舊文件,假設設置每個月滾動, 且<maxHistory> 是1,則只保存最近1個月的文件,刪除之前的舊文件 -->
            <MaxHistory>1</MaxHistory>
        </rollingPolicy>
    </appender>
    <root level="INFO">
        <appender-ref ref="consoleApp" />
        <appender-ref ref="fileInfoApp" />
        <appender-ref ref="fileErrorApp" />
    </root>
</configuration>

 

使用和測試:

package org.dreamtech.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping("/log")
    private Object log() {
        logger.debug("test");
        logger.info("test");
        logger.warn("test");
        logger.error("test");
        return "log";
    }
}

 

訪問localhost:8080/log

會在項目目錄生成一個新目錄app_log

存在兩個文件:一個用來存放ERROR級別信息,一個存放INFO級別和WARN級別

2019-05-11 12:53:53.668 ERROR[http-nio-8080-exec-1]org.dreamtech.demo.controller.TestController.log:18 -test
2019-05-11 12:53:36.264 INFO [main]org.dreamtech.demo.DemoApplication.logStarting:50 -Starting DemoApplication on DESKTOP-59O94TD with PID 14180 (D:\PROJECT\STS_PROJECT\demo\target\classes started by 20235 in D:\PROJECT\STS_PROJECT\demo)
2019-05-11 12:53:36.266 INFO [main]org.dreamtech.demo.DemoApplication.logStartupProfileInfo:675 -No active profile set, falling back to default profiles: default
2019-05-11 12:53:37.019 INFO [main]o.s.boot.web.embedded.tomcat.TomcatWebServer.initialize:90 -Tomcat initialized with port(s): 8080 (http)
2019-05-11 12:53:37.031 INFO [main]org.apache.coyote.http11.Http11NioProtocol.log:173 -Initializing ProtocolHandler ["http-nio-8080"]
2019-05-11 12:53:37.039 INFO [main]org.apache.catalina.core.StandardService.log:173 -Starting service [Tomcat]
2019-05-11 12:53:37.041 INFO [main]org.apache.catalina.core.StandardEngine.log:173 -Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-05-11 12:53:37.122 INFO [main]o.a.catalina.core.ContainerBase.[Tomcat].[localhost].[/].log:173 -Initializing Spring embedded WebApplicationContext
2019-05-11 12:53:37.122 INFO [main]org.springframework.web.context.ContextLoader.prepareWebApplicationContext:296 -Root WebApplicationContext: initialization completed in 824 ms
2019-05-11 12:53:37.289 INFO [main]o.s.scheduling.concurrent.ThreadPoolTaskExecutor.initialize:171 -Initializing ExecutorService 'applicationTaskExecutor'
2019-05-11 12:53:37.413 INFO [main]org.apache.coyote.http11.Http11NioProtocol.log:173 -Starting ProtocolHandler ["http-nio-8080"]
2019-05-11 12:53:37.447 INFO [main]o.s.boot.web.embedded.tomcat.TomcatWebServer.start:204 -Tomcat started on port(s): 8080 (http) with context path ''
2019-05-11 12:53:37.450 INFO [main]org.dreamtech.demo.DemoApplication.logStarted:59 -Started DemoApplication in 1.528 seconds (JVM running for 2.39)
2019-05-11 12:53:53.641 INFO [http-nio-8080-exec-1]o.a.catalina.core.ContainerBase.[Tomcat].[localhost].[/].log:173 -Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-05-11 12:53:53.642 INFO [http-nio-8080-exec-1]org.springframework.web.servlet.DispatcherServlet.initServletBean:524 -Initializing Servlet 'dispatcherServlet'
2019-05-11 12:53:53.650 INFO [http-nio-8080-exec-1]org.springframework.web.servlet.DispatcherServlet.initServletBean:546 -Completed initialization in 8 ms
2019-05-11 12:53:53.668 INFO [http-nio-8080-exec-1]org.dreamtech.demo.controller.TestController.log:16 -test
2019-05-11 12:53:53.668 WARN [http-nio-8080-exec-1]org.dreamtech.demo.controller.TestController.log:17 -test
2019-05-11 12:54:23.513 INFO [RMI TCP Connection(2)-127.0.0.1]o.s.b.a.SpringApplicationAdminMXBeanRegistrar$SpringApplicationAdmin.shutdown:163 -Application shutdown requested.
2019-05-11 12:54:23.515 INFO [RMI TCP Connection(2)-127.0.0.1]o.s.scheduling.concurrent.ThreadPoolTaskExecutor.shutdown:208 -Shutting down ExecutorService 'applicationTaskExecutor'

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM