debug测试源码 。
写在前面
在dubbo之服务本地暴露 文章中我们分析了本地暴露的过程,本文一起看下对应的服务消费者端是如何引用的,即本地引用。
一般的,当使用本地引用时,xml配置如下:
<fakeRoot>
<dubbo:reference id="scopeLocalService" interface="dongshi.daddy.service.scopelocal.ScopeLocalService" scope="local"/>
</fakeRoot>
java代码如下:
class FakeCls {
public class MyProviderScopeLocalMain {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext("META-INF/spring/scopelocal/provider-scope-local.xml");
providerContext.start();
ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext("META-INF/spring/scopelocal/consumer-scope-local.xml");
consumerContext.start();
ScopeLocalService scopeLocalService = consumerContext.getBean("scopeLocalService", ScopeLocalService.class);
scopeLocalService.sayHi("hello scope local!");
System.in.read();
}
}
}
其中的代码consumerContext.getBean("scopeLocalService", ScopeLocalService.class) 就是获取用于本地调用服务提供者的代理类的,在dubbo中<dubbo:reference> 对应的bean是一个工厂bean ,通过该工厂bean获取我们最终需要的代理类,之后用来调用服务提供者,如下debug:
从图中可到最终调用到com.alibaba.dubbo.config.ReferenceConfig#createProxy ,接下来我们就从这个方法来开始分析吧!
1:createProxy
源码如下:
class FakeCls {
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
if (url != null && url.length() > 0) {
isJvmRefer = false;
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
isJvmRefer = isInjvm().booleanValue();
}
if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true;
}
if (c && !invoker.isAvailable()) {
initialized = false;
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
return (T) proxyFactory.getProxy(invoker);
}
}
2022-02-09 18:08:29 处InjvmProtocol.getInjvmProtocol() 处是获取InJvmProtocol扩展类的实例,2022-02-09 18:08:29 处isInjvmRefer ,是根据协议判断是否是本地引用,具体参考1.1:isInjvmRefer 。2022-02-15 10:02:41 处是获取调用本地服务提供者的Invoker,这里refprotocol 是Protocol$Adaptive ,该类为动态生成的类,源码如下:
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([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);
}
}
看其中的方法refer ,因为我们的url是injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?... ,所以最终调用的是Protocol扩展类是com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol ,再考虑wrapper的话,调用顺序是Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => InjvmProtocol ,具体参考2:Protocol 。
1.1:isInjvmRefer
源码如下:
class FakeCls {
public boolean isInjvmRefer(URL url) {
final boolean isJvmRefer;
String scope = url.getParameter(Constants.SCOPE_KEY);
if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) {
isJvmRefer = false;
} else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) {
isJvmRefer = true;
} else if (Constants.SCOPE_REMOTE.equals(scope)) {
isJvmRefer = false;
} else if (url.getParameter(Constants.GENERIC_KEY, false)) {
isJvmRefer = false;
} else if (getExporter(exporterMap, url) != null) {
isJvmRefer = true;
} else {
isJvmRefer = false;
}
return isJvmRefer;
}
}
2022-02-14 17:49:09 处是尝试获取服务提供者对应的Exporter,如果是获取到则说明是本地引用,具体参考1.1.1:getExporter 。
1.1.1:getExporter
源码如下:
class FakeCls {
static Exporter<?> getExporter(Map<String, Exporter<?>> map, URL key) {
Exporter<?> result = null;
if (!key.getServiceKey().contains("*")) {
result = map.get(key.getServiceKey());
} else {
}
if (result == null) {
return null;
} else if (ProtocolUtils.isGeneric(
result.getInvoker().getUrl().getParameter(Constants.GENERIC_KEY))) {
return null;
} else {
return result;
}
}
}
2:Protocol
2.1:ProtocolFilterWrapper
class FakeCls {
@Override
public <T> Invoker<T> refer(Class<T> 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);
}
}
2022-02-15 10:57:02 处是先继续向下调用,然后将最终返回的Invoker添加Filter调用链,buildInvokerChain获取的是public static final String CONSUMER = "consumer"; 即group=consumer 的Filter,如下就会获取到:
@Activate(group = Constants.CONSUMER)
public class DubboHystrixFilter implements Filter {}
2.2:ProtocolFilterWrapper
包装原始Invoker,增加InvokerListener 监听器们,监听暴露和取消暴露事件,源码如下:
class FakeCls {
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
}
}
2022-02-15 14:03:50 处ListenerInvokerWrapper是Invoker的包装类,主要源码如下:
public class ListenerInvokerWrapper<T> implements Invoker<T> {
private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class);
private final Invoker<T> invoker;
}
这其实就是一种装饰设计模式 的应用。
2.3:InjvmProtocol
源码如下:
class FakeCls {
@Override
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}
}
2022-02-15 13:19:45 处是直接封装InjvmInvoker,其中exporterMap 中包含了所有的服务提供者类,如下可能配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:service interface="dongshi.daddy.service.scopelocal.ScopeLocalService" ref="scopeLocalService" scope="local"/>
<bean id="scopeLocalService" class="dongshi.daddy.service.scopelocal.ScopeLocalServiceImpl"/>
<dubbo:service interface="dongshi.daddy.service.scopelocal.ScopeLocalService1" ref="scopeLocalService1" scope="local"/>
<bean id="scopeLocalService1" class="dongshi.daddy.service.scopelocal.ScopeLocalServiceImpl1"/>
</beans>
exporterMap 中就会有2个元素,如下:
|