集群服务器定时任务重复执行的解决方案
服务器采用了负载均衡,有多台服务器,部署的代码也一样,所以定时任务会在某一时间同时执行,这就导致了很多问题,解决方案有以下几种:
1 . 在一台服务器上部署定时任务
优点:简单易懂
缺点:需部署多套代码
2 . 定时任务代码上加特定的IP限制
优点:简单易部署,仅某个IP服务器可以执行定时任务
缺点:此服务器故障时,定时任务不再执行
3 . 采用分布式锁解决,例如zookeeper的分布式锁
(1)定时任务:
@Scheduled(cron = "0 0/10 * * * ?")
public void test() {
log.info("xxx");
try {
if (getToken(KEY)) {
//do something
}
} catch (Exception e) {
log.error("test error:" + ExceptionUtils.getStackTrace(e));
}
log.info("xxx");
}
(2)获取分布式锁:
public boolean getToken(String tokenKey, final int leaderShipRelease) {
CuratorFramework client = null;
LeaderLatch ll = null;
try {
// 连接ZK,每隔四秒重试一次,重试三次
RetryPolicy retryPolicy = new ExponentialBackoffRetry(4000,3);
client = CuratorFrameworkFactory.newClient(zookeeperHost, retryPolicy);
client.start();
// leader选举
ll = new LeaderLatch(client, String.format("%s/zk/%s/lock", zookeeperPath, tokenKey));
ll.start();
// 尝试获得锁
boolean get = ll.await(leaderShipWait, TimeUnit.MILLISECONDS);
if(get){
final CuratorFramework client2 = client;
final LeaderLatch ll2 = ll;
new Thread(new Runnable(){
@Override
public void run() {
try {
// 释放锁
Thread.sleep(leaderShipRelease);
ll2.close();
client2.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
return true;
}else{
ll.close();
client.close();
}
} catch (Exception e) {
e.printStackTrace();
IOUtils.closeStream(ll);
IOUtils.closeStream(client);
}
return false;
}