Dubbo服务目录
什么是服务目录
在一个集群中,服务提供者的数量并不是一成不变的,有时候会有扩展或者缩容,也就是增加或者减少机器的数量,这些变化的信息需要同步到消费组,服务目录中包含了服务提供者的全部信息,以及根据服务提供者的数量或者配置的变更而动态变更,并封装成invoker列表
AbstractDirectory
该类实现了Directory接口并实现了list方法,也就是获取invoker列表的方法。而具体的实现逻辑是在子类StaticDirectory和RegistryDirectory中,很常见的模板方法。
public abstract class AbstractDirectory<T> implements Directory<T> {
// logger
private static final Logger logger = LoggerFactory.getLogger(AbstractDirectory.class);
private final URL url;
// 是否被销毁
private volatile boolean destroyed = false;
// 消费组url
private volatile URL consumerUrl;
// 服务路由
private volatile List<Router> routers;
public AbstractDirectory(URL url) {
this(url, null);
}
public AbstractDirectory(URL url, List<Router> routers) {
this(url, url, routers);
}
public AbstractDirectory(URL url, URL consumerUrl, List<Router> routers) {
if (url == null)
throw new IllegalArgumentException("url == null");
this.url = url;
this.consumerUrl = consumerUrl;
setRouters(routers);
}
@Override
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
if (destroyed) {
throw new RpcException("Directory already destroyed .url: " + getUrl());
}
// 由子类完成获取invoker目录的逻辑
List<Invoker<T>> invokers = doList(invocation);
// 获取到invoker列表后需要进行服务路由
List<Router> localRouters = this.routers; // local reference
if (localRouters != null && !localRouters.isEmpty()) {
for (Router router : localRouters) {
try {
if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
// 进行服务路由
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
}
}
return invokers;
}
protected void setRouters(List<Router> routers) {
// 服务路由列表
routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
// 获取服务路由参数router (tag,script,condition,mock)
String routerkey = url.getParameter(Constants.ROUTER_KEY);
// 通过SPI机制获取到路由
if (routerkey != null && routerkey.length() > 0) {
RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
routers.add(routerFactory.getRouter(url));
}
// 默认添加的类型 MockInvokersSelector和TagRouter
routers.add(new MockInvokersSelector());
routers.add(new TagRouter());
Collections.sort(routers);
this.routers = routers;
}
// 省略其他方法
.......
protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException;
}
StaticDirectory
从名字可以看出是静态目录也就是不会变化的
public class StaticDirectory<T> extends AbstractDirectory<T> {
private final List<Invoker<T>> invokers;
......
@Override
protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
return invokers;
}
......
}
可以看到是直接返回invoker列表
RegistryDirectory
RegistryDirectory是动态目录,实现了NotifyListener接口,实现了notify方法处理动态刷新invoker列表的逻辑。先来看看doList逻辑
@Override
public List<Invoker<T>> doList(Invocation invocation) {
if (forbidden) {
// 1. 服务提供者关闭
// 2. 服务提供者被禁用
throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,
"No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost()
+ " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
}
List<Invoker<T>> invokers = null;
// 从缓存中获取方法名对应的invokerk列表
Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap;
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) {
// 通过方法名获取 Invoker 列表
invokers = localMethodInvokerMap.get(methodName);
}
if (invokers == null) {
// 通过 * 获取 Invoker 列表 (泛化调用)
invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
}
}
// 返回invoker列表
return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
}
invoker列表是从本地缓存中获取的 那么本地缓存中的值是从哪里更新的呢?前面说了该类实现了notify方法可以动态刷新invoker列表
@Override
public synchronized void notify(List<URL> urls) {
// 存放providers,routers,configurators列表下的内容
List<URL> invokerUrls = new ArrayList<URL>();
List<URL> routerUrls = new ArrayList<URL>();
List<URL> configuratorUrls = new ArrayList<URL>();
for (URL url : urls) {
// 获取协议名
String protocol = url.getProtocol();
// 获取category参数值,默认为providers
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
// 根据不同的category类型将url封装成routerUrls,configuratorUrls,invokerUrls
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());
}
}
// 将configuratorUrls封装成List<Configurator>configurators
if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
this.configurators = toConfigurators(configuratorUrls);
}
// 将routerUrls封装成List<Router>routers
if (routerUrls != null && !routerUrls.isEmpty()) {
List<Router> routers = toRouters(routerUrls);
if (routers != null) { // null - do nothing
setRouters(routers);
}
}
// 做缓存
List<Configurator> localConfigurators = this.configurators; // local reference
// merge override parameters
this.overrideDirectoryUrl = directoryUrl;
if (localConfigurators != null && !localConfigurators.isEmpty()) {
for (Configurator configurator : localConfigurators) {
// 配置 overrideDirectoryUrl
this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
}
}
// 刷新invoker列表
refreshInvoker(invokerUrls);
}
private void refreshInvoker(List<URL> invokerUrls) {
// 如果协议头为Empty,设置forbidden为true,清空缓存,销毁所有的invoker
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();
} else {
this.forbidden = false;
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
// 如果invokerUrls为空,但是缓存不为空,则直接添加缓存列表至invokerUrls
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
// 缓存为空,则将invokerUrls添加到缓存中
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);
}
if (invokerUrls.isEmpty()) {
return;
}
// 将 url 转成 Invoker
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
// 将newUrlInvokerMap转换成<method,List<Invoker<T>>>的映射
// doList的方法中就是从newMethodInvokerMap中根据方法名获取List列表
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
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 {
// 销毁无用的invoker
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);
} catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
上面的逻辑比较简单,如果协议为Empty则清空缓存,销毁所有的invoker,将url装成成Invoker列表,再将该列表转成成<method,List<Invoker>>的映射,如果有多组服务提供者则需要合并,添加到缓存中,最后清空无用的invoker。来看看其中一些具体的操作
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<String>();
// 获取服务消费端配置的协议
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
// 检测协议是否被消费端支持
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;
}
}
// 若服务提供者协议头不被消费者所支持,则忽略当前 providerUrl
if (!accept) {
continue;
}
}
// 忽略 empty 协议
if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
// 通过 SPI 检测服务端协议是否被消费端支持,不支持则抛出异常
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 url = mergeUrl(providerUrl);
String key = url.toFullString();
// 过滤重复的url
if (keys.contains(key)) {
continue;
}
keys.add(key);
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
// 如果缓存没有命中则创建新的invoker
if (invoker == null) {
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
invoker = new InvokerDelegate<T>(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) {
// 将新创建的invoker加入到缓存中
newUrlInvokerMap.put(key, invoker);
}
} else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
上面的逻辑也很简单,就是检测协议是否被消费端支持,支持的话合并URL从缓存中获取invoker,如果缓存未命中的话创建新的invoker
private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
if (invokersMap != null && invokersMap.size() > 0) {
for (Invoker<T> invoker : invokersMap.values()) {
// 从URL中获取methods参数值
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)) {
// 从缓存中获取,如果未命中缓存则创建新的invoker列表加入到缓存中
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null) {
methodInvokers = new ArrayList<Invoker<T>>();
newMethodInvokerMap.put(method, methodInvokers);
}
methodInvokers.add(invoker);
}
}
}
}
invokersList.add(invoker);
}
}
// 进行服务级别路由
List<Invoker<T>> newInvokersList = route(invokersList, null);
// 存储 <*, newInvokersList> 映射关系
newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
if (serviceMethods != null && serviceMethods.length > 0) {
for (String method : serviceMethods) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null || methodInvokers.isEmpty()) {
methodInvokers = newInvokersList;
}
// 进行方法级别路由
newMethodInvokerMap.put(method, route(methodInvokers, method));
}
}
// 排序,转成不可变列表
for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
Collections.sort(methodInvokers, InvokerComparator.getComparator());
newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
}
return Collections.unmodifiableMap(newMethodInvokerMap);
}
上面的逻辑相对比较简单,遍历map,获取到methods数组,根据方法名映射成方法名到invoker列表的映射关系存储到map中,并基于服务和方法级别进行路由,对invoker列表进行排序转换成不可变列表
private Map<String, List<Invoker<T>>> toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) {
Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>();
for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) {
String method = entry.getKey();
List<Invoker<T>> invokers = entry.getValue();
Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>();
for (Invoker<T> invoker : invokers) {
// 获取group参数值
String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");
List<Invoker<T>> groupInvokers = groupMap.get(group);
if (groupInvokers == null) {
groupInvokers = new ArrayList<Invoker<T>>();
groupMap.put(group, groupInvokers);
}
groupInvokers.add(invoker);
}
// 如果groupMap 只有一组键值对直接返回
if (groupMap.size() == 1) {
result.put(method, groupMap.values().iterator().next());
} else if (groupMap.size() > 1) {
// 合并多个invoker
List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>();
for (List<Invoker<T>> groupList : groupMap.values()) {
groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList)));
}
result.put(method, groupInvokers);
} else {
result.put(method, invokers);
}
}
return result;
}
遍历map获取method到groupInvokers的映射关系,如果groupMap只有一对则返回该键值对,如果有多个,则通过集群类合并每组 Invoker加入到result中
最后就是销毁的逻辑了
private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
destroyAllInvokers();
return;
}
// check deleted invoker
List<String> deleted = null;
if (oldUrlInvokerMap != null) {
Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
// 查找oldUrlInvokerMap和newUrlInvokerMap的交集存储到List<String> deleted中
for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
if (!newInvokers.contains(entry.getValue())) {
if (deleted == null) {
deleted = new ArrayList<String>();
}
deleted.add(entry.getKey());
}
}
}
// 从oldUrlInvokerMap中移除deleted集合中的invoker
if (deleted != null) {
for (String url : deleted) {
if (url != null) {
// 从oldUrlInvokerMap移除invoker
Invoker<T> invoker = oldUrlInvokerMap.remove(url);
if (invoker != null) {
try {
// 销毁invoker
invoker.destroy();
if (logger.isDebugEnabled()) {
logger.debug("destroy invoker[" + invoker.getUrl() + "] success. ");
}
} catch (Exception e) {
logger.warn("destroy invoker[" + invoker.getUrl() + "] faild. " + e.getMessage(), e);
}
}
}
}
}
}
上面的逻辑就是根据newUrlInvokerMap找出oldUrlInvokerMap中需要删除的url,再从oldUrlInvokerMap中移除并销毁invoekr
总结
整个服务目录的逻辑已经看完了 ,主要对动态服务目录的逻辑进行分析,并总结:
- 将url根据不同的category类型将url封装成routerUrls,configuratorUrls
- 将url转换成invoker列表,并转换成method到invoker列表的映射,如果有多组提供者则合并methodInvokerMap
- 最后销毁无用的invoker