##禁用启用提供者
禁用配置
管理后台:dubbo-admin,或叫治理中心
在dubbo的治理中心,动态配置上配置
disabled=true
此外,enabled=true覆盖规则是否生效,可不填,缺省生效。
接口的动态配置 注册到zookeeper 的节点/dubbo/接口全包名/configurators
override://192.168.204.79:20880/lam.dubbo.api.LoginService?category=configurators&disabled=true&dynamic=false&enabled=true, override://192.168.56.1/lam.dubbo.api.LoginService?category=configurators&dynamic=false&enabled=true&mock=fail:return+null
并通知 提供者的订阅者,比如消费者
05-05 11:23:10:INFO(422) com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry - [DUBBO] Notify urls for subscribe url provider://192.168.56.1:20880/lam.dubbo.api.LoginService?anyhost=true&application=lam-dubbo-provider&category=configurators&charset=UTF-8&check=false&default.accepts=0&default.actives=0&default.buffer=8192&default.charset=UTF-8&default.client=netty&default.cluster=failover&default.connections=0&default.executes=0&default.loadbalance=random&default.owner=sky&default.payload=88388608&default.proxy=javassist&default.retries=2&default.serialization=hessian2&default.server=netty&default.threadpool=fixed&default.threads=100&default.timeout=1000&default.token=false&default.weight=100&dispatcher=all&dubbo=2.5.3&interface=lam.dubbo.api.LoginService&loadbalance=roundrobin&methods=logout,login&owner=skylin&pid=10724&serialization=hessian2&side=provider&threadpool=fixed&threads=100×tamp=1493885611668&transporter=netty, urls: [override://192.168.204.79:20880/lam.dubbo.api.LoginService?category=configurators&disabled=true&dynamic=false&enabled=true, override://192.168.56.1/lam.dubbo.api.LoginService?category=configurators&dynamic=false&enabled=true&mock=fail:return+null], dubbo version: 2.5.3, current host: 127.0.0.1
源码原理
提供者的订阅者,会刷新本地的提供者列表,代码如下:
private void refreshInvoker(List<URL> 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<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
}
if (invokerUrls.size() ==0 ){
return;
}
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表 //1处
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表 //11处
// 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);
}
}
}
在代码1处,toInvokers方法将url转成调用者Invoker的列表,这里会有服务禁用、启用功能的实现,看下面方法的实现代码:
在代码11处,toMethodInvokers方法将调用者Invoker的列表,转成方法名映射Invoker的列表,这里会有路由功能的实现,介绍完禁用服务功能,下面会介绍路由功能。
/**
* 将urls转成invokers,如果url已经被refer过,不再重新引用。
*/
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
if(urls == null || urls.size() == 0){
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<String>();
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<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 缓存中没有,重新refer
try {
boolean enabled = true;
if (url.hasParameter(Constants.DISABLED_KEY)) { //2处
enabled = ! url.getParameter(Constants.DISABLED_KEY, false);
} else {
enabled = url.getParameter(Constants.ENABLED_KEY, true);
}
if (enabled) {
invoker = new InvokerDelegete<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) { // 将新的引用放入缓存
newUrlInvokerMap.put(key, invoker);
}
}else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
在代码2处开始,如果url中包含key disabled,且key的值为true,则该提供者将不会转成调用者Invoker,消费者本地的提供者列表则不包含该提供者,那么消费者在调用提供者时,就不会再调用到该提供者。
处理完提供者列表后,接着将提供者列表转成方法名映射提供者关系,即消费者调用了某个方法,会根据方法映射到提供者,代码如下:
/**
* 将invokers列表转成与方法的映射关系
*
* @param invokersMap Invoker列表
* @return 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>>>();
// 按提供者URL所声明的methods分类,兼容注册中心执行路由过滤掉的methods
List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
if (invokersMap != null && invokersMap.size() > 0) {
for (Invoker<T> 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<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null) {
methodInvokers = new ArrayList<Invoker<T>>();
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<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null || methodInvokers.size() == 0) {
methodInvokers = invokersList;
}
newMethodInvokerMap.put(method, route(methodInvokers, method)); //3处
}
}
// sort and unmodifiable
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);
}
在上面代码的3处,route方法实现了路由功能,路由功能在下章节讲到点击打开链接。
自己写了个RPC:
可以给个star,^0^.