
com.alibaba.dubbo.registry.integration.RegistryProtocol Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dubbo2 Show documentation
Show all versions of dubbo2 Show documentation
The all in one project of dubbo2
The newest version!
/*
* Copyright 1999-2011 Alibaba Group.
*
* 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.
*/
package com.alibaba.dubbo.registry.integration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.common.utils.UrlUtils;
import com.alibaba.dubbo.registry.NotifyListener;
import com.alibaba.dubbo.registry.Registry;
import com.alibaba.dubbo.registry.RegistryFactory;
import com.alibaba.dubbo.registry.RegistryService;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.cluster.Cluster;
import com.alibaba.dubbo.rpc.cluster.Configurator;
import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;
/**
* RegistryProtocol
*
* @author william.liangf
* @author chao.liuc
*/
public class RegistryProtocol implements Protocol {
private Cluster cluster;
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
private Protocol protocol;
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
private RegistryFactory registryFactory;
public void setRegistryFactory(RegistryFactory registryFactory) {
this.registryFactory = registryFactory;
}
private ProxyFactory proxyFactory;
public void setProxyFactory(ProxyFactory proxyFactory) {
this.proxyFactory = proxyFactory;
}
public int getDefaultPort() {
return 9090;
}
private static RegistryProtocol INSTANCE;
public RegistryProtocol() {
INSTANCE = this;
}
public static RegistryProtocol getRegistryProtocol() {
if (INSTANCE == null) {
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(Constants.REGISTRY_PROTOCOL); // load
}
return INSTANCE;
}
private final Map overrideListeners = new ConcurrentHashMap();
public Map getOverrideListeners() {
return overrideListeners;
}
//用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露
//providerurl <--> exporter
private final Map> bounds = new ConcurrentHashMap>();
private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class);
public Exporter export(final Invoker originInvoker) throws RpcException {
//export invoker
final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);
//registry provider
final Registry registry = getRegistry(originInvoker);
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
registry.register(registedProviderUrl);
// 订阅override数据
// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//保证每次export都返回一个新的exporter实例
return new Exporter() {
public Invoker getInvoker() {
return exporter.getInvoker();
}
public void unexport() {
try {
exporter.unexport();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
try {
registry.unregister(registedProviderUrl);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
try {
overrideListeners.remove(overrideSubscribeUrl);
registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
};
}
@SuppressWarnings("unchecked")
private ExporterChangeableWrapper doLocalExport(final Invoker originInvoker){
String key = getCacheKey(originInvoker);
ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) bounds.get(key);
if (exporter == null) {
synchronized (bounds) {
exporter = (ExporterChangeableWrapper) bounds.get(key);
if (exporter == null) {
final Invoker invokerDelegete = new InvokerDelegete(originInvoker, getProviderUrl(originInvoker));
exporter = new ExporterChangeableWrapper((Exporter)protocol.export(invokerDelegete), originInvoker);
bounds.put(key, exporter);
}
}
}
return (ExporterChangeableWrapper) exporter;
}
/**
* 对修改了url的invoker重新export
* @param originInvoker
* @param newInvokerUrl
*/
@SuppressWarnings("unchecked")
private void doChangeLocalExport(final Invoker originInvoker, URL newInvokerUrl){
String key = getCacheKey(originInvoker);
final ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) bounds.get(key);
if (exporter == null){
logger.warn(new IllegalStateException("error state, exporter should not be null"));
return ;//不存在是异常场景 直接返回
} else {
final Invoker invokerDelegete = new InvokerDelegete(originInvoker, newInvokerUrl);
exporter.setExporter(protocol.export(invokerDelegete));
}
}
/**
* 根据invoker的地址获取registry实例
* @param originInvoker
* @return
*/
private Registry getRegistry(final Invoker originInvoker){
URL registryUrl = originInvoker.getUrl();
if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
}
return registryFactory.getRegistry(registryUrl);
}
/**
* 返回注册到注册中心的URL,对URL参数进行一次过滤
* @param originInvoker
* @return
*/
private URL getRegistedProviderUrl(final Invoker originInvoker){
URL providerUrl = getProviderUrl(originInvoker);
//注册中心看到的地址
final URL registedProviderUrl = providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY);
return registedProviderUrl;
}
private URL getSubscribedOverrideUrl(URL registedProviderUrl){
return registedProviderUrl.setProtocol(Constants.PROVIDER_PROTOCOL)
.addParameters(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false));
}
/**
* 通过invoker的url 获取 providerUrl的地址
* @param origininvoker
* @return
*/
private URL getProviderUrl(final Invoker origininvoker){
String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
if (export == null || export.length() == 0) {
throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
}
URL providerUrl = URL.valueOf(export);
return providerUrl;
}
/**
* 获取invoker在bounds中缓存的key
* @param originInvoker
* @return
*/
private String getCacheKey(final Invoker originInvoker){
URL providerUrl = getProviderUrl(originInvoker);
String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
return key;
}
@SuppressWarnings("unchecked")
public Invoker refer(Class type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0 ) {
if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
|| "*".equals( group ) ) {
return doRefer( getMergeableCluster(), registry, type, url );
}
}
return doRefer(cluster, registry, type, url);
}
private Cluster getMergeableCluster() {
return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("mergeable");
}
private Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
RegistryDirectory directory = new RegistryDirectory(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
return cluster.join(directory);
}
//过滤URL中不需要输出的参数(以点号开头的)
private static String[] getFilteredKeys(URL url) {
Map params = url.getParameters();
if (params != null && !params.isEmpty()) {
List filteredKeys = new ArrayList();
for (Map.Entry entry : params.entrySet()) {
if (entry != null && entry.getKey() != null && entry.getKey().startsWith(Constants.HIDE_KEY_PREFIX)) {
filteredKeys.add(entry.getKey());
}
}
return filteredKeys.toArray(new String[filteredKeys.size()]);
} else {
return new String[] {};
}
}
public void destroy() {
List> exporters = new ArrayList>(bounds.values());
for(Exporter exporter :exporters){
exporter.unexport();
}
bounds.clear();
}
/*重新export 1.protocol中的exporter destory问题
*1.要求registryprotocol返回的exporter可以正常destroy
*2.notify后不需要重新向注册中心注册
*3.export 方法传入的invoker最好能一直作为exporter的invoker.
*/
private class OverrideListener implements NotifyListener {
private volatile List configurators;
private final URL subscribeUrl;
public OverrideListener(URL subscribeUrl) {
this.subscribeUrl = subscribeUrl;
}
/*
* provider 端可识别的override url只有这两种.
* override://0.0.0.0/serviceName?timeout=10
* override://0.0.0.0/?timeout=10
*/
public void notify(List urls) {
List result = null;
for (URL url : urls) {
URL overrideUrl = url;
if (url.getParameter(Constants.CATEGORY_KEY) == null
&& Constants.OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
// 兼容旧版本
overrideUrl = url.addParameter(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY);
}
if (! UrlUtils.isMatch(subscribeUrl, overrideUrl)) {
if (result == null) {
result = new ArrayList(urls);
}
result.remove(url);
logger.warn("Subsribe category=configurator, but notifed non-configurator urls. may be registry bug. unexcepted url: " + url);
}
}
if (result != null) {
urls = result;
}
this.configurators = RegistryDirectory.toConfigurators(urls);
List> exporters = new ArrayList>(bounds.values());
for (ExporterChangeableWrapper exporter : exporters){
Invoker invoker = exporter.getOriginInvoker();
final Invoker originInvoker ;
if (invoker instanceof InvokerDelegete){
originInvoker = ((InvokerDelegete)invoker).getInvoker();
}else {
originInvoker = invoker;
}
URL originUrl = RegistryProtocol.this.getProviderUrl(originInvoker);
URL newUrl = getNewInvokerUrl(originUrl, urls);
if (! originUrl.equals(newUrl)){
RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);
}
}
}
private URL getNewInvokerUrl(URL url, List urls){
List localConfigurators = this.configurators; // local reference
// 合并override参数
if (localConfigurators != null && localConfigurators.size() > 0) {
for (Configurator configurator : localConfigurators) {
url = configurator.configure(url);
}
}
return url;
}
}
public static class InvokerDelegete extends InvokerWrapper{
private final Invoker invoker;
/**
* @param invoker
* @param url invoker.getUrl返回此值
*/
public InvokerDelegete(Invoker invoker, URL url){
super(invoker, url);
this.invoker = invoker;
}
public Invoker getInvoker(){
if (invoker instanceof InvokerDelegete){
return ((InvokerDelegete)invoker).getInvoker();
} else {
return invoker;
}
}
}
/**
* exporter代理,建立返回的exporter与protocol export出的exporter的对应关系,在override时可以进行关系修改.
*
* @author chao.liuc
*
* @param
*/
private class ExporterChangeableWrapper implements Exporter{
private Exporter exporter;
private final Invoker originInvoker;
public ExporterChangeableWrapper(Exporter exporter, Invoker originInvoker){
this.exporter = exporter;
this.originInvoker = originInvoker;
}
public Invoker getOriginInvoker() {
return originInvoker;
}
public Invoker getInvoker() {
return exporter.getInvoker();
}
public void setExporter(Exporter exporter){
this.exporter = exporter;
}
public void unexport() {
String key = getCacheKey(this.originInvoker);
bounds.remove(key);
exporter.unexport();
}
}
}