场景
在项目中,部署了多个tomcat应用,在执行定时任务时就会遇到同一任务可能执行多次的情况,我们可以借助分布式锁,保证在同一时间只有一个tomcat应用执行了定时任务
实现1
通过elastic-job 实现
实现2
通过zk锁实现
分布式锁,这个主要得益于 ZooKeeper 为我们保证了数据的强一致性。锁服务可以分为两类,一个是 保持独占,另一个是 控制时序。
- 所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把 zk 上的一个 znode 看作是一把锁,通过 create znode 的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。
- 控制时序,就是所有视图来获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序了。做法和上面基本类似,只是这里 /distributelock 已经预先存在,客户端在它下面创建临时有序节点(这个可以通过节点的属性控制:CreateMode.EPHEMERALSEQUENTIAL 来指定)。Zk 的父节点(/distribute_lock)维持一份 sequence, 保证子节点创建的时序性,从而也形成了每个客户端的全局时序。
实现步骤
实现步骤:
多个Jvm同时在Zookeeper上创建同一个相同的节点( /Lock)
zk节点唯一的! 不能重复!节点类型为临时节点, jvm1创建成功时候,jvm2和jvm3创建节点时候会报错,该节点已经存在。这时候 jvm2和jvm3进行等待(需要实现监听),或者return掉。
实现代码
public interface ZKLock {
//基于zk实现分布式锁
boolean getLock();
//释放锁
void unLock();
}
public abstract class ZookeeperAbstractLock implements ZKLock {
private static final Logger logger = LoggerFactory.getLogger(ZookeeperAbstractLock.class);
protected ZkClient zkClient;
protected String lockPath="/lockPath1";
@PostConstruct
void init(){
}
@Override
public boolean getLock() {
if (tryLock()) {
logger.info("localhost is {}",ServerUtils.getLocalHostName());
logger.info("server ip is {}",ServerUtils.getLocalIP());
logger.info("get lock success");
return true;
}else {
//进行等待
return false;
//waitLock();
}
}
@Override
public void unLock() {
//执行完毕 直接连接
if (zkClient != null) {
zkClient.delete(lockPath);
//zkClient.close();
logger.info("release lock success");
}
}
//获取锁
abstract boolean tryLock();
//创建失败 进行等待
abstract void waitLock();
}
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDistrbuteLock.class);
@Value("${zookeeper.hostList}")
private String zookeeperHostList;
@Override
boolean tryLock() {
if (zkClient==null){
zkClient= new ZkClient(zookeeperHostList);
}
try {
zkClient.createEphemeral(lockPath);
return true;
} catch (Exception e) {
// 如果失败 直接catch
return false;
}
}
@Override
void waitLock() {
logger.info("get lock fail");
}
}
业务端
@Component
@EnableScheduling
public class XXXJob {
private static final Logger logger = LoggerFactory.getLogger(XXXJob.class);
private ZKLock zkLock = new ZookeeperDistrbuteLock();
@Autowired
RiskDataOperaService riskDataOperaService;
@Scheduled(cron = "0 0 17 * * ?")
public void execute() {
boolean getLock = zkLock.getLock();
if (!getLock) {
logger.info("no to get lock retrun");
return;
}
try {
logger.info("begin to process the job...");
Thread.sleep(1000L);
logger.info("end of the job");
} catch (Exception e) {
logger.error(e.getMessage());
} finally {
logger.info("go to unlock");
zkLock.unLock();
}
}
}