什么场景
获取token
最近也是遇到了一些烦恼,比如获取 token ,一天内获取的次数有限,每次获取到的 token 也是有一定的有效期。针对这种情况,其实需要我们定时的去获取token,并且刷新到缓存中,但是如果在多台机子的情况下,我们需要去做一个分布式锁,每次只保证有一台机子去获取token。
单机实现生产消费
自己生产自己消费,其实就是一端往一个队列丢数据,另一端往队列里面消费数据,当然是阻塞队列。当然这种情况下肯定不好的,因为操作都是基于内存的,重启机子的时候会丢失数据。所以这种也只是针对 数据量不是持续的都有。一阵一阵的。那为什么要用定时任务呢? 因为在消费一段,where(true) 去拿数据是不是会导致cpu巨高。所以一般都会使用定时任务,每个几秒去看一下队列里面是否有数据。
怎么简单优雅的实现呢
下面就简单的来一个栗子。解释一下 @PostConstruct注解的作用,被注解的方法,构造函数之后,set执行之前执行。
/**
* @author JZWen
* @date 2020/11/25
*/
@Service
public class TaskService {
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private volatile TaskJob taskJob = null;
@PostConstruct
public void startup() {
System.out.println("-----------start up-----------");
scheduledExecutorService.scheduleWithFixedDelay(new TaskJob(), 2, 10, TimeUnit.SECONDS);
System.out.println("-----------end down------------");
}
public TaskJob getJob() {
if (taskJob != null) {
return taskJob;
}
synchronized (TaskJob.class) {
if (taskJob == null) {
taskJob = new TaskJob();
}
}
return taskJob;
}
}
执行 job 线程
/**
* @author JZWen
* @date 2020/11/25
*/
public class TaskJob implements Runnable{
@Override
public void run() {
System.out.println("开始执行任务-------------------");
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行任务结束-------------------");
}
}
执行结果
-----------start up-----------
-----------end down------------
2020-11-25 15:25:52.441 INFO 16864 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-25 15:25:52.878 INFO 16864 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8088 (http) with context path ''
2020-11-25 15:25:52.882 INFO 16864 --- [ main] com.github.wetest.WxTestApplication : Started WxTestApplication in 5.793 seconds (JVM running for 9.259)
开始执行任务-------------------
执行任务结束-------------------
开始执行任务-------------------
执行任务结束-------------------
开始执行任务-------------------
执行任务结束-------------------
开始执行任务-------------------
执行任务结束-------------------
开始执行任务-------------------
执行任务结束-------------------
开始执行任务-------------------
执行任务结束-------------------
Job 有依赖,拿到的是null
因为 @PostConstruct注解的作用,被注解的方法,构造函数之后,set执行之前执行。所以如果当前类要是使用 @Autowired 或者 @Value 注解的属性,拿到的是null。这点切记。怎么解决这个呢,很简单,在job的构造放里面放入你的依赖,将依赖传过去。如下,job 依赖 wxTemplateMsgService
/**
* @author JZWen
* @date 2020/11/25
*/
@Service
public class TaskService {
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private volatile TaskJob taskJob = null;
@Autowired
private WxTemplateMsgService wxTemplateMsgService;
@PostConstruct
public void startup() {
System.out.println("-----------start up-----------");
scheduledExecutorService.scheduleWithFixedDelay(new TaskJob(wxTemplateMsgService), 2, 10, TimeUnit.SECONDS);
System.out.println("-----------end down------------");
}
public TaskJob getJob(WxTemplateMsgService wxTemplateMsgService) {
if (taskJob != null) {
return taskJob;
}
synchronized (TaskJob.class) {
if (taskJob == null) {
taskJob = new TaskJob(wxTemplateMsgService);
}
}
return taskJob;
}
}
/**
* @author JZWen
* @date 2020/11/25
*/
public class TaskJob implements Runnable{
private WxTemplateMsgService wxTemplateMsgService;
public TaskJob(WxTemplateMsgService wxTemplateMsgService) {
this.wxTemplateMsgService = wxTemplateMsgService;
}
@Override
public void run() {
System.out.println("开始执行任务-------------------");
try {
wxTemplateMsgService.send(new WxTemplateMsgInfo());
Thread.sleep(1000L);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("执行任务结束-------------------");
}
}
最后一点,job 抛异常,定时任务会停止
要是job里面抛异常的话,整个定时任务会停止,所以一定要自己catch 异常并处理。
目前这个操作,我在公司还两块业务用到了,感觉比搭建什么 quartz 更快,但是没有他那么灵活,对于业务量不大的服务可以使用这种方式。奥里给!!
欢迎小兄弟们关注我!!!!!