当前位置: 移动技术网 > IT编程>开发语言>Java > SpringBoot 2.x (11):定时任务与异步任务

SpringBoot 2.x (11):定时任务与异步任务

2019年05月12日  | 移动技术网IT编程  | 我要评论

定时任务:有时候我们需要做定时的一些操作,比如统计信息,定时发送邮件等

在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'

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网