基于ZooKeeper分布式锁的流程
在zookeeper指定节点(locks)下创建临时顺序节点node_n
获取locks下所有子节点children
对子节点按节点自增序号从小到大排序
判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
若监听事件生效,则回到第二步重新进行判断,直到获取到锁
具体实现
添加Maven依赖:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.falsh
mytiming
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-quartz
org.springframework.boot
spring-boot-starter-aop
com.alibaba.spring.boot
dubbo-spring-boot-starter
2.0.0
spring-context
org.springframework
spring-core
org.springframework
org.apache.curator
curator-recipes
2.3.0
org.springframework.boot
spring-boot-maven-plugin
org.apache.maven.plugins
maven-source-plugin
attach-sources
jar
代码:
package com.falsh.tss.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class CommonEnv {
public static String ZK_ADDRESS;
public static String ZK_LOCK_PATH;
@Autowired
public void setZkAddress(@Value("${zk.address}") String zkAddress) {
ZK_ADDRESS = zkAddress;
}
@Autowired
public void setZkLockPath(@Value("${zk.lock.path}") String zkLockPath) {
ZK_LOCK_PATH = zkLockPath;
}
}
package com.falsh.tss.global;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.falsh.tss.mutex.MutexLock;
@Aspect
@Component
public class ControlExecJobAspect {
private final Logger logger = LoggerFactory.getLogger(ControlExecJobAspect.class);
@Pointcut("execution(public * com.zhifu.tss.jobs..*(..))")
public void jobExec() {
}
@Around("jobExec()")
public void doAround(ProceedingJoinPoint pjp) throws Throwable{
String jobClass = pjp.getTarget().getClass().getSimpleName();
String methodName = ((MethodSignature) pjp.getSignature()).getMethod().getName();
if (!MutexLock.isInstantiated() || !MutexLock.getInstance().isAcquiredLock()) {
logger.info("-- None lock acquired ! {}.{} --", jobClass, methodName);
return;
}
logger.info("------- start {}.{} --------", jobClass, methodName);
long start = System.currentTimeMillis();
try {
pjp.proceed();
} catch (Exception e) {
logger.error("global erorr occured while {}.{}", jobClass, methodName, e);
}
long end = System.currentTimeMillis();
logger.info("------- end {}.{}({}ms) --------", jobClass, methodName, end - start);
}
}
package com.falsh.tss.jobs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MyJob1 {
private final static Logger logger = LoggerFactory.getLogger(MyJob1.class);
@Scheduled(cron="${cron.job.myJob1}")
public void execute(){
logger.info("我在执行定时任务1.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.falsh.tss.mutex;
import static com.falsh.tss.config.CommonEnv.ZK_ADDRESS;
import static com.falsh.tss.config.CommonEnv.ZK_LOCK_PATH;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.RetryNTimes;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class MutexLock implements InitializingBean, DisposableBean {
private static CuratorFramework client;
private static InterProcessLock mutexLock;
private static volatile boolean acquiredLock;
private static volatile MutexLock instance;
public MutexLock() {
client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, new RetryNTimes(10,5000));
client.start();
mutexLock = new InterProcessMutex(client, ZK_LOCK_PATH);
}
public static MutexLock getInstance(){
if (instance == null) {
synchronized (MutexLock.class) {
if (instance == null) {//二次检查
instance = new MutexLock();
}
}
}
return instance;
}
public static boolean isInstantiated() {
if (instance == null) {
return false;
}
return true;
}
//获取锁
public static void acquireMutexLock() throws Exception {
mutexLock.acquire();
acquiredLock = true;
}
//释放锁
public static void releaseMutexLock() throws Exception {
mutexLock.release();
}
public static boolean isAcquiredLock() {
return acquiredLock;
}
@Override
public void destroy() throws Exception {
}
@Override
public void afterPropertiesSet() throws Exception {
}
}
package com.falsh.tss;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import com.falsh.tss.mutex.MutexLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@EnableDubboConfiguration
@EnableAutoConfiguration
@SpringBootApplication
@ComponentScan("com.zhifu")
public class Application {
private static final Log logger = LogFactory.getLog(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
try {
MutexLock.getInstance().acquireMutexLock();
while (true) {
logger.warn("锁在我这");
Thread.sleep(60000);
}
} catch (Exception e) {
logger.info("获取锁失败", e);
} finally{
try {
MutexLock.getInstance().releaseMutexLock();
logger.warn("释放了锁");
} catch (Exception e) {
logger.error("释放锁异常", e);
}
}
}
}
配置:
application-dev.properties
spring.dubbo.application.name=myjobtss
spring.dubbo.registry.address=zookeeper://192.168.x.x:2181
spring.dubbo.protocol.name=dubbo
cron.job.myJob1=0 0/1 * * * ?
zk.address=192.168.x.x:2181
zk.lock.path=/myjob-quartz-locks