Spring中定时任务@Scheduled的一点小小研究

Posted xtf2009

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中定时任务@Scheduled的一点小小研究相关的知识,希望对你有一定的参考价值。

最近做一个公众号项目,微信公众号会要求服务端找微信请求一个access_token,获取的过程:

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

公众平台的API调用所需的access_token的使用及生成方式说明:

1、建议公众号开发者使用中控服务器统一获取和刷新Access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;

2、目前Access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器可对外继续输出的老access_token,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;

3、Access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。

 

所以解决方案就是做一个定时任务,服务启动的时候获取一次token,之后每隔两小时自动获取一次。这用Spring的@Scheduled注解很容易实现:

@Override
    @Scheduled(fixedRate=1000*60*59*2)
    public void updateToken() {

        try{
            for(AppConfig appConfig : SysConfig.AppLists){
                Token token = TokenAPI.token(appConfig.wxAppId,appConfig.wxAppSecret);
                appConfig.access_token = token.getAccess_token();
                Ticket ticket = TicketAPI.ticketGetticket(token.getAccess_token());
                appConfig.ticket = ticket.getTicket();
                logger.info("access_token:" + token.getAccess_token()+",js_api_ticket:"+ticket.getTicket());
            }
        }catch (Exception e){
            logger.info("刷新access_token出错");
        }
    }

这里使用fixedRate来指定任务执行的时间间隔(毫秒记)。

然后因为现在项目只有一个服务号,没有申请测试用的服务号,本地调试也会去获取access_token ,结果导致服务端的生产环境的token失效,必须重启服务器的tomcat来刷新token。显然这种做法是非常愚蠢的,目前还没正式投入使用,投入使用后这显然不能接受。

解决方案就是,统一一个接口来向服务端获取token,当token失效时,执行一次updateToken的方法,以刷新token。

 

===================================================================手动分割线============================================================

然后我就比较好奇一个事,那就是,当加了@Scheduled(fixedRate=1000*60*59*2)的方法被其他方法调用的时候,定时任务会有变化吗?比如,我调用一次,那么任务是还是按照以前预定的时间跑呢,还是会在我调用之后的2小时(预约的间隔时间)才会再跑?百度Google找了找,包括官方文档也没给个说法。为这个也不想去看源代码,还是写个demo验证下算了。

首先创建一个Spring Boot的项目,引入web模块(使用rest请求来手动调用定时任务)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 

项目结构:

技术分享图片

 

Service接口:

package com.xiatingfei.schedule.demo.service;

public interface TaskService {

    void SayHelloTask();

}

Service实现(每隔10秒钟在控制台打印一次Hello!):

package com.xiatingfei.schedule.demo.service.impl;

import com.xiatingfei.schedule.demo.service.TaskService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
public class TaskServiceImpl implements TaskService {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Override
    @Scheduled(fixedRate = 10000)
    public void SayHelloTask() {
        System.out.println("Hello! Now is "  + dateFormat.format(new Date()));
    }
}

Controller:

package com.xiatingfei.schedule.demo.controller;

import com.xiatingfei.schedule.demo.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/demo")
public class TriggerController {

    @Autowired
    TaskService taskService;

    @RequestMapping("/sayHello")
    public void TriggerTask(){
        taskService.SayHelloTask();
    }

}

然后在SpringBoot的启动类加入定时任务的注解@EnableScheduling

package com.xiatingfei.schedule.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);
    }
}

启动应用,可以看到控制台可以定时打印Hello和时间:

技术分享图片

在Postman给服务端发一个请求,手动调用定时任务:

get http://localhost:8080/demo/sayHello

结果:

技术分享图片

可以看到定时任务没有受到调用的影响,还是按照自己的节奏10秒钟一次~~

(而且,还会发现Spring会在第一次收到Http请求的时候才会初始化dispatcherServlet,而不是服务启动的时候就初始化)

 

以上是关于Spring中定时任务@Scheduled的一点小小研究的主要内容,如果未能解决你的问题,请参考以下文章

使用spring@Scheduled进行任务定时

Spring使用@Scheduled注解配置定时任务

使用spring提供的@Scheduled注解创建定时任务

在Spring项目中使用@Scheduled注解定义简单定时任务

Spring Boot 定时任务 @Scheduled

使用轻量级Spring @Scheduled注解执行定时任务