dubbo不仅功能强大,扩展性也非常强,在关键的位置,dubbo都留有扩展接口,比如:协议(Protocol)、集群策略(Cluster)、代理工厂(ProxyFactory)、序列化方式(Serialization)、线程池(ThreadPool)、负载均衡(LoadBalance)等等。每一种扩展都有多种实现,采用策略模式把实现写在/META-INF/dubbo/internal中,然后通过ExtensionLoader类来加载扩展,最终通过一系列的策略选择最正确的那个。这一系列的策略正是今天所讲的内容。
标记为可扩展,并且可以设置默认值。用来定义的扩展接口,默认值即表示策略文件文中的某个key,那么这个key的value即是扩展的默认实现。比如:Protocol接口,
@SPI("dubbo")
public interface Protocol {
META-INF\dubbo\internal\com.alibaba.dubbo.rpc.Protocol文件中:
在
加载SPI的扩展时,SPI的默认值缓存在ExtensionLoader.cachedDefaultName中,以备使用。
@Adaptive是用的最多的扩展,可以用来标注类和方法。以下分别进行说明:
ExtensionLoader.getAdativeExtension(***.class) 先到缓存中查找是否已经存在@Adaptive标注的类。加载到ExtensionLoader.cachedAdaptiveClass中,该类只能有一个,有多个的话报异常。
比如AdaptiveCompiler,AdaptiveExtensionFactory
则去需要生成一个***$Adpative的代理类,把所有标注@Adaptive的方法,都生成一个代理方法,经过编译,加载,创建对象返回,代理类继承***接口,调用时与真实的扩展实现无差异。此时最关键的是要弄明白***$Adpative这个代理类中做了什么。
根据结果查找过程,return的代码(以Protocol为例):
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
此处最关键弄清楚extName的值,继续往上看:
code.append("\nString extName = ").append(getNameCode).append(";");
而getNameCode可能来自九个可能:
for (int i = value.length - 1; i >= 0; --i) {
if(i == value.length - 1) { //第一次循环
if(null != defaultExtName) { //@SPI的默认值不为空的情况
if(!"protocol".equals(value[i])) //@Adaptive的value不是protocol的情况
if (hasInvocation) //方法参数含有Invocation类型
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
}
else {
if(!"protocol".equals(value[i]))
if (hasInvocation)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
else
getNameCode = "url.getProtocol()";
}
}
else {
if(!"protocol".equals(value[i]))
if (hasInvocation)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
else
getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
}
}
Protocol扩展:@SPI默认值"dubbo"、@Adaptive无默认值(value被置为“protocol”),此时getNameCode值只可能为:
getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
也就是说根据当前url(参数)进行获取,如果url的值为null,使用默认值dubbo
getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
优先从URL中获取属性名叫做value的属性值,如果不存在,则使用默认的defaultExtName
举例:集群策略(Cluster)
@SPI(FailoverCluster.NAME)
public interface Cluster {
@Adaptive
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
@SPI默认值failover,但如果url中配置了属性cluster=failfast,如下图:
那么返回的扩展实现就是 com.alibaba.dubbo.rpc.cluster.support.FailfastClustergetNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
先去从URL获取methodName.${value}属性的值,然后获取${value}属性的值,如果都没有,则使用SPI的默认值。
举例:负载均衡接口(LoadBalance)
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
@Adaptive("loadbalance")
Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException;
}
存在invocation参数,但并没有获取自适应方法,只从invokers中获取对负载均衡(loadbalance属性)的的值,如果不存在则默认使用随机策略(random)。
if (invokers != null && invokers.size() > 0) {
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
} else {
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
}
优先使用后面属性的值,如果后面的属性值不存在,则使用前面的,如果都不存在,则默认使用defaultExtName
举例:
@SPI(AllDispatcher.NAME)
public interface Dispatcher {
@Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"}) // 后两个参数为兼容旧配置
ChannelHandler dispatch(ChannelHandler handler, URL url);
}
随着产品的迭代,会有些属性名变得不再恰当,轻易废弃或者修改的话就无法兼容旧版本,这种方式是可以达到向下的兼容的目的。本例中,优先使用channel.handler的属性值,不存在则使用dispather的属性值,还不存在则使用dispatcher(注意与dispather区别),还不存在则用@SPI的默认值all。
如果ExtensionLoader.getAdativeExtension(***.class)获取自适应扩展类时,既不存在@Adaptive标记的实现类,接口上也不存在@Adaptive标注的方法,则报异常。
看源码:
public @interface Activate {
/**
* Group过滤条件。
* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
* 如没有Group设置,则不过滤。
*/
String[] group() default {};
/**
* Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
* 示例:
* 注解的值 @Activate("cache,validatioin")
,
* 则{@link ExtensionLoader#getActivateExtension}的URL的参数有cache
Key,或是validatioin
则返回扩展。
* 如没有设置,则不过滤。
*/
String[] value() default {};
String[] before() default {};
String[] after() default {};
int order() default 0;
}
此处只关心group()和value()两个方法。group 表示该扩展用在哪个组中(comsumer、provider)。value表示关注的URL的key。
举例:
例一:限流过滤器(ExecuteLimitFilter)
@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
public class ExecuteLimitFilter implements Filter {
group=provider说明是仅仅用在服务端,value=executes表示,当URL中的executes不为空值时才会使用此过滤器。
注:executes默认cpu+1,不手动设置也会有值的。
例二:缓存过滤器(CacheFilter)
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)
public class CacheFilter implements Filter {
该过滤器同时过滤服务端和消费端,且当URL中的属性cache的值不为空才使用。
ProtocolListenerWrapper,ProtocolFilterWrapper这写Wrapper都有两个共同特点:
1、实现被包装的接口
2、要有一个构造方法,构造方法的参数类型就是被包装的接口类型。
举例:ProtocolListenerWrapper,ProtocolFilterWrapper都是用来包装Protocol的,看他们的源码:
public class ProtocolFilterWrapper implements Protocol {
private final Protocol protocol;
public ProtocolFilterWrapper(Protocol protocol){
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
public class ProtocolListenerWrapper implements Protocol { private final Protocol protocol; public ProtocolListenerWrapper(Protocol protocol){ if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; }
在目标类被调用的时候,包装类会优先被调用,这个的实现方式也在ExtensionLoander中:
try { clazz.getConstructor(type); Set> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet >(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) {
在获取type的目标扩展类时,会调用createExtension方法,方法中把type所有的wrapperClass一层层包装到type的外层,至于顺序时不固定的:
Set> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
wrapper中一般没有set方法,可以忽略injectExtension.这个方法是用来注入该扩展中的持有的其他扩展。
这样返回的instance其实是被wrapper包装过的(可能好几层)的对象,调用的时候会先调到wrapper的方法中。
wrapper中干了什么?
先说ProtocolListenerWrapper:
public Exporter export(Invoker invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
public Invoker refer(Class type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return new ListenerInvokerWrapper(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
}
new ListenerInvokerWrapper和new ListenerExporterWrapper分别会在服务发现和服务暴漏时调用。看代码其实很简单,就是把与该服务相关的listener全部拿出来,先挨个调用一边。“先”的意思时发生在真正的调用之前。
再说ProtocolFilterWrapper:
public Exporter export(Invoker invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
public Invoker refer(Class type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) { Invoker<T> last = invoker; Listfilters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); if (filters.size() > 0) { for (int i = filters.size() - 1; i >= 0; i --) { final Filter filter = filters.get(i); final Invoker<T> next = last; last = new Invoker<T>() { public Class<T> getInterface() { return invoker.getInterface(); } public URL getUrl() { return invoker.getUrl(); } public boolean isAvailable() { return invoker.isAvailable(); } public Result invoke(Invocation invocation) throws RpcException { return filter.invoke(next, invocation); } public void destroy() { invoker.destroy(); } @Override public String toString() { return invoker.toString(); } }; } } return last; }
这个更容易理解,就是把所有应该用到的Filter取出来,组织起一个链条,当服务真正被调用的时候,会先调用这些过滤器。
相同点:都实现Protocol接口,都有一个参数为Protocol类型的构造器。都需要重写export和refer方法。
区别:listenerWrapper执行refer时,会通过new ListenerInvokerWrapper对Invoker进行封装,但ListenerInvokerWrapper中会把所有的listener全部执行一边,也就是listener被组织起来时已经被触发了。
filterWrapper执行refer时,仅仅是把所有的filter编制成一条链(单向链表),然后把链表的尾部返回。这样真正调用invoker.invoke时会从尾部调用filter.invoke,最后再调用真正的invoker.invoke。filter被组织起来并不会马上触发,而是伴随着invoke的调用才触发。
该说的第二节说完了