
com.alibaba.dubbo.registry.integration.RegistryDirectory 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.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.Version;
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.registry.NotifyListener;
import com.alibaba.dubbo.registry.Registry;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcInvocation;
import com.alibaba.dubbo.rpc.cluster.Cluster;
import com.alibaba.dubbo.rpc.cluster.Configurator;
import com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory;
import com.alibaba.dubbo.rpc.cluster.Router;
import com.alibaba.dubbo.rpc.cluster.RouterFactory;
import com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory;
import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;
import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;
import com.alibaba.dubbo.rpc.support.RpcUtils;
/**
* RegistryDirectory
*
* @author william.liangf
* @author chao.liuc
*/
public class RegistryDirectory extends AbstractDirectory implements NotifyListener {
private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);
private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();
private Protocol protocol; // 注入时初始化,断言不为null
private Registry registry; // 注入时初始化,断言不为null
private final String serviceKey; // 构造时初始化,断言不为null
private final Class serviceType; // 构造时初始化,断言不为null
private final Map queryMap; // 构造时初始化,断言不为null
private final URL directoryUrl; // 构造时初始化,断言不为null,并且总是赋非null值
private final String[] serviceMethods;
private final boolean multiGroup;
private volatile boolean forbidden = false;
private volatile URL overrideDirectoryUrl; // 构造时初始化,断言不为null,并且总是赋非null值
/*override规则
* 优先级:override>-D>consumer>provider
* 第一种规则:针对某个provider
* 第二种规则:针对所有provider <* ,timeout=5000>
*/
private volatile List configurators; // 初始为null以及中途可能被赋为null,请使用局部变量引用
// Map cache service url to invoker mapping.
private volatile Map> urlInvokerMap; // 初始为null以及中途可能被赋为null,请使用局部变量引用
// Map cache service method to invokers mapping.
private volatile Map>> methodInvokerMap; // 初始为null以及中途可能被赋为null,请使用局部变量引用
// Set cache invokeUrls to invokers mapping.
private volatile Set cachedInvokerUrls; // 初始为null以及中途可能被赋为null,请使用局部变量引用
public RegistryDirectory(Class serviceType, URL url) {
super(url);
if(serviceType == null )
throw new IllegalArgumentException("service type is null.");
if(url.getServiceKey() == null || url.getServiceKey().length() == 0)
throw new IllegalArgumentException("registry serviceKey is null.");
this.serviceType = serviceType;
this.serviceKey = url.getServiceKey();
this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
String group = directoryUrl.getParameter( Constants.GROUP_KEY, "" );
this.multiGroup = group != null && ("*".equals(group) || group.contains( "," ));
String methods = queryMap.get(Constants.METHODS_KEY);
this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);
}
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
public void setRegistry(Registry registry) {
this.registry = registry;
}
public void subscribe(URL url) {
setConsumerUrl(url);
registry.subscribe(url, this);
}
public void destroy() {
if(isDestroyed()) {
return;
}
// unsubscribe.
try {
if(getConsumerUrl() != null && registry != null && registry.isAvailable()) {
registry.unsubscribe(getConsumerUrl(), this);
}
} catch (Throwable t) {
logger.warn("unexpeced error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t);
}
super.destroy(); // 必须在unsubscribe之后执行
try {
destroyAllInvokers();
} catch (Throwable t) {
logger.warn("Failed to destroy service " + serviceKey, t);
}
}
public synchronized void notify(List urls) {
List invokerUrls = new ArrayList();
List routerUrls = new ArrayList();
List configuratorUrls = new ArrayList();
for (URL url : urls) {
String protocol = url.getProtocol();
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
if (Constants.ROUTERS_CATEGORY.equals(category)
|| Constants.ROUTE_PROTOCOL.equals(protocol)) {
routerUrls.add(url);
} else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
|| Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
configuratorUrls.add(url);
} else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
invokerUrls.add(url);
} else {
logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
}
}
// configurators
if (configuratorUrls != null && configuratorUrls.size() >0 ){
this.configurators = toConfigurators(configuratorUrls);
}
// routers
if (routerUrls != null && routerUrls.size() >0 ){
List routers = toRouters(routerUrls);
if(routers != null){ // null - do nothing
setRouters(routers);
}
}
List localConfigurators = this.configurators; // local reference
// 合并override参数
this.overrideDirectoryUrl = directoryUrl;
if (localConfigurators != null && localConfigurators.size() > 0) {
for (Configurator configurator : localConfigurators) {
this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
}
}
// providers
refreshInvoker(invokerUrls);
}
/**
* 根据invokerURL列表转换为invoker列表。转换规则如下:
* 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
* 2.如果传入的invoker列表不为空,则表示最新的invoker列表
* 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
* @param invokerUrls 传入的参数不能为null
*/
private void refreshInvoker(List invokerUrls){
if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // 禁止访问
this.methodInvokerMap = null; // 置空列表
destroyAllInvokers(); // 关闭所有Invoker
} else {
this.forbidden = false; // 允许访问
Map> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet();
this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
}
if (invokerUrls.size() ==0 ){
return;
}
Map> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
Map>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
// state change
//如果计算错误,则不进行处理.
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
return ;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try{
destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
}catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
private Map>> toMergeMethodInvokerMap(Map>> methodMap) {
Map>> result = new HashMap>>();
for (Map.Entry>> entry : methodMap.entrySet()) {
String method = entry.getKey();
List> invokers = entry.getValue();
Map>> groupMap = new HashMap>>();
for (Invoker invoker : invokers) {
String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");
List> groupInvokers = groupMap.get(group);
if (groupInvokers == null) {
groupInvokers = new ArrayList>();
groupMap.put(group, groupInvokers);
}
groupInvokers.add(invoker);
}
if (groupMap.size() == 1) {
result.put(method, groupMap.values().iterator().next());
} else if (groupMap.size() > 1) {
List> groupInvokers = new ArrayList>();
for (List> groupList : groupMap.values()) {
groupInvokers.add(cluster.join(new StaticDirectory(groupList)));
}
result.put(method, groupInvokers);
} else {
result.put(method, invokers);
}
}
return result;
}
/**
* 将overrideURL转换为map,供重新refer时使用.
* 每次下发全部规则,全部重新组装计算
* @param urls
* 契约:
*
1.override://0.0.0.0/...(或override://ip:port...?anyhost=true)¶1=value1...表示全局规则(对所有的提供者全部生效)
*
2.override://ip:port...?anyhost=false 特例规则(只针对某个提供者生效)
*
3.不支持override://规则... 需要注册中心自行计算.
*
4.不带参数的override://0.0.0.0/ 表示清除override
* @return
*/
public static List toConfigurators(List urls){
List configurators = new ArrayList(urls.size());
if (urls == null || urls.size() == 0){
return configurators;
}
for(URL url : urls){
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
configurators.clear();
break;
}
Map override = new HashMap(url.getParameters());
//override 上的anyhost可能是自动添加的,不能影响改变url判断
override.remove(Constants.ANYHOST_KEY);
if (override.size() == 0){
configurators.clear();
continue;
}
configurators.add(configuratorFactory.getConfigurator(url));
}
Collections.sort(configurators);
return configurators;
}
/**
*
* @param urls
* @return null : no routers ,do nothing
* else :routers list
*/
private List toRouters(List urls) {
List routers = new ArrayList();
if(urls == null || urls.size() < 1){
return routers ;
}
if (urls != null && urls.size() > 0) {
for (URL url : urls) {
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
continue;
}
String routerType = url.getParameter(Constants.ROUTER_KEY);
if (routerType != null && routerType.length() > 0){
url = url.setProtocol(routerType);
}
try{
Router router = routerFactory.getRouter(url);
if (!routers.contains(router))
routers.add(router);
} catch (Throwable t) {
logger.error("convert router url to router error, url: "+ url, t);
}
}
}
return routers;
}
/**
* 将urls转成invokers,如果url已经被refer过,不再重新引用。
*
* @param urls
* @param overrides
* @param query
* @return invokers
*/
private Map> toInvokers(List urls) {
Map> newUrlInvokerMap = new HashMap>();
if(urls == null || urls.size() == 0){
return newUrlInvokerMap;
}
Set keys = new HashSet();
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
//如果reference端配置了protocol,则只选择匹配的protocol
if (queryProtocols != null && queryProtocols.length() >0) {
boolean accept = false;
String[] acceptProtocols = queryProtocols.split(",");
for (String acceptProtocol : acceptProtocols) {
if (providerUrl.getProtocol().equals(acceptProtocol)) {
accept = true;
break;
}
}
if (!accept) {
continue;
}
}
if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
if (! ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
+ ", supported protocol: "+ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
continue;
}
URL url = mergeUrl(providerUrl);
String key = url.toFullString(); // URL参数是排序的
if (keys.contains(key)) { // 重复URL
continue;
}
keys.add(key);
// 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
Map> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 缓存中没有,重新refer
try {
boolean enabled = true;
if (url.hasParameter(Constants.DISABLED_KEY)) {
enabled = ! url.getParameter(Constants.DISABLED_KEY, false);
} else {
enabled = url.getParameter(Constants.ENABLED_KEY, true);
}
if (enabled) {
invoker = new InvokerDelegete(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
logger.error("Failed to refer invoker for interface:"+serviceType+",url:("+url+")" + t.getMessage(), t);
}
if (invoker != null) { // 将新的引用放入缓存
newUrlInvokerMap.put(key, invoker);
}
}else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
/**
* 合并url参数 顺序为override > -D >Consumer > Provider
* @param providerUrl
* @param overrides
* @return
*/
private URL mergeUrl(URL providerUrl){
providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // 合并消费端参数
List localConfigurators = this.configurators; // local reference
if (localConfigurators != null && localConfigurators.size() > 0) {
for (Configurator configurator : localConfigurators) {
providerUrl = configurator.configure(providerUrl);
}
}
providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不检查连接是否成功,总是创建Invoker!
//directoryUrl 与 override 合并是在notify的最后,这里不能够处理
this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // 合并提供者参数
if ((providerUrl.getPath() == null || providerUrl.getPath().length() == 0)
&& "dubbo".equals(providerUrl.getProtocol())) { // 兼容1.0
//fix by tony.chenl DUBBO-44
String path = directoryUrl.getParameter(Constants.INTERFACE_KEY);
if (path != null) {
int i = path.indexOf('/');
if (i >= 0) {
path = path.substring(i + 1);
}
i = path.lastIndexOf(':');
if (i >= 0) {
path = path.substring(0, i);
}
providerUrl = providerUrl.setPath(path);
}
}
return providerUrl;
}
private List> route(List> invokers, String method) {
Invocation invocation = new RpcInvocation(method, new Class[0], new Object[0]);
List routers = getRouters();
if (routers != null) {
for (Router router : routers) {
if (router.getUrl() != null && ! router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
}
}
return invokers;
}
/**
* 将invokers列表转成与方法的映射关系
*
* @param invokersMap Invoker列表
* @return Invoker与方法的映射关系
*/
private Map>> toMethodInvokers(Map> invokersMap) {
Map>> newMethodInvokerMap = new HashMap>>();
// 按提供者URL所声明的methods分类,兼容注册中心执行路由过滤掉的methods
List> invokersList = new ArrayList>();
if (invokersMap != null && invokersMap.size() > 0) {
for (Invoker invoker : invokersMap.values()) {
String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
if (parameter != null && parameter.length() > 0) {
String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
if (methods != null && methods.length > 0) {
for (String method : methods) {
if (method != null && method.length() > 0
&& ! Constants.ANY_VALUE.equals(method)) {
List> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null) {
methodInvokers = new ArrayList>();
newMethodInvokerMap.put(method, methodInvokers);
}
methodInvokers.add(invoker);
}
}
}
}
invokersList.add(invoker);
}
}
newMethodInvokerMap.put(Constants.ANY_VALUE, invokersList);
if (serviceMethods != null && serviceMethods.length > 0) {
for (String method : serviceMethods) {
List> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null || methodInvokers.size() == 0) {
methodInvokers = invokersList;
}
newMethodInvokerMap.put(method, route(methodInvokers, method));
}
}
// sort and unmodifiable
for (String method : new HashSet(newMethodInvokerMap.keySet())) {
List> methodInvokers = newMethodInvokerMap.get(method);
Collections.sort(methodInvokers, InvokerComparator.getComparator());
newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
}
return Collections.unmodifiableMap(newMethodInvokerMap);
}
/**
* 关闭所有Invoker
*/
private void destroyAllInvokers() {
Map> localUrlInvokerMap = this.urlInvokerMap; // local reference
if(localUrlInvokerMap != null) {
for (Invoker invoker : new ArrayList>(localUrlInvokerMap.values())) {
try {
invoker.destroy();
} catch (Throwable t) {
logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
}
}
localUrlInvokerMap.clear();
}
methodInvokerMap = null;
}
/**
* 检查缓存中的invoker是否需要被destroy
* 如果url中指定refer.autodestroy=false,则只增加不减少,可能会有refer泄漏,
*
* @param invokers
*/
private void destroyUnusedInvokers(Map> oldUrlInvokerMap, Map> newUrlInvokerMap) {
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
destroyAllInvokers();
return;
}
// check deleted invoker
List deleted = null;
if (oldUrlInvokerMap != null) {
Collection> newInvokers = newUrlInvokerMap.values();
for (Map.Entry> entry : oldUrlInvokerMap.entrySet()){
if (! newInvokers.contains(entry.getValue())) {
if (deleted == null) {
deleted = new ArrayList();
}
deleted.add(entry.getKey());
}
}
}
if (deleted != null) {
for (String url : deleted){
if (url != null ) {
Invoker invoker = oldUrlInvokerMap.remove(url);
if (invoker != null) {
try {
invoker.destroy();
if(logger.isDebugEnabled()){
logger.debug("destory invoker["+invoker.getUrl()+"] success. ");
}
} catch (Exception e) {
logger.warn("destory invoker["+invoker.getUrl()+"] faild. " + e.getMessage(), e);
}
}
}
}
}
}
public List> doList(Invocation invocation) {
if (forbidden) {
throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "Forbid consumer " + NetUtils.getLocalHost() + " access service " + getInterface().getName() + " from registry " + getUrl().getAddress() + " use dubbo version " + Version.getVersion() + ", Please check registry access list (whitelist/blacklist).");
}
List> invokers = null;
Map>> localMethodInvokerMap = this.methodInvokerMap; // local reference
if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
String methodName = RpcUtils.getMethodName(invocation);
Object[] args = RpcUtils.getArguments(invocation);
if(args != null && args.length > 0 && args[0] != null
&& (args[0] instanceof String || args[0].getClass().isEnum())) {
invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由
}
if(invokers == null) {
invokers = localMethodInvokerMap.get(methodName);
}
if(invokers == null) {
invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
}
if(invokers == null) {
Iterator>> iterator = localMethodInvokerMap.values().iterator();
if (iterator.hasNext()) {
invokers = iterator.next();
}
}
}
return invokers == null ? new ArrayList>(0) : invokers;
}
public Class getInterface() {
return serviceType;
}
public URL getUrl() {
return this.overrideDirectoryUrl;
}
public boolean isAvailable() {
if (isDestroyed()) {
return false;
}
Map> localUrlInvokerMap = urlInvokerMap;
if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) {
for (Invoker invoker : new ArrayList>(localUrlInvokerMap.values())) {
if (invoker.isAvailable()) {
return true;
}
}
}
return false;
}
/**
* Haomin: added for test purpose
*/
public Map> getUrlInvokerMap(){
return urlInvokerMap;
}
/**
* Haomin: added for test purpose
*/
public Map>> getMethodInvokerMap(){
return methodInvokerMap;
}
private static class InvokerComparator implements Comparator> {
private static final InvokerComparator comparator = new InvokerComparator();
public static InvokerComparator getComparator() {
return comparator;
}
private InvokerComparator() {}
public int compare(Invoker o1, Invoker o2) {
return o1.getUrl().toString().compareTo(o2.getUrl().toString());
}
}
/**
* 代理类,主要用于存储注册中心下发的url地址,用于重新重新refer时能够根据providerURL queryMap overrideMap重新组装
*
* @author chao.liuc
*
* @param
*/
private static class InvokerDelegete extends InvokerWrapper{
private URL providerUrl;
public InvokerDelegete(Invoker invoker, URL url, URL providerUrl) {
super(invoker, url);
this.providerUrl = providerUrl;
}
public URL getProviderUrl() {
return providerUrl;
}
}
}