背景
前面文章分析了dubbo服务端的一些逻辑,这篇文章开始我们分析消费端侧的知识,消费端较生产端略复杂些,不过前面跟上了后面更容易理解。
重点
- 消费端Invoker对象的创建和包装
- 如何将Invoker对象和客户端引用的bean对象结合
分析
Spring 入口
消费者的初始化入口还是spring的bean后置处理,我们回顾下消费端的使用
<dubbo:reference id="helloService" interface="com.xx.HelloService" />
public class HelloController {
@Resource
private HelloService helloService;
dubbo 客户端对于服务端接口的引用也托管到了spring容器中,通过 ReferenceBean 类注册为bean。
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean {
@Override
public Object getObject() { return get();}
public synchronized T get() {
checkAndUpdateSubConfigs();
if (ref == null) {
init();
}
return ref;
}
}
改类实现了 FactoryBean 具有个性化的bean创建逻辑,也具有spring 生命周期的能力。
Invoker 对象创建
进到init() 中
org.apache.dubbo.config.ReferenceConfig#init
private void init() {
//...
ref = createProxy(map);
//....
}
org.apache.dubbo.config.ReferenceConfig#createProxy
private T createProxy(Map<String, String> map) {
//...
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
return (T) PROXY_FACTORY.getProxy(invoker);
}
private static Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
invoker 通过 Protocol自适应扩展点 得到,urls.get(0) 得到的是注册中心的地址(registry),我们继续往下。
org.apache.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = URLBuilder.from(url)
.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
.removeParameter(REGISTRY_KEY)
.build();
Registry registry = registryFactory.getRegistry(url);//1
return doRefer(cluster, registry, type, url);
}
这里多了一个概念 “Registry” 注册中心,RegistryFactory 会根据配置类型找到匹配的注册中心,我们这里用的是zookeeper(ZookeeperRegistry),看看doRefer()。
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
registry.register(directory.getRegisteredConsumerUrl());
}
directory.buildRouterChain(subscribeUrl);
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
终于出现invoker了,但是这边又出现了新概念“directory”目录、“cluster” 集群,directory的作用是从多个生产者中选择出一个最合适的作为调用目标地址,cluster主要是作为调用失败之后的容错策略,cluster也是自适应扩展点,默认扩展点为"FailoverCluster失败重试"(当然其中还会包装wrapper扩展)。我们进入cluster.join()看看。
org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper#join
public class MockClusterWrapper implements Cluster {
@Override
public <T> Invoker<T> join(Directory<T> directory) {
return new MockClusterInvoker<T>(directory,this.cluster.join(directory));
}
org.apache.dubbo.rpc.cluster.support.FailoverCluster#join
public class FailoverCluster implements Cluster {
public final static String NAME = "failover";
@Override
public <T> Invoker<T> join(Directory<T> directory) {
return new FailoverClusterInvoker<T>(directory);
}
所以invoker最后被包装为
new MockClusterInvoker(directory, new FailoverClusterInvoker(directory));
代理对象创建
我们继续回到上面的org.apache.dubbo.config.ReferenceConfig#createProxy 方法中 invoker 的值我们已经分析出结果,下面看 PROXY_FACTORY 有看过我另一篇服务暴露文章的应该了解这也是一个ProxyFactory 自适应扩展点。默认实现为 javassist
private static ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
public class JavassistProxyFactory extends AbstractProxyFactory {
@Override
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
这个Proxy要留意,并不是jdk中的,而是 org.apache.dubbo.common.bytecode.Proxy,我们debug下看一眼生成的代理类。
org.apache.dubbo.common.bytecode.Proxy#getProxy
public static Proxy getProxy(Class<?>... ics) {
return getProxy(ClassUtils.getClassLoader(Proxy.class), ics);
}
图中可以看到,getProxy方法中把api接口中所有的方法都生成了一个新的方法体,然后通过反射的方式调用。注意这里和生产者不同,生产者是通过if else进行判断调用的
//重新生成的方法
Object[] args = new Object[1];
args[0] = ($w)$1;
Object ret = handler.invoke(this, methods[0], args); //InvokerInvocationHandler 直接调用
return ret;
反射回调的类是从getProxy 方法传入 InvokerInvocationHandler 这个类实现了java的 InvocationHandler new InvokerInvocationHandler(invoker),在创建对象的时候我们把invoker 对象也传了进去供后面调用远程使用。这样 PROXY_FACTORY.getProxy(invoker); 方法就分析完了,返回的是一个 javassist 生成的字节码对象。这个对象持有invoker远程调用对象。