Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.alibaba.edas.dubbo.EdasRegistry Maven / Gradle / Ivy
package com.alibaba.edas.dubbo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.alibaba.acm.shaded.com.google.common.collect.Lists;
import com.alibaba.cloud.context.cs.AliCloudCsInitializer;
import com.alibaba.cloud.context.cs.CsHandledConfiguration;
import com.alibaba.edas.acm.ConfigService;
import com.alibaba.edas.acm.listener.ConfigChangeListener;
import com.taobao.config.client.Publisher;
import com.taobao.config.client.PublisherRegistrar;
import com.taobao.config.client.PublisherRegistration;
import com.taobao.config.client.Subscriber;
import com.taobao.config.client.SubscriberDataObserver;
import com.taobao.config.client.SubscriberRegistrar;
import com.taobao.config.client.SubscriberRegistration;
import com.taobao.config.client.utils.StringUtils;
import com.taobao.diamond.client.Diamond;
import com.taobao.diamond.client.impl.DiamondEnvRepo;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.taobao.diamond.common.Constants.DEFAULT_GROUP;
/**
* @author edas
*/
public class EdasRegistry extends FailbackRegistry {
private static final Logger LOGGER = LoggerFactory.getLogger(EdasRegistry.class);
private final CsHandledConfiguration csHandledConfiguration;
private final String root;
private final static String DEFAULT_ROOT = "dubbo";
private final static String PREFIX = "dubbo://";
private final static String ACM_SEPARATOR = ":";
private final static int RETRY_TIMES = 5;
private final static String EMPTY = "empty_data";
private final static String TIME_OUT_STRING = System.getProperty("edas.dubbo.subscribe.time");
private final static long TIME_OUT = StringUtils.isBlank(TIME_OUT_STRING) ? 2500 : Integer.parseInt(
TIME_OUT_STRING);
private final ConcurrentHashMap listeners = new ConcurrentHashMap<>();
private final ConcurrentHashMap diamondListeners = new ConcurrentHashMap<>();
private final ConcurrentHashMap> subscriberMap = new ConcurrentHashMap<>();
public EdasRegistry(URL url, CsHandledConfiguration csHandledConfiguration) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
}
if (!group.endsWith(Constants.PATH_SEPARATOR)) {
group = group + Constants.PATH_SEPARATOR;
}
this.root = group;
this.csHandledConfiguration = csHandledConfiguration;
}
@Override
public void doRegister(URL url) {
if (null == url.getParameter(Constants.CATEGORY_KEY) ||
url.getParameter(Constants.CATEGORY_KEY).equals(Constants.CONSUMERS_CATEGORY) ||
url.getParameter(Constants.CATEGORY_KEY).equals(Constants.PROVIDERS_CATEGORY)) {
//如果是consumer 和 provider 数据,则往 config server 注册
registryToConfigServer(url);
} else if (url.getParameter(Constants.CATEGORY_KEY).equals(Constants.ROUTERS_CATEGORY) ||
url.getParameter(Constants.CATEGORY_KEY).equals(Constants.CONFIGURATORS_CATEGORY)) {
//如果是 routers 和 configurators 数据,往 diamond 注册
registryToAcm(url);
}
}
@Override
public void doUnregister(URL url) {
if (null == url.getParameter(Constants.CATEGORY_KEY) ||
url.getParameter(Constants.CATEGORY_KEY).equals(Constants.CONSUMERS_CATEGORY) ||
url.getParameter(Constants.CATEGORY_KEY).equals(Constants.PROVIDERS_CATEGORY)) {
//如果是consumer 和 provider 数据,则往 config server 注册
unRegistryFromConfigServer(url);
} else if (url.getParameter(Constants.CATEGORY_KEY).equals(Constants.ROUTERS_CATEGORY) ||
url.getParameter(Constants.CATEGORY_KEY).equals(Constants.CONFIGURATORS_CATEGORY)) {
//如果是 routers 和 configurators 数据,往 diamond 注册
unRegistryFromAcm(url);
}
}
@Override
public void doSubscribe(URL url, final NotifyListener notifyListener) {
CountDownLatch countDownLatch = new CountDownLatch(1);
ConfigServerNotify configServerNotify = new ConfigServerNotify(countDownLatch, url,
Arrays.asList(notifyListener), this);
listeners.put(url, configServerNotify);
// 订阅 dubbo 协议的数据
subscribeDubboData(url, configServerNotify, notifyListener);
// 当明确地配置了 dubbo 的 group 和 version 时,会尝试订阅 hsf 协议的数据
if (null != url.getParameter("version") && null != url.getParameter("group")) {
subscribeHsfData(url, configServerNotify);
}
try {
countDownLatch.await(TIME_OUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
LOGGER.error("interrupted exception when waiting subscribe ", e);
}
}
@Override
public void doUnsubscribe(URL url, NotifyListener notifyListener) {
unSubscribeFromConfigServer(url);
unSubscribeFromAcm(url);
}
@Override
public boolean isAvailable() {
return false;
}
@Override
public List lookup(URL url) {
return null;
}
private void registryToConfigServer(URL url) {
String dataId = toCategoryPath(url);
String value = url.toFullString();
String publisherName = url.getServiceName() + url.getParameter("pid");
String datumId = getDataTumId(url);
PublisherRegistration registration = new PublisherRegistration(publisherName, dataId,
datumId);
AliCloudCsInitializer.initialize(csHandledConfiguration, registration);
registration.setGroup(url.getParameter("group", ""));
Publisher publisher = PublisherRegistrar.register(registration);
publisher.publish(value);
}
/**
* 方法需要加上synchronized,防止同时更新 ACM 时出现信息覆盖
* 调用 ACM 接口时,需要调用 CAS 接口,防止数据出现覆盖,最多重试五次
*
* @param url
*/
private synchronized void registryToAcm(URL url) {
String dataId = toCategoryPath(url);
dataId = dataId.replace(Constants.PATH_SEPARATOR, ACM_SEPARATOR);
List configs = Lists.newArrayList();
for (int i = 0; i < RETRY_TIMES; i++) {
try {
String content = ConfigService.getConfig(dataId, DEFAULT_GROUP, 3000);
String originContent = content;
if (content == null || StringUtils.isBlank(content)) {
//如果之前无数据,则发布一条
if (i < 2) {
//由于 Diamond 添加配置时不支持 add 的时候使用 CAS,只能是直接发布。
//快速发布两条时,存在第一条丢失的风险,所先 sleep 2s,写法很丑陋
TimeUnit.SECONDS.sleep(1);
continue;
}
configs.add(url.toFullString());
if (Diamond.publishSingle(dataId, DEFAULT_GROUP, configs.toString())) {
return;
}
} else if (StringUtils.equals(content, EMPTY)) {
//由于 Diamond 内容不支持直接设置为 "" ,也不支持设置成 " " 这类,只好设置成一个 EMPTY
configs.add(url.toFullString());
if (Diamond.publishSingleCas(dataId, DEFAULT_GROUP, EMPTY, configs.toString())) {
return;
}
} else {
//如果之前有数据,先解析成数组,增加后更新数据
content = content.substring(1, content.length() - 1);
configs = Lists.newArrayList(content.split(", "));
if (configs.contains(url.toFullString())) {
//如果已经有数据,无需重复添加
return;
}
configs.add(url.toFullString());
if (Diamond.publishSingleCas(dataId, DEFAULT_GROUP, originContent, configs.toString())) {
return;
}
}
} catch (Exception e) {
LOGGER.error("register to acm error,dataId:{},value:{},e:", dataId, url.toFullString(), e);
}
}
LOGGER.error("fail to register to acm after {} retry,dataId:{},value:{},e:", RETRY_TIMES, dataId,
url.toFullString());
}
private void unRegistryFromConfigServer(URL url) {
//do nothing
}
/**
* 方法需要加上synchronized,防止同时更新 ACM 时出现信息覆盖
* 调用 ACM 接口时,需要调用 CAS 接口,防止数据出现覆盖
*
* @param url
*/
private synchronized void unRegistryFromAcm(URL url) {
String dataId = toCategoryPath(url);
dataId = dataId.replace(Constants.PATH_SEPARATOR, ACM_SEPARATOR);
List configs;
for (int i = 0; i < RETRY_TIMES; i++) {
try {
String content = ConfigService.getConfig(dataId, DEFAULT_GROUP, 3000);
String originContent = content;
if (content == null || StringUtils.isBlank(content) || StringUtils.equals(content, EMPTY)) {
//无配置则直接略过
return;
} else {
content = content.substring(1, content.length() - 1);
configs = Lists.newArrayList(content.split(", "));
configs.remove(url.toFullString());
}
if (configs.size() == 0) {
//因为删除接口没有CAS,且内容不支持直接设置为 "" ,也不支持设置成 " "这类,只好设置成一个 EMPTY
//所以其他地方也得加上 EMPTY 的判断
if (Diamond.publishSingleCas(dataId, DEFAULT_GROUP, originContent, EMPTY)) {
return;
}
} else {
content = configs.toString();
if (Diamond.publishSingleCas(dataId, DEFAULT_GROUP, originContent, content)) {
return;
}
}
} catch (Exception e) {
LOGGER.error("unRegister from acm error,dataId:{},e:", dataId, e);
}
}
LOGGER.error("fail to unregister from acm after {} retry,dataId:{},value:{},e:", RETRY_TIMES, dataId,
url.toFullString());
}
private void subscribeHsfData(URL url, ConfigServerNotify configServerNotify) {
String service = url.getServiceInterface();
String version = url.getParameter("version");
String dataId = service + ":" + version;
String subscriberName = url.getServiceName() + url.getParameter("pid");
String datumId = getDataTumId(url);
SubscriberRegistration registration = new SubscriberRegistration(subscriberName,
dataId, datumId);
AliCloudCsInitializer.initialize(csHandledConfiguration, registration);
registration.setGroup(url.getParameter("group"));
Subscriber subscriber = SubscriberRegistrar.register(registration);
subscriber.setDataObserver(configServerNotify);
List subscribers = subscriberMap.get(url);
if (subscribers == null) {
subscribers = new ArrayList<>();
subscriberMap.put(url, subscribers);
}
subscribers.add(subscriber);
}
private void subscribeDubboData(URL url, ConfigServerNotify configServerNotify, NotifyListener notifyListener) {
//不支持通配符查询订阅
String subscriberNameDubbo = url.getServiceName() + url.getParameter("pid");
String datumIdDubbo = getDataTumId(url);
for (String path : toCategoriesPath(url)) {
int index = path.lastIndexOf(Constants.PATH_SEPARATOR);
String category = path.substring(index + 1);
if (category.equals(Constants.PROVIDERS_CATEGORY) || category.equals(Constants.CONSUMERS_CATEGORY)) {
//providers 和 consumers 的数据,从 configserver 获取
SubscriberRegistration registration = new SubscriberRegistration(subscriberNameDubbo, path,
datumIdDubbo);
AliCloudCsInitializer.initialize(csHandledConfiguration, registration);
registration.setGroup(url.getParameter("group", ""));
Subscriber subscriber = SubscriberRegistrar.register(registration);
subscriber.setDataObserver(configServerNotify);
List subscribers = subscriberMap.get(url);
if (subscribers == null) {
subscribers = new ArrayList<>();
subscriberMap.put(url, subscribers);
}
subscribers.add(subscriber);
} else {
//routers 和 configurators的数据,从 diamond 获取
subscribeFromAcm(url, path, notifyListener);
}
}
}
private void unSubscribeFromConfigServer(URL url) {
List subscribers = subscriberMap.get(url);
if (null == subscribers) {
return;
}
for (Subscriber subscriber : subscribers) {
SubscriberRegistrar.unregister(subscriber);
}
}
private void unSubscribeFromAcm(URL url) {
for (String path : toCategoriesPath(url)) {
//routers 和 configurators的数据,从 diamond 获取
path = path.replace(Constants.PATH_SEPARATOR, ACM_SEPARATOR);
ConfigChangeListener configChangeListener = diamondListeners.get(path);
try {
DiamondEnvRepo.getDefaultEnv().removeTenantListener(path, DEFAULT_GROUP, configChangeListener);
} catch (Exception e) {
LOGGER.error("remove config listener from acm error,path:{},", path, e);
}
}
}
private void subscribeFromAcm(URL url, String path, NotifyListener notifyListener) {
path = path.replace(Constants.PATH_SEPARATOR, ACM_SEPARATOR);
DiamondListener diamondListener = diamondListeners.get(path);
if (diamondListener == null) {
diamondListener = new DiamondListener(url, this, Arrays.asList(notifyListener), path);
diamondListeners.put(path, diamondListener);
}
ConfigService.addListener(path, DEFAULT_GROUP, diamondListener);
}
class ConfigServerNotify implements SubscriberDataObserver {
private CountDownLatch countDownLatch;
private URL url;
private Collection listeners;
private EdasRegistry registry;
private List lastHsfUrls;
private List lastDubboUrls;
public ConfigServerNotify(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
public ConfigServerNotify(CountDownLatch countDownLatch, URL url, List notifyListeners,
EdasRegistry edasConfigServerRegistry) {
this.countDownLatch = countDownLatch;
this.url = url;
this.listeners = notifyListeners;
this.registry = edasConfigServerRegistry;
this.lastDubboUrls = Collections.emptyList();
this.lastHsfUrls = Collections.emptyList();
}
@Override
public void handleData(String dataId, List data) {//只要data内容有任何变更,都会回调这个方法
LOGGER.info("New issues from ConfigServer arrived: " + data);
List result = new ArrayList();
if (dataId.contains(Constants.PATH_SEPARATOR)) {
processDubboData(dataId, data);
} else {
processHsfData(dataId, data);
}
result.addAll(lastDubboUrls);
result.addAll(lastHsfUrls);
for (NotifyListener listener : listeners) {
registry.notify(url, listener, result);
}
if (result.size() > 0) {
countDownLatch.countDown();
}
}
private void processHsfData(String dataId, List data) {
String consumerService = url.getServiceInterface();
String providerService = dataId.substring(0, dataId.indexOf(":"));
if (!Constants.ANY_VALUE.equals(consumerService)) {
if (!providerService.equals(consumerService)) {
return;
}
}
List urls = new ArrayList();
for (Object obj : data) {
String str = (String)obj;
URL u = convertHsfDataToDubboURL(str, dataId);
urls.add(u);
}
this.lastHsfUrls = urls;
}
private void processDubboData(String dataId, List data) {
String consumerService = url.getServiceInterface();
String servicePath = toServicePath(dataId);
String providerService = servicePath.startsWith(root) ? servicePath.substring(root.length()) : servicePath;
if (!Constants.ANY_VALUE.equals(consumerService)) {
if (!providerService.equals(consumerService)) {
return;
}
}
List urls = new ArrayList();
for (Object obj : data) {
String str = (String)obj;
str = PREFIX + str;
URL u = URL.valueOf(str);
if (UrlUtils.isMatch(url, u)) {
urls.add(u);
}
}
this.lastDubboUrls = urls;
}
private String toCategoryName(String categoryPath) {
int i = categoryPath.lastIndexOf(Constants.PATH_SEPARATOR);
return i > 0 ? categoryPath.substring(i + 1) : categoryPath;
}
private URL convertHsfDataToDubboURL(String data, String dataId) {
String service = dataId.substring(0, dataId.indexOf(":"));
String version = dataId.substring(dataId.indexOf(":") + 1);
String host = data.substring(0, data.indexOf(":"));
int port = Integer.parseInt(StringUtils.substringBetween(data, ":", "?"));
Map params = new HashMap<>();
params.put("side", "provider");
params.put("interface", service);
params.put("version", version);
params.put("group", url.getParameter("group"));
return new URL("dubbo", host, port, service, params);
}
}
public class DiamondListener extends ConfigChangeListener {
private final URL url;
private final EdasRegistry registry;
private final Collection listeners;
private final String dataId;
public DiamondListener(URL url, EdasRegistry registry,
Collection listeners,
String dataId) {
this.url = url;
this.registry = registry;
this.listeners = listeners;
this.dataId = dataId;
}
private List getEmptyUrls() {
String subscribeStr = this.dataId.replace(ACM_SEPARATOR, Constants.PATH_SEPARATOR);
String service = this.url.getServiceInterface();
String group = this.url.getParameter("group");
String version = this.url.getParameter("version");
String category = subscribeStr.substring(subscribeStr.lastIndexOf(Constants.PATH_SEPARATOR) + 1);
URL emptyUrl = URL.valueOf(Constants.EMPTY_PROTOCOL + "://0.0.0.0/" + service + "?"
+ Constants.CATEGORY_KEY + "=" + category
+ (group == null ? "" : "&" + Constants.GROUP_KEY + "=" + group)
+ (version == null ? "" : "&" + Constants.VERSION_KEY + "=" + version));
List urls = new ArrayList<>();
urls.add(emptyUrl);
return urls;
}
@Override
public void receiveConfigInfo(String configInfo) {
if (StringUtils.isBlank(configInfo)) {
List urls = getEmptyUrls();
for (NotifyListener listener : listeners) {
registry.notify(url, listener, urls);
}
return;
}
String content = configInfo.substring(1, configInfo.length() - 1);
List configs = Lists.newArrayList(content.split(", "));
List urls = new ArrayList<>();
for (String tmp : configs) {
URL url = URL.valueOf(tmp);
urls.add(url);
}
if (urls.size() == 0) {
urls = getEmptyUrls();
}
for (NotifyListener listener : listeners) {
registry.notify(url, listener, urls);
}
}
}
private String toCategoryPath(URL url) {
return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY,
Constants.DEFAULT_CATEGORY);
}
private String toServicePath(String categoryPath) {
int i;
if (categoryPath.startsWith(root)) {
i = categoryPath.indexOf(Constants.PATH_SEPARATOR, root.length());
} else {
i = categoryPath.indexOf(Constants.PATH_SEPARATOR);
}
return i > 0 ? categoryPath.substring(0, i) : categoryPath;
}
private String toServicePath(URL url) {
return root + url.getServiceInterface();
}
private String[] toCategoriesPath(URL url) {
String[] categories;
if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
categories = new String[] {Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
} else {
categories = url.getParameter(Constants.CATEGORY_KEY, new String[] {Constants.DEFAULT_CATEGORY});
}
String[] paths = new String[categories.length];
for (int i = 0; i < categories.length; i++) {
paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categories[i];
}
return paths;
}
private String getDataTumId(URL url) {
String datumId = System.getProperty("JM.CONTAINER.ID");
if (StringUtils.isEmpty(datumId)) {
String appNameId = System.getProperty("project.name");
if (StringUtils.isNotEmpty(appNameId)) {
datumId = "ecu:" + appNameId + ":" + url.getHost();
} else {
datumId = "ecu:" + url.getHost();
}
}
return datumId;
}
}