dolphinschedule(ds) 除了使用zookeeper 还用etcd和mysql 实现了服务注册 分布式锁的功能,这里通过源码角度来看ds是如何通过mysql实现分布式锁的
1. 分布式锁实现
DROP TABLE IF EXISTS `t_ds_jdbc_registry_lock`;
CREATE TABLE `t_ds_jdbc_registry_lock`
(
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`lock_key` varchar(256) NOT NULL COMMENT 'lock path',
`lock_owner` varchar(256) NOT NULL COMMENT 'the lock owner, ip_processId',
`last_term` bigint NOT NULL COMMENT 'last term time',
`last_update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'last update time',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time',
PRIMARY KEY (`id`),
unique (`lock_key`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
这里是通过t_ds_jdbc_registry_lock这张表来实现的分布式锁的能力
2. JdbcRegistry
@Component
@ConditionalOnProperty(prefix = "registry", name = "type", havingValue = "jdbc")
@Slf4j
public class JdbcRegistry implements Registry {
......
}
从这里可以看出在application.yml 配置了registry.type为jdbc时,会注册这个bean,spring容器起来后就会调用如下方法
@PostConstruct
public void start() {
log.info("Starting Jdbc Registry...");
// start a jdbc connect check
ephemeralDateManager.start();
subscribeDataManager.start();
registryLockManager.start();
log.info("Started Jdbc Registry...");
}
对于分布式锁管理,对应的是registryLockManager
3. registryLockManager
public void start() {
lockTermUpdateThreadPool.scheduleWithFixedDelay(
new LockTermRefreshTask(lockHoldMap, jdbcOperator),
registryProperties.getTermRefreshInterval().toMillis(),
registryProperties.getTermRefreshInterval().toMillis(),
TimeUnit.MILLISECONDS);
}
这里启动了一个定时任务,执行LockTermRefreshTask
4. LockTermRefreshTask
public void run() {
try {
if (lockHoldMap.isEmpty()) {
return;
}
List<Long> lockIds = lockHoldMap.values()
.stream()
.map(JdbcRegistryLock::getId)
.collect(Collectors.toList());
if (!jdbcOperator.updateLockTerm(lockIds)) {
log.warn("Update the lock: {} term failed.", lockIds);
}
jdbcOperator.clearExpireLock();
} catch (Exception e) {
log.error("Update lock term error", e);
}
}
从这里可以看出,是把自己当前申请到的lock按两秒一次的频率刷上一遍
jdbcOperator.updateLockTerm(lockIds)
public boolean updateLockTerm(List<Long> lockIds) {
if (CollectionUtils.isEmpty(lockIds)) {
return true;
}
return jdbcRegistryLockMapper.updateTermByIds(lockIds, System.currentTimeMillis()) > 0;
}
更新他的last_term字段
5. 上面是维护锁,再看申请锁
public boolean acquireLock(String key) {
try {
registryLockManager.acquireLock(key);
return true;
} catch (RegistryException e) {
throw e;
} catch (Exception e) {
throw new RegistryException(String.format("Acquire lock: %s error", key), e);
}
}
6. registryLockManager.acquireLock
public void acquireLock(String lockKey) throws RegistryException {
// maybe we can use the computeIf absent
lockHoldMap.computeIfAbsent(lockKey, key -> {
JdbcRegistryLock jdbcRegistryLock;
try {
while ((jdbcRegistryLock = jdbcOperator.tryToAcquireLock(lockKey)) == null) {
log.debug("Acquire the lock {} failed try again", key);
// acquire failed, wait and try again
ThreadUtils.sleep(JdbcRegistryConstant.LOCK_ACQUIRE_INTERVAL);
}
} catch (SQLException e) {
throw new RegistryException("Acquire the lock error", e);
}
return jdbcRegistryLock;
});
}
调用jdbcOperator.tryToAcquireLock去操作库
public JdbcRegistryLock tryToAcquireLock(String key) throws SQLException {
JdbcRegistryLock jdbcRegistryLock = JdbcRegistryLock.builder()
.lockKey(key)
.lockOwner(JdbcRegistryConstant.LOCK_OWNER)
.lastTerm(System.currentTimeMillis())
.build();
try {
jdbcRegistryLockMapper.insert(jdbcRegistryLock);
return jdbcRegistryLock;
} catch (Exception e) {
if (e instanceof SQLIntegrityConstraintViolationException) {
return null;
}
throw e;
}
}
这里利用 mysql unique key的特性,如果已经有这个key了,那么会返回null,进入sleep循环等待,来实现AcquireLock的操作
7. releaseLock
public void releaseLock(String lockKey) {
JdbcRegistryLock jdbcRegistryLock = lockHoldMap.get(lockKey);
if (jdbcRegistryLock != null) {
try {
// the lock is unExit
jdbcOperator.releaseLock(jdbcRegistryLock.getId());
lockHoldMap.remove(lockKey);
} catch (SQLException e) {
throw new RegistryException(String.format("Release lock: %s error", lockKey), e);
}
}
}
通过删除表中所数据和清除lockHoldMap中的数据实现释放key对应的锁