/**
* Copyright 2016 vip.com.
* <p>
* Licensed 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.
* </p>
*/
package com.vip.saturn.job.console.service.impl;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import com.vip.saturn.job.console.utils.ExecutorNodePath;
import com.vip.saturn.job.integrate.service.ReportAlarmService;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.vip.saturn.job.console.SaturnEnvProperties;
import com.vip.saturn.job.console.domain.RegistryCenterClient;
import com.vip.saturn.job.console.domain.RegistryCenterConfiguration;
import com.vip.saturn.job.console.domain.RequestResult;
import com.vip.saturn.job.console.domain.ZkCluster;
import com.vip.saturn.job.console.repository.zookeeper.CuratorRepository;
import com.vip.saturn.job.console.service.InitRegistryCenterService;
import com.vip.saturn.job.console.service.RegistryCenterService;
import com.vip.saturn.job.console.utils.LocalHostService;
import com.vip.saturn.job.sharding.NamespaceShardingManager;
@Service
public class RegistryCenterServiceImpl implements RegistryCenterService {
protected static Logger log = LoggerFactory.getLogger(RegistryCenterServiceImpl.class);
@Resource
private CuratorRepository curatorRepository;
@Resource
private ReportAlarmService reportAlarmService;
public static ConcurrentHashMap<String /** nns */, RegistryCenterClient> NNS_CURATOR_CLIENT_MAP = new ConcurrentHashMap<>();
/** 为保证values有序 **/
public static LinkedHashMap<String/** zkAddr **/, ZkCluster> ZKADDR_TO_ZKCLUSTER_MAP = new LinkedHashMap<>();
private final AtomicBoolean refreshingRegCenter = new AtomicBoolean(false);
private ConcurrentHashMap<String /** nns **/, NamespaceShardingManager> namespaceShardingListenerManagerMap = new ConcurrentHashMap<String, NamespaceShardingManager>();
@PostConstruct
public void init() throws Exception {
refreshAll();
}
private String generateShardingLeadershipHostValue() {
return LocalHostService.cachedIpAddress + "-" + UUID.randomUUID().toString();
}
private void refreshNamespaceShardingListenerManagerMap() {
Collection<ZkCluster> zkClusters = RegistryCenterServiceImpl.ZKADDR_TO_ZKCLUSTER_MAP.values();
for (ZkCluster zkCluster: zkClusters) {
for(RegistryCenterConfiguration conf: zkCluster.getRegCenterConfList()) {
String nns = conf.getNameAndNamespace();
if(!namespaceShardingListenerManagerMap.containsKey(nns)) {
// client 从缓存取,不再新建也就不需要关闭
try {
CuratorFramework client = connect(conf.getNameAndNamespace()).getCuratorClient();
NamespaceShardingManager newObj = new NamespaceShardingManager(client, conf.getNamespace(), generateShardingLeadershipHostValue(), reportAlarmService);
if (namespaceShardingListenerManagerMap.putIfAbsent(nns, newObj) == null) {
log.info("start NamespaceShardingManager {}", nns);
newObj.start();
log.info("done starting NamespaceShardingManager {}", nns);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
}
// 关闭无用的
Iterator<Entry<String, NamespaceShardingManager>> iterator = namespaceShardingListenerManagerMap.entrySet().iterator();
while(iterator.hasNext()) {
Entry<String, NamespaceShardingManager> next = iterator.next();
String nns = next.getKey();
NamespaceShardingManager namespaceShardingManager = next.getValue();
boolean find = false;
for (ZkCluster zkCluster: zkClusters) {
for(RegistryCenterConfiguration conf: zkCluster.getRegCenterConfList()) {
if(conf.getNameAndNamespace().equals(nns)) {
find = true;
break;
}
}
if(find) {
break;
}
}
// 防止zk抖动,不stop不移除
/*if(!find) {
namespaceShardingManager.stop();
iterator.remove();
// clear NNS_CURATOR_CLIENT_MAP
RegistryCenterClient registryCenterClient = NNS_CURATOR_CLIENT_MAP.remove(nns);
if (registryCenterClient != null) {
log.info("close zk client in NNS_CURATOR_CLIENT_MAP, nns: {}");
CloseableUtils.closeQuietly(registryCenterClient.getCuratorClient());
}
}*/
}
}
private void refreshRegistryCenterFromJsonFile() throws IOException {
ArrayList<RegistryCenterConfiguration> list = new ArrayList<>();
String json = FileUtils.readFileToString(new File(SaturnEnvProperties.REG_CENTER_JSON_FILE), StandardCharsets.UTF_8);
list = (ArrayList<RegistryCenterConfiguration>) JSON.parseArray(json, RegistryCenterConfiguration.class);
LinkedHashMap<String/** zkAddr **/, ZkCluster> newClusterMap = new LinkedHashMap<>();
for (RegistryCenterConfiguration conf: list) {
try {
conf.initNameAndNamespace(conf.getNameAndNamespace());
if (conf.getZkAlias() == null) {
conf.setZkAlias(conf.getZkAddressList());
}
if (conf.getBootstrapKey() == null) {
conf.setBootstrapKey(conf.getZkAddressList());
}
ZkCluster cluster = newClusterMap.get(conf.getZkAddressList());
if (cluster == null) {
CuratorFramework curatorFramework = curatorRepository.connect(conf.getZkAddressList(), "", conf.getDigest());
cluster = new ZkCluster(conf.getZkAlias(), conf.getZkAddressList(), curatorFramework);
newClusterMap.put(conf.getZkAddressList(), cluster);
} else if (cluster.getCuratorFramework() == null) {
if (cluster.getCuratorFramework() !=null && !cluster.getCuratorFramework().getZookeeperClient().isConnected()) {
cluster.getCuratorFramework().close();
}
CuratorFramework curatorFramework = curatorRepository.connect(conf.getZkAddressList(), "", conf.getDigest());
cluster.setCuratorFramework(curatorFramework);
}
if (cluster.getCuratorFramework() == null) {
throw new IllegalArgumentException();
}
cluster.setOffline(false);
conf.setVersion(getVersion(conf.getNamespace(), cluster.getCuratorFramework()));
cluster.getRegCenterConfList().add(conf);
} catch (Exception e) {
log.error("found an offline zkCluster: {}", conf);
log.error(e.getMessage(), e);
ZkCluster cluster = new ZkCluster(conf.getZkAlias(), conf.getZkAddressList(), null);
cluster.setOffline(true);
newClusterMap.put(conf.getZkAddressList(), cluster);
}
}
shutdownZkClientInZkClusterMap();
ZKADDR_TO_ZKCLUSTER_MAP = newClusterMap;
}
private String getVersion(String namespace, CuratorFramework curatorFramework) {
try {
List<String> versionList = new ArrayList<>();
String executorsPath = "/" + namespace + ExecutorNodePath.getExecutorNodePath();
if (curatorFramework.checkExists().forPath(executorsPath) != null) {
List<String> executors = curatorFramework.getChildren().forPath(executorsPath);
if (executors != null && !executors.isEmpty()) {
for (String exe : executors) {
String versionPath = executorsPath + "/" + exe + "/version";
if (curatorFramework.checkExists().forPath(versionPath) != null) {
byte[] bs = curatorFramework.getData().forPath(versionPath);
if (bs != null) {
String version = new String(bs, "UTF-8");
if (version != null && !version.trim().isEmpty()) {
String tmp = version.trim();
if (!versionList.contains(tmp)) {
versionList.add(tmp);
}
}
}
}
}
}
}
Collections.sort(versionList);
String versionStr = "";
for (int i = 0; i < versionList.size(); i++) {
String version = versionList.get(i);
versionStr = versionStr + version;
if (i < versionList.size() - 1) {
versionStr = versionStr + ", ";
}
}
return versionStr;
} catch (Exception e) {
log.error(e.getMessage(), e);
return "";
}
}
private static void shutdownZkClientInZkClusterMap() {
Collection<ZkCluster> zkClusters = ZKADDR_TO_ZKCLUSTER_MAP.values();
for (ZkCluster zkCluster : zkClusters) {
if (zkCluster.getCuratorFramework() != null) {
try {
log.info("shutdown zkclient in ZK_CLUSTER_MAP: {}", zkCluster);
zkCluster.getCuratorFramework().close();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
}
private void refreshTreeData() {
Collection<ZkCluster> zkClusters = RegistryCenterServiceImpl.ZKADDR_TO_ZKCLUSTER_MAP.values();
for (ZkCluster zkCluster : zkClusters) {
InitRegistryCenterService.initTreeJson(zkCluster.getRegCenterConfList(), zkCluster.getZkAddr());
}
}
@Override
public RegistryCenterClient connect(final String nameAndNameSpace) {
RegistryCenterClient clientInCache = findInCache(nameAndNameSpace);
if (null != clientInCache) {
return clientInCache;
}
RegistryCenterConfiguration toBeConnectedConfig = findConfig(nameAndNameSpace);
CuratorFramework client = curatorRepository.connect(toBeConnectedConfig.getZkAddressList(),
toBeConnectedConfig.getNamespace(), toBeConnectedConfig.getDigest());
RegistryCenterClient result = new RegistryCenterClient(nameAndNameSpace);
if (null == client) {
return result;
}
setRegistryCenterClient(result, client);
return result;
}
@Override
public RegistryCenterClient connectByNamespace(String namespace) {
RegistryCenterClient result = new RegistryCenterClient();
RegistryCenterConfiguration registryCenterConfiguration = findConfigByNamespace(namespace);
if(registryCenterConfiguration == null) {
return result;
}
RegistryCenterClient clientInCache = findInCache(registryCenterConfiguration.getNameAndNamespace());
if (null != clientInCache) {
return clientInCache;
}
CuratorFramework client = curatorRepository.connect(registryCenterConfiguration.getZkAddressList(),
registryCenterConfiguration.getNamespace(), registryCenterConfiguration.getDigest());
result.setNameAndNamespace(registryCenterConfiguration.getNameAndNamespace());
if (null == client) {
return result;
}
setRegistryCenterClient(result, client);
return result;
}
private RegistryCenterClient findInCache(final String nameAndNameSpace) {
if (NNS_CURATOR_CLIENT_MAP.containsKey(nameAndNameSpace)) {
if (NNS_CURATOR_CLIENT_MAP.get(nameAndNameSpace).isConnected()) {
return NNS_CURATOR_CLIENT_MAP.get(nameAndNameSpace);
}
NNS_CURATOR_CLIENT_MAP.remove(nameAndNameSpace);
}
return null;
}
private void setRegistryCenterClient(final RegistryCenterClient registryCenterClient, final CuratorFramework client) {
registryCenterClient.setConnected(true);
registryCenterClient.setCuratorClient(client);
NNS_CURATOR_CLIENT_MAP.putIfAbsent(registryCenterClient.getNameAndNamespace(), registryCenterClient);
}
@Override
public RegistryCenterConfiguration findConfig(String nameAndNamespace) {
if(Strings.isNullOrEmpty(nameAndNamespace)){
return null;
}
Collection<ZkCluster> zkClusters = RegistryCenterServiceImpl.ZKADDR_TO_ZKCLUSTER_MAP.values();
for (ZkCluster zkCluster: zkClusters) {
for(RegistryCenterConfiguration each: zkCluster.getRegCenterConfList()) {
if (each != null && nameAndNamespace.equals(each.getNameAndNamespace())) {
return each;
}
}
}
return null;
}
@Override
public RegistryCenterConfiguration findConfigByNamespace(String namespace) {
if(Strings.isNullOrEmpty(namespace)){
return null;
}
Collection<ZkCluster> zkClusters = RegistryCenterServiceImpl.ZKADDR_TO_ZKCLUSTER_MAP.values();
for (ZkCluster zkCluster: zkClusters) {
for(RegistryCenterConfiguration each: zkCluster.getRegCenterConfList()) {
if (each != null && namespace.equals(each.getNamespace())) {
return each;
}
}
}
return null;
}
@Override
public RequestResult refreshRegCenter() {
RequestResult result = new RequestResult();
if(refreshingRegCenter.compareAndSet(false, true)) {
try {
refreshAll();
result.setSuccess(true);
} catch (Throwable t) {
log.error(t.getMessage(), t);
result.setSuccess(false);
result.setMessage(ExceptionUtils.getMessage(t));
} finally {
refreshingRegCenter.set(false);
}
} else {
result.setSuccess(false);
result.setMessage("refreshing, try it later!");
}
return result;
}
private void refreshAll() throws IOException {
refreshRegistryCenterFromJsonFile();
refreshNamespaceShardingListenerManagerMap();
refreshTreeData();
}
public static RegistryCenterClient getCuratorByNameAndNamespace(String nameAndNamespace) {
return NNS_CURATOR_CLIENT_MAP.get(nameAndNamespace);
}
/**
* @see com.vip.saturn.job.console.service.RegistryCenterService#findShardingManagerByNamespace(java.lang.String)
*/
@Override
public NamespaceShardingManager findShardingManagerByNamespace(String namespace) {
return null;
}
}