🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 引用服务 dubbo中引用服务时,首先需要在配置文件使用`dubbo:reference`标签引用接口,最主要的目的是指定引用的服务名;之后,在程序中使用Spring的Context的getBean()返回接口的代理实例;有了这个实例后就可以在Consumer端调用接口相关的方法。 `dubbo:reference`使用的ReferenceBean实现了FactoryBean接口,getBean时会调用getObject()方法,这是获取引用的入口。 >FactoryBean:以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的Id从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身, 如果要获取FactoryBean对象,可以在id前面加一个&符号来获取。 getObject首先会检查是否已经获取过,如果没有获取过才会执行init进行初始化。主要过程有: ![refer流程](http://www.uxiaowo.com/dubbo/refer.png) ## 1.检查配置 获取consumer端全局配置,检查是否指定了interfaceName,检查指定的接口类是否存在;可以配置一个属性`dubbo.resolve.file`,指定一个文件或使用默认的文件`dubbo-resolve.properties`,内部可以配置将interfaceName转换为url,这个url是点对点直连服务地址; 根据application>module>consumer依次获取注册中心,监控中心等信息,组装成map。这个map中保存了服务及全局配置的信息,用于后续生成代理对象。 ## 2.创建invoker 引用服务时的invoker与发布服务时创建的invoker不同,发布服务中时为了在客户端请求时服务器调用实现类,而引用服务时生成的invoker是为了封装底层的序列化、通信等操作。 1. 将map转为url,如果resolver中指定了url,那么不从injvm中引用;默认情况下如果本地有服务暴露,则引用本地服务;若为injvm协议则使用protocol的refer获取invoker,具体的过程可见injvm实现。 ``` private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); invoker = refprotocol.refer(interfaceClass, url); ``` 2.如果不是injvm协议,会检查是否提供了点对点直连地址,如果提供了则解析地址加入urls中;如果没有提供则会加载注册中心,获取注册中心的url 3.如果有一个直连地址或一个注册中心,会使用这个地址创建invoker ``` invoker = refprotocol.refer(interfaceClass, urls.get(0)); ``` 4.如果有多个直连地址或多个注册中心,则会遍历地址,生成多个invoker加入到一个invoker列表,然后将该列表包装为Directory对象,加入到Cluster中。 ``` List<Invoker<?>> invokers = new ArrayList<Invoker<?>>(); URL registryURL = null; for (URL url : urls) { invokers.add(refprotocol.refer(interfaceClass, url)); if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { registryURL = url; // 用了最后一个registry url } } if (registryURL != null) { // 有 注册中心协议的URL // 对有注册中心的Cluster 只用 AvailableCluster URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); invoker = cluster.join(new StaticDirectory(u, invokers)); } else { // 不是 注册中心的URL invoker = cluster.join(new StaticDirectory(invokers)); } ``` 在这里,需要对Directory对象和Cluster进行说明。 Directory接口保存了某个服务的Invoker列表 ``` public interface Directory<T> extends Node { Class<T> getInterface(); // 获取接口类 List<Invoker<T>> list(Invocation invocation) throws RpcException; // 列出Invoker列表 } ``` Cluster接口的目的是存在多个服务提供者时,失败自动切换,当出现失败,重试其它服务器。通过观察接口,我们可以看出,Cluster中的Directory保存Invoker列表,Cluster又封装为一个Invoker,在doInvoker中做失败切换的策略。 ``` public interface Cluster { <T> Invoker<T> join(Directory<T> directory) throws RpcException; } ``` ### 协议引用服务 1. 由于此时的url是`registry://`,会首先获取 ``` |- ProtocolFilterWrapper |- ProtocolListenerWrapper |- RegistryProtocol ``` ProtocolFilterWrapper和ProtocolListenerWrapper在`registry://`时不起作用,会直接交给RegistryProtocol处理,RegistryProtocol内部首先获取注册中心,如果配置了group属性则使用MergeableCluster,否则使用Cluster$Adaptive; 2.RegistryProtocol会创建RegistryDirectory保存invoker列表,首先通知注册中心注册要消费的服务;使用注册中心的register()注册`consumer://xxx/xxx?xxx`,在zookeeper上创建`/dubbo/接口名/consumers/consumer://xxx`节点。 3. 之后,使用RegistryDirectory的subscribe方法订阅服务消费者`consumer://xxx/xxx?xxx`,根据url中category的值创建以下几个节点,并添加监听事件,监听事件在RegistryDirectory中,也就是说如果providers、configurators、routers节点发生变化,RegistryDirectory会根据注册中心的配置修改内部的Invoker列表 ``` [/dubbo/com.alibaba.dubbo.demo.DemoService/providers, /dubbo/com.alibaba.dubbo.demo.DemoService/configurators, /dubbo/com.alibaba.dubbo.demo.DemoService/routers] ``` 4. 最后使用`cluster.join(directory)`创建一个可以进行失败重试的Invoker返回。 ## 3.创建代理 生成invoker后,最重要的一步时生成代理对象给应用程序使用,使用下面的程序生成指定接口的实例 ``` private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); (T) proxyFactory.getProxy(invoker); ``` proxyFactory对象与发布服务流程中一样,也是下面的结构, ``` -| StubProxyFactoryWrapper -| JavassistProxyFactory ``` StubProxyFactoryWrapper的getProxy方法中使用内部的JavassistProxyFactory的getProxy方法,并对GenericService类型的接口进行处理。 JavassistProxyFactory的getProxy方法中会将consumer端生成的invoker实例包装为指定的服务接口的实例,便于应用程序直接调用相关方法。 ``` public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); } ``` 首先使用 Proxy.getProxy()创建了interfaces接口的实现类A,newInstance方法会实例化这个实现类A并将invoker包装为InvokerInvocationHandler传给实现类的实例。 ``` Proxy.getProxy() 动态创建实现类A new A(new InvokerInvocationHandler(invoker)); ``` 在实现上,getProxy()会动态生成一个类A,内部包含了接口中定义的方法,内部会使用InvokerInvocationHandler执行相关逻辑 ``` // 动态创建实现类A的sayHello方法会执行handler的invoke方法 public abstract java.lang.String com.alibaba.dubbo.demo.DemoService.sayHello(java.lang.String){ Object[] args = new Object[1]; args[0] = ($w)$1; Object ret = handler.invoke(this, methods[0], args); return (java.lang.String)ret; } ``` 而InvokerInvocationHandler中执行invoker的invoke方法,由于不同协议生成的Invoker不同,会执行不同的调用。 ``` public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); if (method.getDeclaringClass() == Object.class) { return method.invoke(invoker, args); } if ("toString".equals(methodName) && parameterTypes.length == 0) { return invoker.toString(); } if ("hashCode".equals(methodName) && parameterTypes.length == 0) { return invoker.hashCode(); } if ("equals".equals(methodName) && parameterTypes.length == 1) { return invoker.equals(args[0]); } return invoker.invoke(new RpcInvocation(method, args)).recreate(); } ```