Curator一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,本文主要介绍使用curator框架来实现zookeeper的分布式锁实现方案。
使用curator来实现zookeeper分布式锁有多种方案,本文主要使用 InterProcessMutex 来实现全局共享锁。
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-testartifactId>
<version>2.12.0version>
dependency>
在 application.yml 中配置 zookeeper 的配置:
zookeeper:
#每次重试时间间隔,单位毫秒
baseSleepTimeMs: 1000
#重试次数
maxRetries: 3
#zookeeper服务连接id与端口
connectString: 127.0.0.1:2181
#会话超时时间,单位毫秒
sessionTimeoutMs: 5000
#连接创建超时时间,单位毫秒
connection-timeout-ms: 5000
后台定义一个 ZookeeperProperties 类注入 zookeeper 的配置,实现如下:
/**
* 注入 zookeeper 的配置信息
*/
@Component
@ConfigurationProperties(prefix = "zookeeper")
public class ZookeeperProperties {
private int baseSleepTimeMs;
private int maxRetries;
private String connectString;
private int sessionTimeoutMs;
private int connectionTimeoutMs;
...
}
CuratorFramework 类封装了对zookeeper底层的操作,配置如下:
/**
* curator 配置
*/
@Configuration
public class ZookeeperConfig {
/**
* 获取 CuratorFramework
* 使用 curator 操作 zookeeper
* @return
*/
@Bean
public CuratorFramework curatorFramework(ZookeeperProperties zookeeperProperties) {
//配置zookeeper连接的重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(zookeeperProperties.getBaseSleepTimeMs(), zookeeperProperties.getMaxRetries());
//构建 CuratorFramework
CuratorFramework curatorFramework =
CuratorFrameworkFactory.builder()
.connectString(zookeeperProperties.getConnectString())
.sessionTimeoutMs(zookeeperProperties.getSessionTimeoutMs())
.connectionTimeoutMs(zookeeperProperties.getConnectionTimeoutMs())
.retryPolicy(retryPolicy)
.build();
//连接 zookeeper
curatorFramework.start();
return curatorFramework;
}
}
使用模板模式,将加锁、释放锁的通用代码给抽取出来,通过接口回调方式回调具体的业务实现逻辑,实现如下:
/**
* 可重入共享锁组件
*/
@Component
public class ShardReentrantLockComponent {
@Autowired
private CuratorFramework curatorFramework;
/**
* 该方法为模板方法,获得锁后回调 BaseLockHandler 中的 handler 方法
* @return
*/
public <T> T acquireLock(BaseLockHandler<T> baseLockHandler) {
//获取要加锁的路径
String path = baseLockHandler.getPath();
//获取超时时间
int timeOut = baseLockHandler.getTimeOut();
//时间单位
TimeUnit timeUnit = baseLockHandler.getTimeUnit();
//通过 InterProcessMutex 该类来获取可重入共性锁
InterProcessMutex lock = new InterProcessMutex(this.curatorFramework, path);
//用于标识是否获取了锁
boolean acquire = false;
try {
try {
//成功获得锁后返回 true
acquire = lock.acquire(timeOut, timeUnit);
} catch (Exception e) {
e.printStackTrace();
}
if(acquire) {
//获得锁后回调具体的业务逻辑
return baseLockHandler.handler();
} else {
//没有获得锁返回 null
return null;
}
} finally {
try {
if(acquire) {
//释放锁
lock.release();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
BaseLockHandler 抽象类回调具体的业务逻辑,实现如下:
/**
* 业务逻辑抽象类
* @param
*/
public abstract class BaseLockHandler<T> {
//获得锁的默认超时时间,默认为 200ms
private static final int DEFAULT_TIME_OUT = 200;
//加锁的资源路径
private String path;
public BaseLockHandler(String path) {
this.path = path;
}
/**
* 具体的业务实现逻辑,重写该方法
* @return
*/
public abstract T handler();
/**
* 返回加锁的路径
* @return
*/
public String getPath() {
return this.path;
}
/**
* 返回加锁的超时时间
* @return
*/
public int getTimeOut() {
return DEFAULT_TIME_OUT;
}
/**
* 时间单位
* @return
*/
public TimeUnit getTimeUnit() {
return TimeUnit.MILLISECONDS;
}
}
获取锁的实现类:
@Service
public class ZookeeperLockServiceImpl implements ILockService {
@Autowired
private CuratorFramework curatorFramework;
@Override
public AutoCloseable getLocker(String path, Integer overTimeMs) throws Exception {
InterProcessMutex mutex = new InterProcessMutex(curatorFramework, path);
Locker locker = null;
try {
// 使用AutoCloseable 实现自动关闭资源。在try结束的时候,会自动将这些资源关闭(调用close方法)
locker = new Locker(mutex, overTimeMs, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw e;
}
return locker;
}
}
使用静态方法包装:
@Component
public class ZookeeperLockProvider implements InitializingBean {
private static ZookeeperLockProvider self;
// 自动注入获取锁的实现类
@Resource
private ILockService lockService;
@Override
public void afterPropertiesSet() throws Exception {
self = this;
}
/**
* 静态方法,真正供外部调用
*
* @param path
* @param overTimeMs
* @return
* @throws Exception
*/
public static AutoCloseable getLock(String path, int overTimeMs) throws Exception {
return self.lockService.getLocker(path, overTimeMs);
}
}
这样使用更加方便:
// 在try结束的时候,会自动将这些资源关闭(调用close方法)
try (AutoCloseable lock = ZookeeperLockProvider.getLock(path, DEFAULT_LOCK_MILLIS_TIME)) {
// 执行加锁后的操作
}