Browse Source
[Feature#3961]Registry SPI All the logical structure of the registry must be converted into a tree structure within the system, so some plug-ins must be converted internally, such as ETCD The registry supports distributed locks. todo: The specific information about the registration center of the API module needs to be adjusted.2.0.7-release
Kirs
4 years ago
committed by
CalvinKirs
135 changed files with 3149 additions and 3531 deletions
@ -1,46 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.common.plugin; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
import org.junit.Ignore; |
||||
import org.junit.Test; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
|
||||
public class DolphinSchedulerPluginLoaderTest { |
||||
|
||||
/** |
||||
* Method: loadPlugins() |
||||
*/ |
||||
@Test |
||||
@Ignore |
||||
public void testLoadPlugins() { |
||||
PluginManagerTest pluginManager = new PluginManagerTest(); |
||||
DolphinPluginManagerConfig alertPluginManagerConfig = new DolphinPluginManagerConfig(); |
||||
String path = Objects.requireNonNull(DolphinPluginLoader.class.getClassLoader().getResource("")).getPath(); |
||||
alertPluginManagerConfig.setPlugins(path + "../../../dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml"); |
||||
DolphinPluginLoader alertPluginLoader = new DolphinPluginLoader(alertPluginManagerConfig, ImmutableList.of(pluginManager)); |
||||
try { |
||||
//alertPluginLoader.loadPlugins();
|
||||
} catch (Exception e) { |
||||
throw new RuntimeException("load Alert Plugin Failed !", e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- |
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
||||
~ contributor license agreements. See the NOTICE file distributed with |
||||
~ this work for additional information regarding copyright ownership. |
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
~ (the "License"); you may not use this file except in compliance with |
||||
~ the License. You may obtain a copy of the License at |
||||
~ |
||||
~ http://www.apache.org/licenses/LICENSE-2.0 |
||||
~ |
||||
~ Unless required by applicable law or agreed to in writing, software |
||||
~ distributed under the License is distributed on an "AS IS" BASIS, |
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
~ See the License for the specific language governing permissions and |
||||
~ limitations under the License. |
||||
--> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
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"> |
||||
<parent> |
||||
<artifactId>dolphinscheduler-registry-plugin</artifactId> |
||||
<groupId>org.apache.dolphinscheduler</groupId> |
||||
<version>1.3.6-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
|
||||
<artifactId>dolphinscheduler-registry-zookeeper</artifactId> |
||||
<!-- can be load as a Alert Plugin when development and run server in IDE --> |
||||
<packaging>dolphinscheduler-plugin</packaging> |
||||
|
||||
<dependencies> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.zookeeper</groupId> |
||||
<artifactId>zookeeper</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.curator</groupId> |
||||
<artifactId>curator-framework</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.curator</groupId> |
||||
<artifactId>curator-recipes</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>ch.qos.logback</groupId> |
||||
<artifactId>logback-classic</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.slf4j</groupId> |
||||
<artifactId>slf4j-api</artifactId> |
||||
</dependency> |
||||
|
||||
|
||||
<dependency> |
||||
<groupId>org.apache.curator</groupId> |
||||
<artifactId>curator-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>junit</groupId> |
||||
<artifactId>junit</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
</dependencies> |
||||
|
||||
<build> |
||||
<finalName>dolphinscheduler-registry-zookeeper-${project.version}</finalName> |
||||
</build> |
||||
|
||||
</project> |
@ -0,0 +1,64 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.plugin.registry.zookeeper; |
||||
|
||||
import java.util.function.Function; |
||||
|
||||
public enum ZookeeperConfiguration { |
||||
|
||||
NAME_SPACE("namespace", "dolphinscheduler", value -> value), |
||||
SERVERS("servers", null, value -> value), |
||||
|
||||
/** |
||||
* Initial amount of time to wait between retries |
||||
*/ |
||||
BASE_SLEEP_TIME("base.sleep.time.ms", 60, Integer::valueOf), |
||||
MAX_SLEEP_TIME("max.sleep.ms", 300, Integer::valueOf), |
||||
DIGEST("digest", null, value -> value), |
||||
|
||||
MAX_RETRIES("max.retries", 5, Integer::valueOf), |
||||
|
||||
|
||||
//todo
|
||||
SESSION_TIMEOUT_MS("session.timeout.ms", 1000, Integer::valueOf), |
||||
CONNECTION_TIMEOUT_MS("connection.timeout.ms", 1000, Integer::valueOf), |
||||
|
||||
BLOCK_UNTIL_CONNECTED_WAIT_MS("block.until.connected.wait", 600, Integer::valueOf), |
||||
; |
||||
private final String name; |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
private final Object defaultValue; |
||||
|
||||
private final Function<String, Object> converter; |
||||
|
||||
<T> ZookeeperConfiguration(String name, T defaultValue, Function<String, T> converter) { |
||||
this.name = name; |
||||
this.defaultValue = defaultValue; |
||||
this.converter = (Function<String, Object>) converter; |
||||
} |
||||
|
||||
public <T> T getParameterValue(String param) { |
||||
Object value = param != null ? converter.apply(param) : defaultValue; |
||||
return (T) value; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,56 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.plugin.registry.zookeeper; |
||||
|
||||
import org.apache.dolphinscheduler.spi.register.RegistryConnectListener; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryConnectState; |
||||
|
||||
import org.apache.curator.framework.CuratorFramework; |
||||
import org.apache.curator.framework.state.ConnectionState; |
||||
import org.apache.curator.framework.state.ConnectionStateListener; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class ZookeeperConnectionStateListener implements ConnectionStateListener { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ZookeeperConnectionStateListener.class); |
||||
|
||||
private RegistryConnectListener registryConnectListener; |
||||
|
||||
public ZookeeperConnectionStateListener(RegistryConnectListener registryConnectListener) { |
||||
this.registryConnectListener = registryConnectListener; |
||||
} |
||||
|
||||
@Override |
||||
public void stateChanged(CuratorFramework client, ConnectionState newState) { |
||||
|
||||
if (newState == ConnectionState.LOST) { |
||||
logger.error("connection lost from zookeeper"); |
||||
registryConnectListener.notify(RegistryConnectState.LOST); |
||||
} else if (newState == ConnectionState.RECONNECTED) { |
||||
logger.info("reconnected to zookeeper"); |
||||
registryConnectListener.notify(RegistryConnectState.RECONNECTED); |
||||
} else if (newState == ConnectionState.SUSPENDED) { |
||||
logger.warn("zookeeper connection SUSPENDED"); |
||||
registryConnectListener.notify(RegistryConnectState.SUSPENDED); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,335 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.plugin.registry.zookeeper; |
||||
|
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.BASE_SLEEP_TIME; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.BLOCK_UNTIL_CONNECTED_WAIT_MS; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.CONNECTION_TIMEOUT_MS; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.DIGEST; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.MAX_RETRIES; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.NAME_SPACE; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.SERVERS; |
||||
import static org.apache.dolphinscheduler.plugin.registry.zookeeper.ZookeeperConfiguration.SESSION_TIMEOUT_MS; |
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS; |
||||
|
||||
import org.apache.dolphinscheduler.spi.register.DataChangeEvent; |
||||
import org.apache.dolphinscheduler.spi.register.ListenerManager; |
||||
import org.apache.dolphinscheduler.spi.register.Registry; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryConnectListener; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryException; |
||||
import org.apache.dolphinscheduler.spi.register.SubscribeListener; |
||||
|
||||
import org.apache.curator.RetryPolicy; |
||||
import org.apache.curator.framework.CuratorFramework; |
||||
import org.apache.curator.framework.CuratorFrameworkFactory; |
||||
import org.apache.curator.framework.api.ACLProvider; |
||||
import org.apache.curator.framework.api.transaction.TransactionOp; |
||||
import org.apache.curator.framework.recipes.cache.TreeCache; |
||||
import org.apache.curator.framework.recipes.cache.TreeCacheEvent; |
||||
import org.apache.curator.framework.recipes.cache.TreeCacheListener; |
||||
import org.apache.curator.framework.recipes.locks.InterProcessMutex; |
||||
import org.apache.curator.retry.ExponentialBackoffRetry; |
||||
import org.apache.curator.utils.CloseableUtils; |
||||
import org.apache.zookeeper.CreateMode; |
||||
import org.apache.zookeeper.ZooDefs; |
||||
import org.apache.zookeeper.data.ACL; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Comparator; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import com.google.common.base.Strings; |
||||
|
||||
public class ZookeeperRegistry implements Registry { |
||||
|
||||
private CuratorFramework client; |
||||
|
||||
/** |
||||
* treeCache map |
||||
* k-subscribe key |
||||
* v-listener |
||||
*/ |
||||
private Map<String, TreeCache> treeCacheMap = new HashMap<>(); |
||||
|
||||
/** |
||||
* Distributed lock map |
||||
*/ |
||||
private ThreadLocal<Map<String, InterProcessMutex>> threadLocalLockMap = new ThreadLocal<>(); |
||||
|
||||
/** |
||||
* build retry policy |
||||
*/ |
||||
private static RetryPolicy buildRetryPolicy(Map<String, String> registerData) { |
||||
int baseSleepTimeMs = BASE_SLEEP_TIME.getParameterValue(registerData.get(BASE_SLEEP_TIME.getName())); |
||||
int maxRetries = MAX_RETRIES.getParameterValue(registerData.get(MAX_RETRIES.getName())); |
||||
int maxSleepMs = baseSleepTimeMs * maxRetries; |
||||
return new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries, maxSleepMs); |
||||
} |
||||
|
||||
/** |
||||
* build digest |
||||
*/ |
||||
private static void buildDigest(CuratorFrameworkFactory.Builder builder, String digest) { |
||||
builder.authorization(DIGEST.getName(), digest.getBytes(StandardCharsets.UTF_8)) |
||||
.aclProvider(new ACLProvider() { |
||||
@Override |
||||
public List<ACL> getDefaultAcl() { |
||||
return ZooDefs.Ids.CREATOR_ALL_ACL; |
||||
} |
||||
|
||||
@Override |
||||
public List<ACL> getAclForPath(final String path) { |
||||
return ZooDefs.Ids.CREATOR_ALL_ACL; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
public void init(Map<String, String> registerData) { |
||||
|
||||
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() |
||||
.connectString(SERVERS.getParameterValue(registerData.get(SERVERS.getName()))) |
||||
.retryPolicy(buildRetryPolicy(registerData)) |
||||
.namespace(NAME_SPACE.getParameterValue(registerData.get(NAME_SPACE.getName()))) |
||||
.sessionTimeoutMs(SESSION_TIMEOUT_MS.getParameterValue(registerData.get(SESSION_TIMEOUT_MS.getName()))) |
||||
.connectionTimeoutMs(CONNECTION_TIMEOUT_MS.getParameterValue(registerData.get(CONNECTION_TIMEOUT_MS.getName()))); |
||||
|
||||
String digest = DIGEST.getParameterValue(registerData.get(DIGEST.getName())); |
||||
if (!Strings.isNullOrEmpty(digest)) { |
||||
buildDigest(builder, digest); |
||||
} |
||||
client = builder.build(); |
||||
|
||||
client.start(); |
||||
try { |
||||
if (!client.blockUntilConnected(BLOCK_UNTIL_CONNECTED_WAIT_MS.getParameterValue(registerData.get(BLOCK_UNTIL_CONNECTED_WAIT_MS.getName())), MILLISECONDS)) { |
||||
client.close(); |
||||
throw new RegistryException("zookeeper connect timeout"); |
||||
} |
||||
} catch (InterruptedException e) { |
||||
Thread.currentThread().interrupt(); |
||||
throw new RegistryException("zookeeper connect error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void addConnectionStateListener(RegistryConnectListener registryConnectListener) { |
||||
client.getConnectionStateListenable().addListener(new ZookeeperConnectionStateListener(registryConnectListener)); |
||||
} |
||||
|
||||
@Override |
||||
public boolean subscribe(String path, SubscribeListener subscribeListener) { |
||||
if (null != treeCacheMap.get(path)) { |
||||
return false; |
||||
} |
||||
TreeCache treeCache = new TreeCache(client, path); |
||||
TreeCacheListener treeCacheListener = (client, event) -> { |
||||
TreeCacheEvent.Type type = event.getType(); |
||||
DataChangeEvent eventType = null; |
||||
String dataPath = null; |
||||
switch (type) { |
||||
case NODE_ADDED: |
||||
|
||||
dataPath = event.getData().getPath(); |
||||
eventType = DataChangeEvent.ADD; |
||||
break; |
||||
case NODE_UPDATED: |
||||
eventType = DataChangeEvent.UPDATE; |
||||
dataPath = event.getData().getPath(); |
||||
|
||||
break; |
||||
case NODE_REMOVED: |
||||
eventType = DataChangeEvent.REMOVE; |
||||
dataPath = event.getData().getPath(); |
||||
break; |
||||
default: |
||||
} |
||||
if (null != eventType && null != dataPath) { |
||||
ListenerManager.dataChange(path, dataPath, eventType); |
||||
} |
||||
}; |
||||
treeCache.getListenable().addListener(treeCacheListener); |
||||
treeCacheMap.put(path, treeCache); |
||||
try { |
||||
treeCache.start(); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("start zookeeper tree cache error", e); |
||||
} |
||||
ListenerManager.addListener(path, subscribeListener); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public void unsubscribe(String path) { |
||||
TreeCache treeCache = treeCacheMap.get(path); |
||||
treeCache.close(); |
||||
ListenerManager.removeListener(path); |
||||
} |
||||
|
||||
@Override |
||||
public String get(String key) { |
||||
try { |
||||
return new String(client.getData().forPath(key), StandardCharsets.UTF_8); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper get data error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void remove(String key) { |
||||
|
||||
try { |
||||
client.delete().deletingChildrenIfNeeded().forPath(key); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper remove error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isExisted(String key) { |
||||
try { |
||||
return null != client.checkExists().forPath(key); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper check key is existed error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void persist(String key, String value) { |
||||
try { |
||||
if (isExisted(key)) { |
||||
update(key, value); |
||||
return; |
||||
} |
||||
client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(key, value.getBytes(StandardCharsets.UTF_8)); |
||||
|
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper persist error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void persistEphemeral(String key, String value) { |
||||
try { |
||||
if (isExisted(key)) { |
||||
update(key, value); |
||||
return; |
||||
} |
||||
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(key, value.getBytes(StandardCharsets.UTF_8)); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper persist ephemeral error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void update(String key, String value) { |
||||
try { |
||||
if (!isExisted(key)) { |
||||
return; |
||||
} |
||||
TransactionOp transactionOp = client.transactionOp(); |
||||
client.transaction().forOperations(transactionOp.check().forPath(key), transactionOp.setData().forPath(key, value.getBytes(StandardCharsets.UTF_8))); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper update error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getChildren(String key) { |
||||
try { |
||||
List<String> result = client.getChildren().forPath(key); |
||||
result.sort(Comparator.reverseOrder()); |
||||
return result; |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper get children error", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean delete(String nodePath) { |
||||
try { |
||||
client.delete() |
||||
.deletingChildrenIfNeeded() |
||||
.forPath(nodePath); |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper delete key error", e); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean acquireLock(String key) { |
||||
|
||||
InterProcessMutex interProcessMutex = new InterProcessMutex(client, key); |
||||
try { |
||||
interProcessMutex.acquire(); |
||||
if (null == threadLocalLockMap.get()) { |
||||
threadLocalLockMap.set(new HashMap<>(3)); |
||||
} |
||||
threadLocalLockMap.get().put(key, interProcessMutex); |
||||
return true; |
||||
} catch (Exception e) { |
||||
try { |
||||
interProcessMutex.release(); |
||||
throw new RegistryException("zookeeper get lock error", e); |
||||
} catch (Exception exception) { |
||||
throw new RegistryException("zookeeper release lock error", e); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public boolean releaseLock(String key) { |
||||
if (null == threadLocalLockMap.get().get(key)) { |
||||
return false; |
||||
} |
||||
try { |
||||
threadLocalLockMap.get().get(key).release(); |
||||
threadLocalLockMap.get().remove(key); |
||||
if (threadLocalLockMap.get().isEmpty()) { |
||||
threadLocalLockMap.remove(); |
||||
} |
||||
} catch (Exception e) { |
||||
throw new RegistryException("zookeeper release lock error", e); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
public CuratorFramework getClient() { |
||||
return client; |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
treeCacheMap.forEach((key, value) -> value.close()); |
||||
waitForCacheClose(500); |
||||
CloseableUtils.closeQuietly(client); |
||||
} |
||||
|
||||
private void waitForCacheClose(long millis) { |
||||
try { |
||||
Thread.sleep(millis); |
||||
} catch (final InterruptedException ex) { |
||||
Thread.currentThread().interrupt(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.plugin.registry.zookeeper; |
||||
|
||||
import org.apache.dolphinscheduler.spi.register.Registry; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryFactory; |
||||
|
||||
/** |
||||
* Zookeeper registry factory |
||||
*/ |
||||
public class ZookeeperRegistryFactory implements RegistryFactory { |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return "zookeeper"; |
||||
} |
||||
|
||||
@Override |
||||
public Registry create() { |
||||
return new ZookeeperRegistry(); |
||||
} |
||||
} |
@ -0,0 +1,129 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.plugin.registry.zookeeper; |
||||
|
||||
import org.apache.dolphinscheduler.spi.register.DataChangeEvent; |
||||
import org.apache.dolphinscheduler.spi.register.SubscribeListener; |
||||
|
||||
import org.apache.curator.test.TestingServer; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.CountDownLatch; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Assert; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class ZookeeperRegistryTest { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ZookeeperRegistryTest.class); |
||||
|
||||
TestingServer server; |
||||
|
||||
ZookeeperRegistry registry = new ZookeeperRegistry(); |
||||
|
||||
@Before |
||||
public void before() throws Exception { |
||||
server = new TestingServer(true); |
||||
Map<String, String> registryConfig = new HashMap<>(); |
||||
registryConfig.put(ZookeeperConfiguration.SERVERS.getName(), server.getConnectString()); |
||||
registry.init(registryConfig); |
||||
registry.persist("/sub", ""); |
||||
} |
||||
|
||||
@Test |
||||
public void persistTest() { |
||||
registry.persist("/nodes/m1", ""); |
||||
registry.persist("/nodes/m2", ""); |
||||
Assert.assertEquals(Arrays.asList("m2", "m1"), registry.getChildren("/nodes")); |
||||
Assert.assertTrue(registry.isExisted("/nodes/m1")); |
||||
registry.delete("/nodes/m2"); |
||||
Assert.assertFalse(registry.isExisted("/nodes/m2")); |
||||
} |
||||
|
||||
@Test |
||||
public void lockTest() throws InterruptedException { |
||||
CountDownLatch preCountDownLatch = new CountDownLatch(1); |
||||
CountDownLatch allCountDownLatch = new CountDownLatch(2); |
||||
List<String> testData = new ArrayList<>(); |
||||
new Thread(() -> { |
||||
registry.acquireLock("/lock"); |
||||
preCountDownLatch.countDown(); |
||||
logger.info(Thread.currentThread().getName() + " :I got the lock, but I don't want to work. I want to rest for a while"); |
||||
try { |
||||
Thread.sleep(1000); |
||||
logger.info(Thread.currentThread().getName() + " :I'm going to start working"); |
||||
testData.add("thread1"); |
||||
|
||||
} catch (InterruptedException e) { |
||||
Thread.currentThread().interrupt(); |
||||
} finally { |
||||
logger.info(Thread.currentThread().getName() + " :I have finished my work, now I release the lock"); |
||||
registry.releaseLock("/lock"); |
||||
allCountDownLatch.countDown(); |
||||
} |
||||
}).start(); |
||||
preCountDownLatch.await(); |
||||
new Thread(() -> { |
||||
try { |
||||
logger.info(Thread.currentThread().getName() + " :I am trying to acquire the lock"); |
||||
registry.acquireLock("/lock"); |
||||
logger.info(Thread.currentThread().getName() + " :I got the lock and I started working"); |
||||
|
||||
testData.add("thread2"); |
||||
} finally { |
||||
registry.releaseLock("/lock"); |
||||
allCountDownLatch.countDown(); |
||||
} |
||||
|
||||
}).start(); |
||||
allCountDownLatch.await(); |
||||
Assert.assertEquals(testData, Arrays.asList("thread1", "thread2")); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void subscribeTest() { |
||||
boolean status = registry.subscribe("/sub", new TestListener()); |
||||
Assert.assertTrue(status); |
||||
|
||||
} |
||||
|
||||
class TestListener implements SubscribeListener { |
||||
|
||||
@Override |
||||
public void notify(String path, DataChangeEvent dataChangeEvent) { |
||||
logger.info("I'm test listener"); |
||||
} |
||||
} |
||||
|
||||
@After |
||||
public void after() throws IOException { |
||||
registry.close(); |
||||
server.close(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- |
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
||||
~ contributor license agreements. See the NOTICE file distributed with |
||||
~ this work for additional information regarding copyright ownership. |
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
~ (the "License"); you may not use this file except in compliance with |
||||
~ the License. You may obtain a copy of the License at |
||||
~ |
||||
~ http://www.apache.org/licenses/LICENSE-2.0 |
||||
~ |
||||
~ Unless required by applicable law or agreed to in writing, software |
||||
~ distributed under the License is distributed on an "AS IS" BASIS, |
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
~ See the License for the specific language governing permissions and |
||||
~ limitations under the License. |
||||
--> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
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"> |
||||
<parent> |
||||
<artifactId>dolphinscheduler</artifactId> |
||||
<groupId>org.apache.dolphinscheduler</groupId> |
||||
<version>1.3.6-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<groupId>org.apache.dolphinscheduler</groupId> |
||||
<artifactId>dolphinscheduler-registry-plugin</artifactId> |
||||
<packaging>pom</packaging> |
||||
|
||||
<dependencies> |
||||
<!-- dolphinscheduler --> |
||||
<dependency> |
||||
<groupId>org.apache.dolphinscheduler</groupId> |
||||
<artifactId>dolphinscheduler-spi</artifactId> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<modules> |
||||
<module>dolphinscheduler-registry-zookeeper</module> |
||||
</modules> |
||||
</project> |
@ -1,144 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import org.apache.dolphinscheduler.common.Constants; |
||||
import org.apache.dolphinscheduler.common.utils.DateUtils; |
||||
import org.apache.dolphinscheduler.common.utils.NetUtils; |
||||
import org.apache.dolphinscheduler.remote.utils.NamedThreadFactory; |
||||
import org.apache.dolphinscheduler.server.master.config.MasterConfig; |
||||
import org.apache.dolphinscheduler.server.registry.HeartBeatTask; |
||||
import org.apache.dolphinscheduler.server.registry.ZookeeperRegistryCenter; |
||||
|
||||
import org.apache.curator.framework.state.ConnectionState; |
||||
|
||||
import java.util.Date; |
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import com.google.common.collect.Sets; |
||||
|
||||
/** |
||||
* master registry |
||||
*/ |
||||
@Service |
||||
public class MasterRegistry { |
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MasterRegistry.class); |
||||
|
||||
/** |
||||
* zookeeper registry center |
||||
*/ |
||||
@Autowired |
||||
private ZookeeperRegistryCenter zookeeperRegistryCenter; |
||||
|
||||
/** |
||||
* master config |
||||
*/ |
||||
@Autowired |
||||
private MasterConfig masterConfig; |
||||
|
||||
/** |
||||
* heartbeat executor |
||||
*/ |
||||
private ScheduledExecutorService heartBeatExecutor; |
||||
|
||||
/** |
||||
* master start time |
||||
*/ |
||||
private String startTime; |
||||
|
||||
@PostConstruct |
||||
public void init() { |
||||
this.startTime = DateUtils.dateToString(new Date()); |
||||
this.heartBeatExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("HeartBeatExecutor")); |
||||
} |
||||
|
||||
/** |
||||
* registry |
||||
*/ |
||||
public void registry() { |
||||
String address = NetUtils.getAddr(masterConfig.getListenPort()); |
||||
String localNodePath = getMasterPath(); |
||||
zookeeperRegistryCenter.getRegisterOperator().persistEphemeral(localNodePath, ""); |
||||
zookeeperRegistryCenter.getRegisterOperator().getZkClient().getConnectionStateListenable().addListener( |
||||
(client, newState) -> { |
||||
if (newState == ConnectionState.LOST) { |
||||
logger.error("master : {} connection lost from zookeeper", address); |
||||
} else if (newState == ConnectionState.RECONNECTED) { |
||||
logger.info("master : {} reconnected to zookeeper", address); |
||||
} else if (newState == ConnectionState.SUSPENDED) { |
||||
logger.warn("master : {} connection SUSPENDED ", address); |
||||
} |
||||
}); |
||||
int masterHeartbeatInterval = masterConfig.getMasterHeartbeatInterval(); |
||||
HeartBeatTask heartBeatTask = new HeartBeatTask(startTime, |
||||
masterConfig.getMasterMaxCpuloadAvg(), |
||||
masterConfig.getMasterReservedMemory(), |
||||
Sets.newHashSet(getMasterPath()), |
||||
Constants.MASTER_TYPE, |
||||
zookeeperRegistryCenter); |
||||
|
||||
this.heartBeatExecutor.scheduleAtFixedRate(heartBeatTask, masterHeartbeatInterval, masterHeartbeatInterval, TimeUnit.SECONDS); |
||||
logger.info("master node : {} registry to ZK successfully with heartBeatInterval : {}s", address, masterHeartbeatInterval); |
||||
} |
||||
|
||||
/** |
||||
* remove registry info |
||||
*/ |
||||
public void unRegistry() { |
||||
String address = getLocalAddress(); |
||||
String localNodePath = getMasterPath(); |
||||
zookeeperRegistryCenter.getRegisterOperator().remove(localNodePath); |
||||
logger.info("master node : {} unRegistry to ZK.", address); |
||||
heartBeatExecutor.shutdown(); |
||||
logger.info("heartbeat executor shutdown"); |
||||
} |
||||
|
||||
/** |
||||
* get master path |
||||
*/ |
||||
public String getMasterPath() { |
||||
String address = getLocalAddress(); |
||||
return this.zookeeperRegistryCenter.getMasterPath() + "/" + address; |
||||
} |
||||
|
||||
/** |
||||
* get local address |
||||
*/ |
||||
private String getLocalAddress() { |
||||
return NetUtils.getAddr(masterConfig.getListenPort()); |
||||
} |
||||
|
||||
/** |
||||
* get zookeeper registry center |
||||
* @return ZookeeperRegistryCenter |
||||
*/ |
||||
public ZookeeperRegistryCenter getZookeeperRegistryCenter() { |
||||
return zookeeperRegistryCenter; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,90 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import static org.apache.dolphinscheduler.common.Constants.REGISTRY_DOLPHINSCHEDULER_MASTERS; |
||||
import static org.apache.dolphinscheduler.common.Constants.REGISTRY_DOLPHINSCHEDULER_WORKERS; |
||||
|
||||
import org.apache.dolphinscheduler.common.Constants; |
||||
import org.apache.dolphinscheduler.common.enums.NodeType; |
||||
import org.apache.dolphinscheduler.spi.register.DataChangeEvent; |
||||
import org.apache.dolphinscheduler.spi.register.SubscribeListener; |
||||
|
||||
import javax.annotation.Resource; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class MasterRegistryDataListener implements SubscribeListener { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MasterRegistryDataListener.class); |
||||
|
||||
@Resource |
||||
MasterRegistryClient masterRegistryClient; |
||||
|
||||
@Override |
||||
public void notify(String path, DataChangeEvent event) { |
||||
//monitor master
|
||||
if (path.startsWith(REGISTRY_DOLPHINSCHEDULER_MASTERS + Constants.SINGLE_SLASH)) { |
||||
handleMasterEvent(event, path); |
||||
} else if (path.startsWith(REGISTRY_DOLPHINSCHEDULER_WORKERS + Constants.SINGLE_SLASH)) { |
||||
//monitor worker
|
||||
handleWorkerEvent(event, path); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* monitor master |
||||
* |
||||
* @param event event |
||||
* @param path path |
||||
*/ |
||||
public void handleMasterEvent(DataChangeEvent event, String path) { |
||||
switch (event) { |
||||
case ADD: |
||||
logger.info("master node added : {}", path); |
||||
break; |
||||
case REMOVE: |
||||
masterRegistryClient.removeNodePath(path, NodeType.MASTER, true); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* monitor worker |
||||
* |
||||
* @param event event |
||||
* @param path path |
||||
*/ |
||||
public void handleWorkerEvent(DataChangeEvent event, String path) { |
||||
switch (event) { |
||||
case ADD: |
||||
logger.info("worker node added : {}", path); |
||||
break; |
||||
case REMOVE: |
||||
logger.info("worker node deleted : {}", path); |
||||
masterRegistryClient.removeNodePath(path, NodeType.WORKER, true); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,239 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.registry; |
||||
|
||||
import static org.apache.dolphinscheduler.common.Constants.SINGLE_SLASH; |
||||
import static org.apache.dolphinscheduler.common.Constants.UNDERLINE; |
||||
|
||||
import org.apache.dolphinscheduler.common.Constants; |
||||
import org.apache.dolphinscheduler.common.IStoppable; |
||||
import org.apache.dolphinscheduler.service.zk.RegisterOperator; |
||||
import org.apache.dolphinscheduler.service.zk.ZookeeperConfig; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* zookeeper register center |
||||
*/ |
||||
@Service |
||||
public class ZookeeperRegistryCenter implements InitializingBean { |
||||
|
||||
private final AtomicBoolean isStarted = new AtomicBoolean(false); |
||||
|
||||
|
||||
@Autowired |
||||
protected RegisterOperator registerOperator; |
||||
@Autowired |
||||
private ZookeeperConfig zookeeperConfig; |
||||
|
||||
/** |
||||
* nodes namespace |
||||
*/ |
||||
public String NODES; |
||||
|
||||
/** |
||||
* master path |
||||
*/ |
||||
public String MASTER_PATH; |
||||
|
||||
/** |
||||
* worker path |
||||
*/ |
||||
public String WORKER_PATH; |
||||
|
||||
public final String EMPTY = ""; |
||||
|
||||
private IStoppable stoppable; |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws Exception { |
||||
NODES = zookeeperConfig.getDsRoot() + "/nodes"; |
||||
MASTER_PATH = NODES + "/master"; |
||||
WORKER_PATH = NODES + "/worker"; |
||||
|
||||
init(); |
||||
} |
||||
|
||||
/** |
||||
* init node persist |
||||
*/ |
||||
public void init() { |
||||
if (isStarted.compareAndSet(false, true)) { |
||||
initNodes(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* init nodes |
||||
*/ |
||||
private void initNodes() { |
||||
registerOperator.persist(MASTER_PATH, EMPTY); |
||||
registerOperator.persist(WORKER_PATH, EMPTY); |
||||
} |
||||
|
||||
/** |
||||
* close |
||||
*/ |
||||
public void close() { |
||||
if (isStarted.compareAndSet(true, false) && registerOperator != null) { |
||||
registerOperator.close(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* get master path |
||||
* |
||||
* @return master path |
||||
*/ |
||||
public String getMasterPath() { |
||||
return MASTER_PATH; |
||||
} |
||||
|
||||
/** |
||||
* get worker path |
||||
* |
||||
* @return worker path |
||||
*/ |
||||
public String getWorkerPath() { |
||||
return WORKER_PATH; |
||||
} |
||||
|
||||
/** |
||||
* get master nodes directly |
||||
* |
||||
* @return master nodes |
||||
*/ |
||||
public Set<String> getMasterNodesDirectly() { |
||||
List<String> masters = getChildrenKeys(MASTER_PATH); |
||||
return new HashSet<>(masters); |
||||
} |
||||
|
||||
/** |
||||
* get worker nodes directly |
||||
* |
||||
* @return master nodes |
||||
*/ |
||||
public Set<String> getWorkerNodesDirectly() { |
||||
List<String> workers = getChildrenKeys(WORKER_PATH); |
||||
return new HashSet<>(workers); |
||||
} |
||||
|
||||
/** |
||||
* get worker group directly |
||||
* |
||||
* @return worker group nodes |
||||
*/ |
||||
public Set<String> getWorkerGroupDirectly() { |
||||
List<String> workers = getChildrenKeys(getWorkerPath()); |
||||
return new HashSet<>(workers); |
||||
} |
||||
|
||||
/** |
||||
* get worker group nodes |
||||
* |
||||
* @param workerGroup |
||||
* @return |
||||
*/ |
||||
public Set<String> getWorkerGroupNodesDirectly(String workerGroup) { |
||||
List<String> workers = getChildrenKeys(getWorkerGroupPath(workerGroup)); |
||||
return new HashSet<>(workers); |
||||
} |
||||
|
||||
/** |
||||
* whether worker path |
||||
* |
||||
* @param path path |
||||
* @return result |
||||
*/ |
||||
public boolean isWorkerPath(String path) { |
||||
return path != null && path.contains(WORKER_PATH); |
||||
} |
||||
|
||||
/** |
||||
* whether master path |
||||
* |
||||
* @param path path |
||||
* @return result |
||||
*/ |
||||
public boolean isMasterPath(String path) { |
||||
return path != null && path.contains(MASTER_PATH); |
||||
} |
||||
|
||||
/** |
||||
* get worker group path |
||||
* |
||||
* @param workerGroup workerGroup |
||||
* @return worker group path |
||||
*/ |
||||
public String getWorkerGroupPath(String workerGroup) { |
||||
return WORKER_PATH + "/" + workerGroup; |
||||
} |
||||
|
||||
/** |
||||
* get children nodes |
||||
* |
||||
* @param key key |
||||
* @return children nodes |
||||
*/ |
||||
public List<String> getChildrenKeys(final String key) { |
||||
return registerOperator.getChildrenKeys(key); |
||||
} |
||||
|
||||
/** |
||||
* @return get dead server node parent path |
||||
*/ |
||||
public String getDeadZNodeParentPath() { |
||||
return registerOperator.getZookeeperConfig().getDsRoot() + Constants.ZOOKEEPER_DOLPHINSCHEDULER_DEAD_SERVERS; |
||||
} |
||||
|
||||
public void setStoppable(IStoppable stoppable) { |
||||
this.stoppable = stoppable; |
||||
} |
||||
|
||||
public IStoppable getStoppable() { |
||||
return stoppable; |
||||
} |
||||
|
||||
/** |
||||
* check dead server or not , if dead, stop self |
||||
* |
||||
* @param zNode node path |
||||
* @param serverType master or worker prefix |
||||
* @return true if not exists |
||||
* @throws Exception errors |
||||
*/ |
||||
protected boolean checkIsDeadServer(String zNode, String serverType) throws Exception { |
||||
// ip_sequence_no
|
||||
String[] zNodesPath = zNode.split("\\/"); |
||||
String ipSeqNo = zNodesPath[zNodesPath.length - 1]; |
||||
String deadServerPath = getDeadZNodeParentPath() + SINGLE_SLASH + serverType + UNDERLINE + ipSeqNo; |
||||
|
||||
return !registerOperator.isExisted(zNode) || registerOperator.isExisted(deadServerPath); |
||||
} |
||||
|
||||
public RegisterOperator getRegisterOperator() { |
||||
return registerOperator; |
||||
} |
||||
} |
@ -0,0 +1,105 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import static org.mockito.BDDMockito.given; |
||||
import static org.mockito.Mockito.doNothing; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.CommandType; |
||||
import org.apache.dolphinscheduler.common.enums.NodeType; |
||||
import org.apache.dolphinscheduler.common.model.Server; |
||||
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskInstance; |
||||
import org.apache.dolphinscheduler.server.master.config.MasterConfig; |
||||
import org.apache.dolphinscheduler.service.process.ProcessService; |
||||
import org.apache.dolphinscheduler.service.registry.RegistryClient; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Date; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
/** |
||||
* MasterRegistryClientTest |
||||
*/ |
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class MasterRegistryClientTest { |
||||
|
||||
@InjectMocks |
||||
private MasterRegistryClient masterRegistryClient; |
||||
|
||||
@Mock |
||||
private MasterConfig masterConfig; |
||||
|
||||
@Mock |
||||
private RegistryClient registryClient; |
||||
|
||||
@Mock |
||||
private ScheduledExecutorService heartBeatExecutor; |
||||
@Mock |
||||
private ProcessService processService; |
||||
|
||||
@Before |
||||
public void before() throws Exception { |
||||
given(registryClient.getLock(Mockito.anyString())).willReturn(true); |
||||
given(registryClient.getMasterFailoverLockPath()).willReturn("/path"); |
||||
given(registryClient.releaseLock(Mockito.anyString())).willReturn(true); |
||||
given(registryClient.getHostByEventDataPath(Mockito.anyString())).willReturn("127.0.0.1:8080"); |
||||
doNothing().when(registryClient).handleDeadServer(Mockito.anyString(), Mockito.any(NodeType.class), Mockito.anyString()); |
||||
ProcessInstance processInstance = new ProcessInstance(); |
||||
processInstance.setId(1); |
||||
processInstance.setHost("127.0.0.1:8080"); |
||||
processInstance.setHistoryCmd("xxx"); |
||||
processInstance.setCommandType(CommandType.STOP); |
||||
given(processService.queryNeedFailoverProcessInstances(Mockito.anyString())).willReturn(Arrays.asList(processInstance)); |
||||
doNothing().when(processService).processNeedFailoverProcessInstances(Mockito.any(ProcessInstance.class)); |
||||
TaskInstance taskInstance = new TaskInstance(); |
||||
taskInstance.setId(1); |
||||
taskInstance.setStartTime(new Date()); |
||||
taskInstance.setHost("127.0.0.1:8080"); |
||||
given(processService.queryNeedFailoverTaskInstances(Mockito.anyString())).willReturn(Arrays.asList(taskInstance)); |
||||
given(processService.findProcessInstanceDetailById(Mockito.anyInt())).willReturn(processInstance); |
||||
given(registryClient.checkNodeExists(Mockito.anyString(), Mockito.any())).willReturn(true); |
||||
Server server = new Server(); |
||||
server.setHost("127.0.0.1"); |
||||
server.setPort(8080); |
||||
server.setCreateTime(new Date()); |
||||
given(registryClient.getServerList(NodeType.WORKER)).willReturn(Arrays.asList(server)); |
||||
} |
||||
|
||||
@Test |
||||
public void registryTest() { |
||||
masterRegistryClient.registry(); |
||||
} |
||||
|
||||
@Test |
||||
public void removeNodePathTest() { |
||||
|
||||
masterRegistryClient.removeNodePath("/path", NodeType.MASTER, false); |
||||
masterRegistryClient.removeNodePath("/path", NodeType.MASTER, true); |
||||
//Cannot mock static methods
|
||||
masterRegistryClient.removeNodePath("/path", NodeType.WORKER, true); |
||||
} |
||||
} |
@ -1,79 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import static org.apache.dolphinscheduler.common.Constants.HEARTBEAT_FOR_ZOOKEEPER_INFO_LENGTH; |
||||
|
||||
import org.apache.dolphinscheduler.common.utils.NetUtils; |
||||
import org.apache.dolphinscheduler.remote.utils.Constants; |
||||
import org.apache.dolphinscheduler.server.master.config.MasterConfig; |
||||
import org.apache.dolphinscheduler.server.registry.ZookeeperRegistryCenter; |
||||
import org.apache.dolphinscheduler.server.zk.SpringZKServer; |
||||
import org.apache.dolphinscheduler.service.zk.CuratorZookeeperClient; |
||||
import org.apache.dolphinscheduler.service.zk.ZookeeperCachedOperator; |
||||
import org.apache.dolphinscheduler.service.zk.ZookeeperConfig; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.junit4.SpringRunner; |
||||
|
||||
/** |
||||
* master registry test |
||||
*/ |
||||
@RunWith(SpringRunner.class) |
||||
@ContextConfiguration(classes = {SpringZKServer.class, MasterRegistry.class, ZookeeperRegistryCenter.class, |
||||
MasterConfig.class, ZookeeperCachedOperator.class, ZookeeperConfig.class, CuratorZookeeperClient.class}) |
||||
public class MasterRegistryTest { |
||||
|
||||
@Autowired |
||||
private MasterRegistry masterRegistry; |
||||
|
||||
@Autowired |
||||
private ZookeeperRegistryCenter zookeeperRegistryCenter; |
||||
|
||||
@Autowired |
||||
private MasterConfig masterConfig; |
||||
|
||||
@Test |
||||
public void testRegistry() throws InterruptedException { |
||||
masterRegistry.registry(); |
||||
String masterPath = zookeeperRegistryCenter.getMasterPath(); |
||||
TimeUnit.SECONDS.sleep(masterConfig.getMasterHeartbeatInterval() + 2); //wait heartbeat info write into zk node
|
||||
String masterNodePath = masterPath + "/" + (NetUtils.getAddr(Constants.LOCAL_ADDRESS, masterConfig.getListenPort())); |
||||
String heartbeat = zookeeperRegistryCenter.getRegisterOperator().get(masterNodePath); |
||||
Assert.assertEquals(HEARTBEAT_FOR_ZOOKEEPER_INFO_LENGTH, heartbeat.split(",").length); |
||||
masterRegistry.unRegistry(); |
||||
} |
||||
|
||||
@Test |
||||
public void testUnRegistry() throws InterruptedException { |
||||
masterRegistry.init(); |
||||
masterRegistry.registry(); |
||||
TimeUnit.SECONDS.sleep(masterConfig.getMasterHeartbeatInterval() + 2); //wait heartbeat info write into zk node
|
||||
masterRegistry.unRegistry(); |
||||
String masterPath = zookeeperRegistryCenter.getMasterPath(); |
||||
List<String> childrenKeys = zookeeperRegistryCenter.getRegisterOperator().getChildrenKeys(masterPath); |
||||
Assert.assertTrue(childrenKeys.isEmpty()); |
||||
} |
||||
} |
@ -1,78 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.zk; |
||||
|
||||
import org.apache.dolphinscheduler.common.utils.CollectionUtils; |
||||
import org.apache.dolphinscheduler.common.utils.NetUtils; |
||||
import org.apache.dolphinscheduler.dao.datasource.SpringConnectionFactory; |
||||
import org.apache.dolphinscheduler.server.master.config.MasterConfig; |
||||
import org.apache.dolphinscheduler.server.master.registry.MasterRegistry; |
||||
import org.apache.dolphinscheduler.server.master.registry.ServerNodeManager; |
||||
import org.apache.dolphinscheduler.server.registry.DependencyConfig; |
||||
import org.apache.dolphinscheduler.server.registry.ZookeeperRegistryCenter; |
||||
import org.apache.dolphinscheduler.server.worker.config.WorkerConfig; |
||||
import org.apache.dolphinscheduler.server.zk.SpringZKServer; |
||||
import org.apache.dolphinscheduler.service.zk.RegisterOperator; |
||||
import org.apache.dolphinscheduler.service.zk.ZookeeperCachedOperator; |
||||
import org.apache.dolphinscheduler.service.zk.ZookeeperConfig; |
||||
|
||||
import java.util.Set; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; |
||||
|
||||
/** |
||||
* zookeeper master client test |
||||
*/ |
||||
@RunWith(SpringJUnit4ClassRunner.class) |
||||
@ContextConfiguration(classes = {DependencyConfig.class, SpringZKServer.class, MasterRegistry.class, |
||||
ZookeeperRegistryCenter.class, MasterConfig.class, WorkerConfig.class, SpringConnectionFactory.class, |
||||
ZookeeperCachedOperator.class, ZookeeperConfig.class, ServerNodeManager.class, |
||||
ZKMasterClient.class, RegisterOperator.class}) |
||||
public class ZKMasterClientTest { |
||||
|
||||
@Autowired |
||||
private ZKMasterClient zkMasterClient; |
||||
|
||||
@Autowired |
||||
private ServerNodeManager serverNodeManager; |
||||
|
||||
@Autowired |
||||
private MasterConfig masterConfig; |
||||
|
||||
@Test |
||||
public void testZKMasterClient() { |
||||
zkMasterClient.start(); |
||||
try { |
||||
//let the serverNodeManager catch the registry event
|
||||
Thread.sleep(2000); |
||||
} catch (InterruptedException ignore) { |
||||
//ignore
|
||||
} |
||||
Set<String> masterNodes = serverNodeManager.getMasterNodes(); |
||||
Assert.assertTrue(CollectionUtils.isNotEmpty(masterNodes)); |
||||
Assert.assertEquals(1, masterNodes.size()); |
||||
Assert.assertEquals(NetUtils.getAddr(masterConfig.getListenPort()), masterNodes.iterator().next()); |
||||
zkMasterClient.close(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,102 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.worker.registry; |
||||
|
||||
import static org.mockito.BDDMockito.given; |
||||
|
||||
import org.apache.dolphinscheduler.server.worker.config.WorkerConfig; |
||||
import org.apache.dolphinscheduler.service.registry.RegistryClient; |
||||
|
||||
import java.util.Set; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.google.common.collect.Sets; |
||||
|
||||
/** |
||||
* worker registry test |
||||
*/ |
||||
@RunWith(MockitoJUnitRunner.Silent.class) |
||||
public class WorkerRegistryClientTest { |
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WorkerRegistryClientTest.class); |
||||
|
||||
private static final String TEST_WORKER_GROUP = "test"; |
||||
|
||||
@InjectMocks |
||||
private WorkerRegistryClient workerRegistryClient; |
||||
|
||||
@Mock |
||||
private RegistryClient registryClient; |
||||
|
||||
@Mock |
||||
private WorkerConfig workerConfig; |
||||
|
||||
@Mock |
||||
private Set<String> workerGroups = Sets.newHashSet("127.0.0.1"); |
||||
|
||||
@Mock |
||||
private ScheduledExecutorService heartBeatExecutor; |
||||
|
||||
//private static final Set<String> workerGroups;
|
||||
|
||||
static { |
||||
// workerGroups = Sets.newHashSet(DEFAULT_WORKER_GROUP, TEST_WORKER_GROUP);
|
||||
} |
||||
|
||||
@Before |
||||
public void before() { |
||||
|
||||
given(registryClient.getWorkerPath()).willReturn("/nodes/worker"); |
||||
given(workerConfig.getWorkerGroups()).willReturn(Sets.newHashSet("127.0.0.1")); |
||||
//given(heartBeatExecutor.getWorkerGroups()).willReturn(Sets.newHashSet("127.0.0.1"));
|
||||
//scheduleAtFixedRate
|
||||
given(heartBeatExecutor.scheduleAtFixedRate(Mockito.any(), Mockito.anyLong(), Mockito.anyLong(), Mockito.any(TimeUnit.class))).willReturn(null); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void testRegistry() { |
||||
//workerRegistryClient.initWorkRegistry();
|
||||
// System.out.println(this.workerGroups.iterator());
|
||||
//Set<String> workerGroups = Sets.newHashSet("127.0.0.1");
|
||||
//workerRegistryClient.registry();
|
||||
// workerRegistryClient.handleDeadServer();
|
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void testUnRegistry() { |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void testGetWorkerZkPaths() { |
||||
|
||||
} |
||||
} |
@ -1,185 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.worker.registry; |
||||
|
||||
import static org.apache.dolphinscheduler.common.Constants.DEFAULT_WORKER_GROUP; |
||||
|
||||
import org.apache.dolphinscheduler.common.utils.NetUtils; |
||||
import org.apache.dolphinscheduler.common.utils.StringUtils; |
||||
import org.apache.dolphinscheduler.server.registry.ZookeeperRegistryCenter; |
||||
import org.apache.dolphinscheduler.server.worker.config.WorkerConfig; |
||||
import org.apache.dolphinscheduler.service.zk.RegisterOperator; |
||||
|
||||
import org.apache.curator.framework.imps.CuratorFrameworkImpl; |
||||
import org.apache.curator.framework.listen.Listenable; |
||||
import org.apache.curator.framework.state.ConnectionStateListener; |
||||
|
||||
import java.util.List; |
||||
import java.util.Set; |
||||
import java.util.concurrent.Executor; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.google.common.collect.Sets; |
||||
|
||||
/** |
||||
* worker registry test |
||||
*/ |
||||
@RunWith(MockitoJUnitRunner.Silent.class) |
||||
public class WorkerRegistryTest { |
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WorkerRegistryTest.class); |
||||
|
||||
private static final String TEST_WORKER_GROUP = "test"; |
||||
|
||||
@InjectMocks |
||||
private WorkerRegistry workerRegistry; |
||||
|
||||
@Mock |
||||
private ZookeeperRegistryCenter zookeeperRegistryCenter; |
||||
|
||||
@Mock |
||||
private RegisterOperator registerOperator; |
||||
|
||||
@Mock |
||||
private CuratorFrameworkImpl zkClient; |
||||
|
||||
@Mock |
||||
private WorkerConfig workerConfig; |
||||
|
||||
private static final Set<String> workerGroups; |
||||
|
||||
static { |
||||
workerGroups = Sets.newHashSet(DEFAULT_WORKER_GROUP, TEST_WORKER_GROUP); |
||||
} |
||||
|
||||
@Before |
||||
public void before() { |
||||
|
||||
Mockito.when(workerConfig.getWorkerGroups()).thenReturn(workerGroups); |
||||
|
||||
Mockito.when(zookeeperRegistryCenter.getWorkerPath()).thenReturn("/dolphinscheduler/nodes/worker"); |
||||
Mockito.when(zookeeperRegistryCenter.getRegisterOperator()).thenReturn(registerOperator); |
||||
Mockito.when(zookeeperRegistryCenter.getRegisterOperator().getZkClient()).thenReturn(zkClient); |
||||
Mockito.when(zookeeperRegistryCenter.getRegisterOperator().getZkClient().getConnectionStateListenable()).thenReturn( |
||||
new Listenable<ConnectionStateListener>() { |
||||
@Override |
||||
public void addListener(ConnectionStateListener connectionStateListener) { |
||||
LOGGER.info("add listener"); |
||||
} |
||||
|
||||
@Override |
||||
public void addListener(ConnectionStateListener connectionStateListener, Executor executor) { |
||||
LOGGER.info("add listener executor"); |
||||
} |
||||
|
||||
@Override |
||||
public void removeListener(ConnectionStateListener connectionStateListener) { |
||||
LOGGER.info("remove listener"); |
||||
} |
||||
}); |
||||
|
||||
Mockito.when(workerConfig.getWorkerHeartbeatInterval()).thenReturn(10); |
||||
|
||||
Mockito.when(workerConfig.getWorkerReservedMemory()).thenReturn(1.1); |
||||
|
||||
Mockito.when(workerConfig.getWorkerMaxCpuloadAvg()).thenReturn(1); |
||||
} |
||||
|
||||
@Test |
||||
public void testRegistry() { |
||||
|
||||
workerRegistry.init(); |
||||
|
||||
workerRegistry.registry(); |
||||
|
||||
String workerPath = zookeeperRegistryCenter.getWorkerPath(); |
||||
|
||||
int i = 0; |
||||
for (String workerGroup : workerConfig.getWorkerGroups()) { |
||||
String workerZkPath = workerPath + "/" + workerGroup.trim() + "/" + (NetUtils.getAddr(workerConfig.getListenPort())); |
||||
String heartbeat = zookeeperRegistryCenter.getRegisterOperator().get(workerZkPath); |
||||
if (0 == i) { |
||||
Assert.assertTrue(workerZkPath.startsWith("/dolphinscheduler/nodes/worker/test/")); |
||||
} else { |
||||
Assert.assertTrue(workerZkPath.startsWith("/dolphinscheduler/nodes/worker/default/")); |
||||
} |
||||
i++; |
||||
} |
||||
|
||||
workerRegistry.unRegistry(); |
||||
|
||||
workerConfig.getWorkerGroups().add(StringUtils.EMPTY); |
||||
workerRegistry.init(); |
||||
workerRegistry.registry(); |
||||
|
||||
workerRegistry.unRegistry(); |
||||
|
||||
// testEmptyWorkerGroupsRegistry
|
||||
workerConfig.getWorkerGroups().remove(StringUtils.EMPTY); |
||||
workerConfig.getWorkerGroups().remove(TEST_WORKER_GROUP); |
||||
workerConfig.getWorkerGroups().remove(DEFAULT_WORKER_GROUP); |
||||
workerRegistry.init(); |
||||
workerRegistry.registry(); |
||||
|
||||
List<String> testWorkerGroupPathZkChildren = zookeeperRegistryCenter.getChildrenKeys(workerPath + "/" + TEST_WORKER_GROUP); |
||||
List<String> defaultWorkerGroupPathZkChildren = zookeeperRegistryCenter.getChildrenKeys(workerPath + "/" + DEFAULT_WORKER_GROUP); |
||||
|
||||
Assert.assertEquals(0, testWorkerGroupPathZkChildren.size()); |
||||
Assert.assertEquals(0, defaultWorkerGroupPathZkChildren.size()); |
||||
workerRegistry.unRegistry(); |
||||
} |
||||
|
||||
@Test |
||||
public void testUnRegistry() { |
||||
workerRegistry.init(); |
||||
workerRegistry.registry(); |
||||
|
||||
workerRegistry.unRegistry(); |
||||
String workerPath = zookeeperRegistryCenter.getWorkerPath(); |
||||
|
||||
for (String workerGroup : workerConfig.getWorkerGroups()) { |
||||
String workerGroupPath = workerPath + "/" + workerGroup.trim(); |
||||
List<String> childrenKeys = zookeeperRegistryCenter.getRegisterOperator().getChildrenKeys(workerGroupPath); |
||||
Assert.assertTrue(childrenKeys.isEmpty()); |
||||
} |
||||
|
||||
// testEmptyWorkerGroupsUnRegistry
|
||||
workerConfig.getWorkerGroups().remove(TEST_WORKER_GROUP); |
||||
workerConfig.getWorkerGroups().remove(DEFAULT_WORKER_GROUP); |
||||
workerRegistry.init(); |
||||
workerRegistry.registry(); |
||||
|
||||
workerRegistry.unRegistry(); |
||||
} |
||||
|
||||
@Test |
||||
public void testGetWorkerZkPaths() { |
||||
workerRegistry.init(); |
||||
Assert.assertEquals(workerGroups.size(),workerRegistry.getWorkerZkPaths().size()); |
||||
} |
||||
} |
@ -1,178 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.apache.dolphinscheduler.server.zk; |
||||
|
||||
import org.apache.curator.framework.CuratorFramework; |
||||
import org.apache.curator.framework.CuratorFrameworkFactory; |
||||
import org.apache.curator.retry.ExponentialBackoffRetry; |
||||
import org.apache.zookeeper.server.ZooKeeperServerMain; |
||||
import org.apache.zookeeper.server.quorum.QuorumPeerConfig; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.core.PriorityOrdered; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import javax.annotation.PreDestroy; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.util.concurrent.TimeUnit; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
|
||||
/** |
||||
* just for test |
||||
*/ |
||||
@Service |
||||
public class SpringZKServer implements PriorityOrdered { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SpringZKServer.class); |
||||
|
||||
private static volatile PublicZooKeeperServerMain zkServer = null; |
||||
|
||||
public static final int DEFAULT_ZK_TEST_PORT = 2181; |
||||
|
||||
public static final String DEFAULT_ZK_STR = "localhost:" + DEFAULT_ZK_TEST_PORT; |
||||
|
||||
private static String dataDir = null; |
||||
|
||||
private static final AtomicBoolean isStarted = new AtomicBoolean(false); |
||||
|
||||
@PostConstruct |
||||
public void start() { |
||||
try { |
||||
startLocalZkServer(DEFAULT_ZK_TEST_PORT); |
||||
} catch (Exception e) { |
||||
logger.error("Failed to start ZK: " + e); |
||||
} |
||||
} |
||||
|
||||
public static boolean isStarted(){ |
||||
return isStarted.get(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return PriorityOrdered.HIGHEST_PRECEDENCE; |
||||
} |
||||
|
||||
static class PublicZooKeeperServerMain extends ZooKeeperServerMain { |
||||
|
||||
@Override |
||||
public void initializeAndRun(String[] args) |
||||
throws QuorumPeerConfig.ConfigException, IOException { |
||||
super.initializeAndRun(args); |
||||
} |
||||
|
||||
@Override |
||||
public void shutdown() { |
||||
super.shutdown(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Starts a local Zk instance with a generated empty data directory |
||||
* |
||||
* @param port The port to listen on |
||||
*/ |
||||
public void startLocalZkServer(final int port) { |
||||
startLocalZkServer(port, org.apache.commons.io.FileUtils.getTempDirectoryPath() + File.separator + "test-" + System.currentTimeMillis()); |
||||
} |
||||
|
||||
/** |
||||
* Starts a local Zk instance |
||||
* |
||||
* @param port The port to listen on |
||||
* @param dataDirPath The path for the Zk data directory |
||||
*/ |
||||
private void startLocalZkServer(final int port, final String dataDirPath) { |
||||
if (zkServer != null) { |
||||
throw new RuntimeException("Zookeeper server is already started!"); |
||||
} |
||||
try { |
||||
zkServer = new PublicZooKeeperServerMain(); |
||||
logger.info("Zookeeper data path : {} ", dataDirPath); |
||||
dataDir = dataDirPath; |
||||
final String[] args = new String[]{Integer.toString(port), dataDirPath}; |
||||
Thread init = new Thread(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
try { |
||||
System.setProperty("zookeeper.jmx.log4j.disable", "true"); |
||||
zkServer.initializeAndRun(args); |
||||
} catch (QuorumPeerConfig.ConfigException e) { |
||||
logger.warn("Caught exception while starting ZK", e); |
||||
} catch (IOException e) { |
||||
logger.warn("Caught exception while starting ZK", e); |
||||
} |
||||
} |
||||
}, "init-zk-thread"); |
||||
init.start(); |
||||
} catch (Exception e) { |
||||
logger.warn("Caught exception while starting ZK", e); |
||||
throw new RuntimeException(e); |
||||
} |
||||
|
||||
CuratorFramework zkClient = CuratorFrameworkFactory.builder() |
||||
.connectString(DEFAULT_ZK_STR) |
||||
.retryPolicy(new ExponentialBackoffRetry(10,100)) |
||||
.sessionTimeoutMs(1000 * 30) |
||||
.connectionTimeoutMs(1000 * 30) |
||||
.build(); |
||||
|
||||
try { |
||||
zkClient.blockUntilConnected(10, TimeUnit.SECONDS); |
||||
zkClient.close(); |
||||
} catch (InterruptedException ignore) { |
||||
} |
||||
isStarted.compareAndSet(false, true); |
||||
logger.info("zk server started"); |
||||
} |
||||
|
||||
@PreDestroy |
||||
public void stop() { |
||||
try { |
||||
stopLocalZkServer(true); |
||||
logger.info("zk server stopped"); |
||||
|
||||
} catch (Exception e) { |
||||
logger.error("Failed to stop ZK ",e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Stops a local Zk instance. |
||||
* |
||||
* @param deleteDataDir Whether or not to delete the data directory |
||||
*/ |
||||
private void stopLocalZkServer(final boolean deleteDataDir) { |
||||
if (zkServer != null) { |
||||
try { |
||||
zkServer.shutdown(); |
||||
zkServer = null; |
||||
if (deleteDataDir) { |
||||
org.apache.commons.io.FileUtils.deleteDirectory(new File(dataDir)); |
||||
} |
||||
isStarted.compareAndSet(true, false); |
||||
} catch (Exception e) { |
||||
logger.warn("Caught exception while stopping ZK server", e); |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,269 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.service.registry; |
||||
|
||||
import static org.apache.dolphinscheduler.common.Constants.REGISTRY_DOLPHINSCHEDULER_DEAD_SERVERS; |
||||
|
||||
import org.apache.dolphinscheduler.common.IStoppable; |
||||
import org.apache.dolphinscheduler.common.utils.PropertyUtils; |
||||
import org.apache.dolphinscheduler.common.utils.StringUtils; |
||||
import org.apache.dolphinscheduler.spi.plugin.DolphinPluginLoader; |
||||
import org.apache.dolphinscheduler.spi.plugin.DolphinPluginManagerConfig; |
||||
import org.apache.dolphinscheduler.spi.register.Registry; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryConnectListener; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryException; |
||||
import org.apache.dolphinscheduler.spi.register.RegistryPluginManager; |
||||
import org.apache.dolphinscheduler.spi.register.SubscribeListener; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
|
||||
/** |
||||
* All business parties use this class to access the registry |
||||
*/ |
||||
public class RegistryCenter { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RegistryCenter.class); |
||||
|
||||
private final AtomicBoolean isStarted = new AtomicBoolean(false); |
||||
|
||||
private Registry registry; |
||||
|
||||
private IStoppable stoppable; |
||||
|
||||
/** |
||||
* nodes namespace |
||||
*/ |
||||
protected static String NODES; |
||||
|
||||
/** |
||||
* master path |
||||
*/ |
||||
protected static String MASTER_PATH = "/nodes/master"; |
||||
|
||||
private RegistryPluginManager registryPluginManager; |
||||
/** |
||||
* worker path |
||||
*/ |
||||
protected static String WORKER_PATH = "/nodes/worker"; |
||||
|
||||
protected static final String EMPTY = ""; |
||||
|
||||
private static final String REGISTRY_PREFIX = "registry"; |
||||
|
||||
private static final String REGISTRY_PLUGIN_BINDING = "registry.plugin.binding"; |
||||
|
||||
private static final String REGISTRY_PLUGIN_DIR = "registry.plugin.dir"; |
||||
|
||||
private static final String MAVEN_LOCAL_REPOSITORY = "maven.local.repository"; |
||||
|
||||
private static final String REGISTRY_PLUGIN_NAME = "plugin.name"; |
||||
|
||||
/** |
||||
* default registry plugin dir |
||||
*/ |
||||
private static final String REGISTRY_PLUGIN_PATH = "lib/plugin/registry"; |
||||
|
||||
private static final String REGISTRY_CONFIG_FILE_PATH = "/registry.properties"; |
||||
|
||||
/** |
||||
* init node persist |
||||
*/ |
||||
public void init() { |
||||
if (isStarted.compareAndSet(false, true)) { |
||||
PropertyUtils.loadPropertyFile(REGISTRY_CONFIG_FILE_PATH); |
||||
Map<String, String> registryConfig = PropertyUtils.getPropertiesByPrefix(REGISTRY_PREFIX); |
||||
|
||||
if (null == registryConfig || registryConfig.isEmpty()) { |
||||
throw new RegistryException("registry config param is null"); |
||||
} |
||||
if (null == registryPluginManager) { |
||||
installRegistryPlugin(registryConfig.get(REGISTRY_PLUGIN_NAME)); |
||||
registry = registryPluginManager.getRegistry(); |
||||
} |
||||
|
||||
registry.init(registryConfig); |
||||
initNodes(); |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* init nodes |
||||
*/ |
||||
private void initNodes() { |
||||
persist(MASTER_PATH, EMPTY); |
||||
persist(WORKER_PATH, EMPTY); |
||||
} |
||||
|
||||
/** |
||||
* install registry plugin |
||||
*/ |
||||
private void installRegistryPlugin(String registryPluginName) { |
||||
DolphinPluginManagerConfig registryPluginManagerConfig = new DolphinPluginManagerConfig(); |
||||
registryPluginManagerConfig.setPlugins(PropertyUtils.getString(REGISTRY_PLUGIN_BINDING)); |
||||
if (StringUtils.isNotBlank(PropertyUtils.getString(REGISTRY_PLUGIN_DIR))) { |
||||
registryPluginManagerConfig.setPlugins(PropertyUtils.getString(REGISTRY_PLUGIN_DIR, REGISTRY_PLUGIN_PATH).trim()); |
||||
} |
||||
|
||||
if (StringUtils.isNotBlank(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY))) { |
||||
registryPluginManagerConfig.setMavenLocalRepository(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY).trim()); |
||||
} |
||||
if (StringUtils.isNotBlank(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY))) { |
||||
registryPluginManagerConfig.setMavenLocalRepository(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY).trim()); |
||||
} |
||||
|
||||
registryPluginManager = new RegistryPluginManager(registryPluginName); |
||||
|
||||
DolphinPluginLoader registryPluginLoader = new DolphinPluginLoader(registryPluginManagerConfig, ImmutableList.of(registryPluginManager)); |
||||
try { |
||||
registryPluginLoader.loadPlugins(); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException("Load registry Plugin Failed !", e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* close |
||||
*/ |
||||
public void close() { |
||||
if (isStarted.compareAndSet(true, false) && registry != null) { |
||||
registry.close(); |
||||
} |
||||
} |
||||
|
||||
public void persist(String key, String value) { |
||||
registry.persist(key, value); |
||||
} |
||||
|
||||
public void persistEphemeral(String key, String value) { |
||||
registry.persistEphemeral(key, value); |
||||
} |
||||
|
||||
public void remove(String key) { |
||||
registry.remove(key); |
||||
} |
||||
|
||||
public void update(String key, String value) { |
||||
registry.update(key, value); |
||||
} |
||||
|
||||
public String get(String key) { |
||||
return registry.get(key); |
||||
} |
||||
|
||||
public void subscribe(String path, SubscribeListener subscribeListener) { |
||||
registry.subscribe(path, subscribeListener); |
||||
} |
||||
|
||||
public void addConnectionStateListener(RegistryConnectListener registryConnectListener) { |
||||
registry.addConnectionStateListener(registryConnectListener); |
||||
} |
||||
|
||||
public boolean isExisted(String key) { |
||||
return registry.isExisted(key); |
||||
} |
||||
|
||||
public boolean getLock(String key) { |
||||
return registry.acquireLock(key); |
||||
} |
||||
|
||||
public boolean releaseLock(String key) { |
||||
return registry.releaseLock(key); |
||||
} |
||||
|
||||
/** |
||||
* @return get dead server node parent path |
||||
*/ |
||||
public String getDeadZNodeParentPath() { |
||||
return REGISTRY_DOLPHINSCHEDULER_DEAD_SERVERS; |
||||
} |
||||
|
||||
public void setStoppable(IStoppable stoppable) { |
||||
this.stoppable = stoppable; |
||||
} |
||||
|
||||
public IStoppable getStoppable() { |
||||
return stoppable; |
||||
} |
||||
|
||||
/** |
||||
* get master path |
||||
* |
||||
* @return master path |
||||
*/ |
||||
public String getMasterPath() { |
||||
return MASTER_PATH; |
||||
} |
||||
|
||||
/** |
||||
* whether master path |
||||
* |
||||
* @param path path |
||||
* @return result |
||||
*/ |
||||
public boolean isMasterPath(String path) { |
||||
return path != null && path.contains(MASTER_PATH); |
||||
} |
||||
|
||||
/** |
||||
* get worker path |
||||
* |
||||
* @return worker path |
||||
*/ |
||||
public String getWorkerPath() { |
||||
return WORKER_PATH; |
||||
} |
||||
|
||||
/** |
||||
* get worker group path |
||||
* |
||||
* @param workerGroup workerGroup |
||||
* @return worker group path |
||||
*/ |
||||
public String getWorkerGroupPath(String workerGroup) { |
||||
return WORKER_PATH + "/" + workerGroup; |
||||
} |
||||
|
||||
/** |
||||
* whether worker path |
||||
* |
||||
* @param path path |
||||
* @return result |
||||
*/ |
||||
public boolean isWorkerPath(String path) { |
||||
return path != null && path.contains(WORKER_PATH); |
||||
} |
||||
|
||||
/** |
||||
* get children nodes |
||||
* |
||||
* @param key key |
||||
* @return children nodes |
||||
*/ |
||||
public List<String> getChildrenKeys(final String key) { |
||||
return registry.getChildren(key); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,448 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.service.registry; |
||||
|
||||
import static org.apache.dolphinscheduler.common.Constants.ADD_OP; |
||||
import static org.apache.dolphinscheduler.common.Constants.COLON; |
||||
import static org.apache.dolphinscheduler.common.Constants.DELETE_OP; |
||||
import static org.apache.dolphinscheduler.common.Constants.DIVISION_STRING; |
||||
import static org.apache.dolphinscheduler.common.Constants.MASTER_TYPE; |
||||
import static org.apache.dolphinscheduler.common.Constants.SINGLE_SLASH; |
||||
import static org.apache.dolphinscheduler.common.Constants.UNDERLINE; |
||||
import static org.apache.dolphinscheduler.common.Constants.WORKER_TYPE; |
||||
|
||||
import org.apache.dolphinscheduler.common.Constants; |
||||
import org.apache.dolphinscheduler.common.enums.NodeType; |
||||
import org.apache.dolphinscheduler.common.model.Server; |
||||
import org.apache.dolphinscheduler.common.utils.ResInfo; |
||||
import org.apache.dolphinscheduler.common.utils.StringUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* abstract registry client |
||||
*/ |
||||
@Service |
||||
public class RegistryClient extends RegistryCenter { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RegistryClient.class); |
||||
|
||||
private void loadRegistry() { |
||||
init(); |
||||
} |
||||
|
||||
/** |
||||
* get active master num |
||||
* |
||||
* @return active master number |
||||
*/ |
||||
public int getActiveMasterNum() { |
||||
List<String> childrenList = new ArrayList<>(); |
||||
try { |
||||
// read master node parent path from conf
|
||||
if (isExisted(getNodeParentPath(NodeType.MASTER))) { |
||||
childrenList = getChildrenKeys(getNodeParentPath(NodeType.MASTER)); |
||||
} |
||||
} catch (Exception e) { |
||||
logger.error("getActiveMasterNum error", e); |
||||
} |
||||
return childrenList.size(); |
||||
} |
||||
|
||||
/** |
||||
* get server list. |
||||
* |
||||
* @param nodeType zookeeper node type |
||||
* @return server list |
||||
*/ |
||||
public List<Server> getServerList(NodeType nodeType) { |
||||
Map<String, String> serverMaps = getServerMaps(nodeType); |
||||
String parentPath = getNodeParentPath(nodeType); |
||||
|
||||
List<Server> serverList = new ArrayList<>(); |
||||
for (Map.Entry<String, String> entry : serverMaps.entrySet()) { |
||||
Server server = ResInfo.parseHeartbeatForRegistryInfo(entry.getValue()); |
||||
if (server == null) { |
||||
continue; |
||||
} |
||||
String key = entry.getKey(); |
||||
server.setZkDirectory(parentPath + "/" + key); |
||||
// set host and port
|
||||
String[] hostAndPort = key.split(COLON); |
||||
String[] hosts = hostAndPort[0].split(DIVISION_STRING); |
||||
// fetch the last one
|
||||
server.setHost(hosts[hosts.length - 1]); |
||||
server.setPort(Integer.parseInt(hostAndPort[1])); |
||||
serverList.add(server); |
||||
} |
||||
return serverList; |
||||
} |
||||
|
||||
/** |
||||
* get server nodes. |
||||
* |
||||
* @param nodeType registry node type |
||||
* @return result : list<node> |
||||
*/ |
||||
public List<String> getServerNodes(NodeType nodeType) { |
||||
String path = getNodeParentPath(nodeType); |
||||
List<String> serverList = getChildrenKeys(path); |
||||
if (nodeType == NodeType.WORKER) { |
||||
List<String> workerList = new ArrayList<>(); |
||||
for (String group : serverList) { |
||||
List<String> groupServers = getChildrenKeys(path + Constants.SLASH + group); |
||||
for (String groupServer : groupServers) { |
||||
workerList.add(group + Constants.SLASH + groupServer); |
||||
} |
||||
} |
||||
serverList = workerList; |
||||
} |
||||
return serverList; |
||||
} |
||||
|
||||
/** |
||||
* get server list map. |
||||
* |
||||
* @param nodeType zookeeper node type |
||||
* @param hostOnly host only |
||||
* @return result : {host : resource info} |
||||
*/ |
||||
public Map<String, String> getServerMaps(NodeType nodeType, boolean hostOnly) { |
||||
Map<String, String> serverMap = new HashMap<>(); |
||||
try { |
||||
String path = getNodeParentPath(nodeType); |
||||
List<String> serverList = getServerNodes(nodeType); |
||||
for (String server : serverList) { |
||||
String host = server; |
||||
if (nodeType == NodeType.WORKER && hostOnly) { |
||||
host = server.split(Constants.SLASH)[1]; |
||||
} |
||||
serverMap.putIfAbsent(host, get(path + Constants.SLASH + server)); |
||||
} |
||||
} catch (Exception e) { |
||||
logger.error("get server list failed", e); |
||||
} |
||||
|
||||
return serverMap; |
||||
} |
||||
|
||||
/** |
||||
* get server list map. |
||||
* |
||||
* @param nodeType zookeeper node type |
||||
* @return result : {host : resource info} |
||||
*/ |
||||
public Map<String, String> getServerMaps(NodeType nodeType) { |
||||
return getServerMaps(nodeType, false); |
||||
} |
||||
|
||||
/** |
||||
* get server node set. |
||||
* |
||||
* @param nodeType zookeeper node type |
||||
* @param hostOnly host only |
||||
* @return result : set<host> |
||||
*/ |
||||
public Set<String> getServerNodeSet(NodeType nodeType, boolean hostOnly) { |
||||
Set<String> serverSet = new HashSet<>(); |
||||
try { |
||||
List<String> serverList = getServerNodes(nodeType); |
||||
for (String server : serverList) { |
||||
String host = server; |
||||
if (nodeType == NodeType.WORKER && hostOnly) { |
||||
host = server.split(Constants.SLASH)[1]; |
||||
} |
||||
serverSet.add(host); |
||||
} |
||||
} catch (Exception e) { |
||||
logger.error("get server node set failed", e); |
||||
} |
||||
return serverSet; |
||||
} |
||||
|
||||
/** |
||||
* get server node list. |
||||
* |
||||
* @param nodeType zookeeper node type |
||||
* @param hostOnly host only |
||||
* @return result : list<host> |
||||
*/ |
||||
public List<String> getServerNodeList(NodeType nodeType, boolean hostOnly) { |
||||
Set<String> serverSet = getServerNodeSet(nodeType, hostOnly); |
||||
List<String> serverList = new ArrayList<>(serverSet); |
||||
Collections.sort(serverList); |
||||
return serverList; |
||||
} |
||||
|
||||
/** |
||||
* check the zookeeper node already exists |
||||
* |
||||
* @param host host |
||||
* @param nodeType zookeeper node type |
||||
* @return true if exists |
||||
*/ |
||||
public boolean checkNodeExists(String host, NodeType nodeType) { |
||||
String path = getNodeParentPath(nodeType); |
||||
if (StringUtils.isEmpty(path)) { |
||||
logger.error("check zk node exists error, host:{}, zk node type:{}", |
||||
host, nodeType); |
||||
return false; |
||||
} |
||||
Map<String, String> serverMaps = getServerMaps(nodeType, true); |
||||
for (String hostKey : serverMaps.keySet()) { |
||||
if (hostKey.contains(host)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* @return get worker node parent path |
||||
*/ |
||||
protected String getWorkerNodeParentPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_WORKERS; |
||||
} |
||||
|
||||
/** |
||||
* @return get master node parent path |
||||
*/ |
||||
protected String getMasterNodeParentPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_MASTERS; |
||||
} |
||||
|
||||
/** |
||||
* @return get dead server node parent path |
||||
*/ |
||||
protected String getDeadNodeParentPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_DEAD_SERVERS; |
||||
} |
||||
|
||||
/** |
||||
* @return get master lock path |
||||
*/ |
||||
public String getMasterLockPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_LOCK_MASTERS; |
||||
} |
||||
|
||||
/** |
||||
* @param nodeType zookeeper node type |
||||
* @return get zookeeper node parent path |
||||
*/ |
||||
public String getNodeParentPath(NodeType nodeType) { |
||||
String path = ""; |
||||
switch (nodeType) { |
||||
case MASTER: |
||||
return getMasterNodeParentPath(); |
||||
case WORKER: |
||||
return getWorkerNodeParentPath(); |
||||
case DEAD_SERVER: |
||||
return getDeadNodeParentPath(); |
||||
default: |
||||
break; |
||||
} |
||||
return path; |
||||
} |
||||
|
||||
/** |
||||
* @return get master start up lock path |
||||
*/ |
||||
public String getMasterStartUpLockPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_LOCK_FAILOVER_STARTUP_MASTERS; |
||||
} |
||||
|
||||
/** |
||||
* @return get master failover lock path |
||||
*/ |
||||
public String getMasterFailoverLockPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_LOCK_FAILOVER_MASTERS; |
||||
} |
||||
|
||||
/** |
||||
* @return get worker failover lock path |
||||
*/ |
||||
public String getWorkerFailoverLockPath() { |
||||
return Constants.REGISTRY_DOLPHINSCHEDULER_LOCK_FAILOVER_WORKERS; |
||||
} |
||||
|
||||
/** |
||||
* opType(add): if find dead server , then add to zk deadServerPath |
||||
* opType(delete): delete path from zk |
||||
* |
||||
* @param node node path |
||||
* @param nodeType master or worker |
||||
* @param opType delete or add |
||||
* @throws Exception errors |
||||
*/ |
||||
public void handleDeadServer(String node, NodeType nodeType, String opType) throws Exception { |
||||
String host = getHostByEventDataPath(node); |
||||
String type = (nodeType == NodeType.MASTER) ? MASTER_TYPE : WORKER_TYPE; |
||||
|
||||
//check server restart, if restart , dead server path in zk should be delete
|
||||
if (opType.equals(DELETE_OP)) { |
||||
removeDeadServerByHost(host, type); |
||||
|
||||
} else if (opType.equals(ADD_OP)) { |
||||
String deadServerPath = getDeadZNodeParentPath() + SINGLE_SLASH + type + UNDERLINE + host; |
||||
if (!isExisted(deadServerPath)) { |
||||
//add dead server info to zk dead server path : /dead-servers/
|
||||
|
||||
persist(deadServerPath, (type + UNDERLINE + host)); |
||||
|
||||
logger.info("{} server dead , and {} added to zk dead server path success", |
||||
nodeType, node); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* check dead server or not , if dead, stop self |
||||
* |
||||
* @param node node path |
||||
* @param serverType master or worker prefix |
||||
* @return true if not exists |
||||
* @throws Exception errors |
||||
*/ |
||||
public boolean checkIsDeadServer(String node, String serverType) throws Exception { |
||||
// ip_sequence_no
|
||||
String[] zNodesPath = node.split("\\/"); |
||||
String ipSeqNo = zNodesPath[zNodesPath.length - 1]; |
||||
String deadServerPath = getDeadZNodeParentPath() + SINGLE_SLASH + serverType + UNDERLINE + ipSeqNo; |
||||
|
||||
return !isExisted(node) || isExisted(deadServerPath); |
||||
} |
||||
|
||||
/** |
||||
* get master nodes directly |
||||
* |
||||
* @return master nodes |
||||
*/ |
||||
public Set<String> getMasterNodesDirectly() { |
||||
List<String> masters = getChildrenKeys(MASTER_PATH); |
||||
return new HashSet<>(masters); |
||||
} |
||||
|
||||
/** |
||||
* get worker nodes directly |
||||
* |
||||
* @return master nodes |
||||
*/ |
||||
public Set<String> getWorkerNodesDirectly() { |
||||
List<String> workers = getChildrenKeys(WORKER_PATH); |
||||
return new HashSet<>(workers); |
||||
} |
||||
|
||||
/** |
||||
* get worker group directly |
||||
* |
||||
* @return worker group nodes |
||||
*/ |
||||
public Set<String> getWorkerGroupDirectly() { |
||||
List<String> workers = getChildrenKeys(getWorkerPath()); |
||||
return new HashSet<>(workers); |
||||
} |
||||
|
||||
/** |
||||
* get worker group nodes |
||||
*/ |
||||
public Set<String> getWorkerGroupNodesDirectly(String workerGroup) { |
||||
List<String> workers = getChildrenKeys(getWorkerGroupPath(workerGroup)); |
||||
return new HashSet<>(workers); |
||||
} |
||||
|
||||
/** |
||||
* opType(add): if find dead server , then add to zk deadServerPath |
||||
* opType(delete): delete path from zk |
||||
* |
||||
* @param nodeSet node path set |
||||
* @param nodeType master or worker |
||||
* @param opType delete or add |
||||
* @throws Exception errors |
||||
*/ |
||||
public void handleDeadServer(Set<String> nodeSet, NodeType nodeType, String opType) throws Exception { |
||||
|
||||
String type = (nodeType == NodeType.MASTER) ? MASTER_TYPE : WORKER_TYPE; |
||||
for (String node : nodeSet) { |
||||
String host = getHostByEventDataPath(node); |
||||
//check server restart, if restart , dead server path in zk should be delete
|
||||
if (opType.equals(DELETE_OP)) { |
||||
removeDeadServerByHost(host, type); |
||||
|
||||
} else if (opType.equals(ADD_OP)) { |
||||
String deadServerPath = getDeadZNodeParentPath() + SINGLE_SLASH + type + UNDERLINE + host; |
||||
if (!isExisted(deadServerPath)) { |
||||
//add dead server info to zk dead server path : /dead-servers/
|
||||
persist(deadServerPath, (type + UNDERLINE + host)); |
||||
logger.info("{} server dead , and {} added to registry dead server path success", |
||||
nodeType, node); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* get host ip:port, string format: parentPath/ip:port |
||||
* |
||||
* @param path path |
||||
* @return host ip:port, string format: parentPath/ip:port |
||||
*/ |
||||
public String getHostByEventDataPath(String path) { |
||||
if (StringUtils.isEmpty(path)) { |
||||
logger.error("empty path!"); |
||||
return ""; |
||||
} |
||||
String[] pathArray = path.split(SINGLE_SLASH); |
||||
if (pathArray.length < 1) { |
||||
logger.error("parse ip error: {}", path); |
||||
return ""; |
||||
} |
||||
return pathArray[pathArray.length - 1]; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* remove dead server by host |
||||
* |
||||
* @param host host |
||||
* @param serverType serverType |
||||
*/ |
||||
public void removeDeadServerByHost(String host, String serverType) { |
||||
List<String> deadServers = getChildrenKeys(getDeadZNodeParentPath()); |
||||
for (String serverPath : deadServers) { |
||||
if (serverPath.startsWith(serverType + UNDERLINE + host)) { |
||||
String server = getDeadZNodeParentPath() + SINGLE_SLASH + serverPath; |
||||
remove(server); |
||||
logger.info("{} server {} deleted from zk dead server path success", serverType, host); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue