Dubbo的分支: 3.0
Dubbo的服务提供者会将RPC服务的调用说明,导出到配置中心。然后服务的消费者向配置中心订阅这些服务,也就是引用这些服务。
服务端-服务提供方-暴露/导出服务
-
dubbo根据spring的扩展api,增加了dubbo命名空间,以及各种xml元素,用来配置服务。
- 关于dubbo和spring的集成,以及spring对自定义的命名空间解析,详见NamespaceHandler,NamespaceHandlerSupport的使用。
spring boot启动,解析dubbo框架xml配置文件,装载实例,放入spring容器,到这相当于实例化bean完成。
-
dubbo增加了应用上下文监听类DubboBootstrapApplicationListener,类内覆盖onApplicationContextEvent(ApplicationContextEvent event)方法,启动dubbo。
@Override public void onApplicationContextEvent(ApplicationContextEvent event) { //使用事件监听器的方式与spring集成 if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } else if (event instanceof ContextClosedEvent) { onContextClosedEvent((ContextClosedEvent) event); } } private void onContextRefreshedEvent(ContextRefreshedEvent event) { //启动dubbo dubboBootstrap.start(); }
-
org.apache.dubbo.config.bootstrap.DubboBootstrap#start
-
如果是第一次启动的话,调用initialize()完成spi加载、配置中心初始化、事件监听等初始化。
public DubboBootstrap start() { if (started.compareAndSet(false, true)) { startup.set(false); initialize(); //start方法内有两行关键的代码,一个是服务提供方开始暴露/导出所有服务,一个是消费方开始发现/引用服务 // 1. 导出dubbo所有服务 exportServices(); // Not only provider register if (!isOnlyRegisterProvider() || hasExportedServices()) { // 2. export MetadataService exportMetadataService(); //3. Register the local ServiceInstance if required registerServiceInstance(); } //消费者引用服务 referServices(); //省略... } return this; }
-
调用exportServices暴露/导出服务。
private void exportServices() { //从配置中心拿到配置的所有service bean,依次导出。 configManager.getServices().forEach(sc -> { ServiceConfig serviceConfig = (ServiceConfig) sc; serviceConfig.setBootstrap(this); if (exportAsync) { //异步导出 ExecutorService executor = executorRepository.getServiceExporterExecutor(); Future<?> future = executor.submit(() -> { sc.export(); exportedServices.add(sc); }); asyncExportingFutures.add(future); } else { //同步导出 sc.export(); exportedServices.add(sc); } }); }
-
遍历所有服务,依次调用org.apache.dubbo.config.ServiceConfig#export,导出服务。最终调用org.apache.dubbo.config.ServiceConfig#doExportUrls,对该服务配置的暴露协议,每个协议暴露一份服务。
private void doExportUrls() { //repository.services存有所有的服务描述对象,包括默认加载的隐含服务, //如EchoService,GenericService,MonitorService,MetricsService //repository.providers存有所有的服务提供者对象 //repository.consumers存有所有的服务消费者对象 ServiceRepository repository = ApplicationModel.getServiceRepository(); //此处省略部分代码...... //获取向配置中心注册的服务url格式,即registry://xxxx List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true); for (ProtocolConfig protocolConfig : protocols) { //此处省略部分代码...... //因为一个服务可以使用多种通讯协议,所以此处是每个协议导出一份服务配置,如dubbo/http/injvm协议各一份 doExportUrlsFor1Protocol(protocolConfig, registryURLs); } }
-
导出的核心方法:org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
- 因为dubbo设计的是使用url作为配置总线,代表最终的导出格式。这个方法会先补充url信息,如失败重试retry、token、Generic服务等数据。
- 判断一个服务只要不是配置了只能暴露给远程的话,就会导出一份本地injvm服务,即调用exportLocal(url)。
- 暴露给远程。
-
单独说下导出一个服务的主要逻辑
-
这里需要先解释下dubbo设计的几个类:Protocol/Exporter/Invoker,说下这三个类的关系。
- Protocol是协议的意思,比如Dubbo、Injvm协议。一个服务要以某个协议的方式暴露出来,即可以用这个协议来访问这个服务,如HelloService以http协议的方式暴露出来,也就是以后可以用http的方式访问这个服务。
- Dubbo抽象出来一个Exporter的概念,负责如何暴露服务,如何取消服务暴露。 一个服务被暴露出来,需要知道它该怎么调用,就有了Invoker。Invoker负责实际的调用服务返回响应。
- 综上,1个Protocol包含多个服务的Exporter,1个Exporter在大部分情况下只包含1个Invoker。
- 从代码来看三者的关系:
public abstract class AbstractProtocol implements Protocol { //该协议的所有导出者,即暴露的所有服务 //"greeting/org.apache.dubbo.demo.GreetingService:1.0.0:20880" -> DubboExporter对象 //"greeting/org.apache.dubbo.demo.GreetingService:1.0.0" -> InjvmExporter对象 protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>(); //省略...... } public abstract class AbstractExporter<T> implements Exporter<T> { //每个AbstractExporter子类都会至少包含一个Invoker private final Invoker<T> invoker; //省略...... }
-
再说下这三者的组装过程:
public class ServiceConfig<T> extends ServiceConfigBase<T> { //自适应Protocol,默认以DubboProtocol为基础,按优先级在外层包装各种包装类,如:ProtocolFilterWrapper,ProtocolListenerWrapper,QosProtocolWrapper private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); //Invoker动态代理工厂,默认为JavassistProxyFactory private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); //所有导出的服务 private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>(); private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { //省略... if (CollectionUtils.isNotEmpty(registryURLs)) { for (URL registryURL : registryURLs) { //省略... //使用JavassistProxyFactory动态生成、加载invoker字节码。以GreetingService举例 //ref=greetingServiceImpl,interfaceClass=GreetingService.class,第三个参数是url=injvm://127.0.0.1/org.apache.dubbo.demo.GreetingService ?anyhost=true&application=demo-provider&bind.ip=192.168.2.3&bind.port=20880 &deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=greeting &interface=org.apache.dubbo.demo.GreetingService&mapping-type=metadata &mapping.type=metadata&metadata-type=remote&methods=hello&pid=25062&qos.port=22222 &release=&revision=1.0.0&side=provider&timeout=5000×tamp=1615949370199&version=1.0.0 //JavassistProxyFactory.getInvoker方法内部会先生成invoker wrapper包装类,目的是统一bean的方法调用。生成的wrapper类详见下面Wrapper1。 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //使用JavassistProxyFactory动态生成、加载invoker字节码。以GreetingService举例 //ref=greetingServiceImpl,interfaceClass=GreetingService.class, //第三个参数是url=injvm://127.0.0.1/org.apache.dubbo.demo.GreetingService?anyhost=true&application=demo-provider // &bind.ip=192.168.2.3&bind.port=20880 &deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=greeting // &interface=org.apache.dubbo.demo.GreetingService&mapping-type=metadata &mapping.type=metadata // &metadata-type=remote&methods=hello&pid=25062&qos.port=22222 &release=&revision=1.0.0&side=provider // &timeout=5000×tamp=1615949370199&version=1.0.0 //JavassistProxyFactory.getInvoker方法内部会先生成invoker wrapper包装类。生成的wrapper类详见下面Wrapper1。 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //将invoker以某个协议暴露出去,返回exporter //在protocol的多层包装对象中,有一个ProtocolFilterWrapper,调用export方法时,它会先判断是否为registry协议,如果不是, //需要先构造invoker的过滤器链,增强invoker功能,如MonitorFilter,TimeoutFilter,TraceFilter,ExceptionFilter等,再继续导出。 Exporter<?> exporter = PROTOCOL.export(wrapperInvoker); exporters.add(exporter); } } else { //省略,基本同上 Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = PROTOCOL.export(wrapperInvoker); exporters.add(exporter); } } }
-
服务如何暴露给远程,即向公共配置中心注册服务
上面说过我们在xml中配置一个服务,可以指定它以哪些协议的方式暴露出来,比如dubbo,injvm,http,grpc,hessian等
dubbo会遍历所有协议,每一个协议都会调用doExportUrlsFor1Protocol()来导出对应格式的服务。
dubbo会判断如果这个协议配置了暴露给远程,那么会根据当前协议的url,创建一个用于注册registry的url。如,当前是http协议,框架判断需要暴露给远程,即需要注册到公共配置中心,那么会根据http://xxx,生成一个registry://xxx的url,用于注册服务。
-
服务导出是由协议对象调用export()触发,Exporter<?> exporter = PROTOCOL.export(wrapperInvoker)。其中registry://协议是由RegistryProtocol.export负责导出。
@Override public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { //orginInvoker的url=registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?xxx //得到registryUrl=zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?xxx URL registryUrl = getRegistryUrl(originInvoker); //得到providerUrl=dubbo://192.168.2.3:20880/org.apache.dubbo.demo.GreetingService?xxx URL providerUrl = getProviderUrl(originInvoker); //省略... // decide if we need to delay publish boolean register = providerUrl.getParameter(REGISTER_KEY, true); if (register) { //立即向配置中心如zk,注册服务 register(registryUrl, registeredProviderUrl); } //省略... //Ensure that a new exporter instance is returned every time export return new DestroyableExporter<>(exporter); } private void register(URL registryUrl, URL registeredProviderUrl) { //根据url协议头,获取配置中心注册器对象,得到ZookeeperRegistry Registry registry = registryFactory.getRegistry(registryUrl); //调用ZookeeperRegistry的注册方法,内部为使用CuratorZookeeperClient向配置中心写服务数据 //到这里也就完成了将服务注册到公共配置中心,暴露给远程了 registry.register(registeredProviderUrl); }
到此服务暴露完毕。
客户端-服务消费方-发现/引用服务
先需要理解消费方的几个重要概念
- 先举个例子,借着例子来理解。假设在北京和上海分别有一个注册中心zk-beijing,zk-shanghai。在北京有三台机器,ip分别为1.1.1.1/2.2.2.2/3.3.3.3。其中1.1.1.1为消费者,2.2.2.2和3.3.3.3为服务提供者。2.2.2.2提供ServiceA,ServiceB两个服务,3.3.3.3提供ServiceA,ServiceC两个服务。
-
我们先简化环境,看下只有一个注册中心的情况,即只向zk-beijing注册。在2.2.2.2/3.3.3.3启动初始化时,会向注册中心注册暴露服务,路径为:
//zookeeper路径结构 /dubbo /ServiceA /providers /URL.encode(dubbo://2.2.2.2:20880/ServiceA?anyhost=true&application=demo-provider&delay=5000&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=ServiceA&mapping-type=metadata&mapping.type=metadata&metadata-type=remote&methods=sayHello,sayHelloAsync&pid=37004&release=&side=provider&timeout=3000×tamp=1617773391401) /URL.encode(dubbo://3.3.3.3:20880/ServiceA?anyhost=true&application=demo-provider&delay=5000&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=ServiceA&mapping-type=metadata&mapping.type=metadata&metadata-type=remote&methods=sayHello,sayHelloAsync&pid=35111&release=&side=provider&timeout=3000×tamp=1617773391507) /ServiceB /providers /URL.encode(rest://2.2.2.2:80/ServiceB?anyhost=true&application=demo-provider&delay=5000&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=ServiceB&mapping-type=metadata&mapping.type=metadata&metadata-type=remote&methods=buy,sell&pid=37004&release=&revision=1.0.0&side=provider&timeout=5000×tamp=1617773396526&version=1.0.0) /ServiceC /providers /URL.encode(dubbo://3.3.3.3:20880/ServiceC?anyhost=true&application=demo-provider&delay=5000&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=ServiceC&mapping-type=metadata&mapping.type=metadata&metadata-type=remote&methods=go,back&pid=351111&release=&side=provider&timeout=3000×tamp=1617773391507)
从路径可以看出,dubbo不是以一个机器为基本单位,暴露该机器下有哪些服务的,如:/dubbo/2.2.2.2/我的服务xxx。dubbo是以服务为单位来暴露。因为我们引用都是引用哪个服务,暴露也应该是暴露哪个服务,服务下标明哪些机器上有该服务。你需要引用哪些服务就可以随意的引用哪些,不需要引用整个机器暴露出来的所有服务。假如一个服务暴露在多台机器上,就更是个问题了。
再看多注册中心,假如有另外几台机器,向shanghai注册,情况和beijing差不多。这种多注册中心存在时,可以看做有两个异地服务集群,比如HelloService有北京和上海两个服务集群。
-
概念一:Invoker
- Invoker是什么:从zk树上看,/providers下的每一个服务url在消费方都会对应一个Invoker。如果1.1.1.1引用了这三个服务,那就会创建4个对应的Invoker。一个Invoker表示一个消费方持有的,对一个提供方某一个服务的引用。Invoker内包含连接池属性字段ExchangeClient[] clients。因为ClusterInvoker的存在,为了区分,Invoker也叫做普通Invoker。
概念二:Directory
- Directory就是多个Invoker的集合,实现类一般会有一个List<Invoker<T>>属性字段,存储Invoker。为了方便表示以及操作多Invoker,抽象出来的概念。
概念三:Router
- Router路由。在实际使用中,可能由于我们部署的物理机性能有差别,有几台机器性能特别好,或者说为了测试某几台机器上的服务,要求请求更多的或者全部路由到某几台机器,Router负责的就是这个路由逻辑。Router会从Directory中遍历选出与路由规则匹配的invoker子集,然后交给负载均衡器。
概念四:LoadBalance
- LoadBalance(负载均衡器)接收Router筛选出来的可以使用的invoker集合,从中选出一个普通invoker,用于发起实际调用。
概念五:ClusterInvoker
- ClusterInvoker就是实际的某一个物理集群了,如HelloService服务的北京集群。
概念六:Cluster
- Cluster是对实际物理集群抽象出的最上层概念,即某一个服务的所有物理集群的集合叫做Cluster。如HelloService的北京和上海两个异地物理集群合起来叫做Cluster。
总结
- HelloService服务部署在两个异地集群,一个北京,一个上海。两地各自都有一个注册中心zk。
- 自下而上的组装过程:
- dubbo会从北京的注册中心zk读取HelloService的所有提供者,一个提供者封装成一个普通Invoker。
- 将多个普通Invoker存储到一个Directory中。
- 根据业务特性,创建一个对应的集群执行器对象ClusterInvoker。选择适合的Router和LoadBalance,将Directory传入。到此北京集群的invoker封装好了。
- 重复上述步骤,将上海集群也封装成一个ClusterInvoker。
- 将北京、上海ClusterInvoker封装到多注册中心Cluster对象中。
- 服务以Cluster为入口,对外提供服务。
- 自上而下的调用过程:
Cluster会根据订阅的各个注册中心的配置、与服务消费方是否处于同一个物理集群、权重等先选出来一个调用的ClusterInvoker,即先选出来一个物理集群。假如选出来的是北京集群。
然后在北京集群ClusterInvoker执行Router路由,选出匹配的普通Invoker集合。
在路由后的Invoker集合上,执行负载均衡,由LoadBalance选出一个最终用来执行RPC的普通Invoker。
-
执行Invoker.invoke(Invocation invocation),发起调用。
//从代码看下调用过程 public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> { public Result invoke(final Invocation invocation) throws RpcException { //省略 //使用路由器Router从ClusterInvoker.directory存储的所有的invoker中,路由出符合路由规则的一批invoker。 List<Invoker<T>> invokers = list(invocation); //上面选出了方向正确的一批invoker,现在需要负载均衡选一个执行了。此处初始化负载均衡器LoadBalance。 LoadBalance loadbalance = initLoadBalance(invokers, invocation); //为异步执行附着调用id RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); //使用负载均衡器选一个invoker,发起调用,返回响应。 return doInvoke(invocation, invokers, loadbalance); } }
消费方发现/引用服务的过程
- Dubbo是严重依赖Spring框架的,我们在xml配置了java interface > HelloService的Bean声明和引用关系后,如何在服务消费方生成、加载该接口的实现类,创建实例对象,纳入到Spring容器管理,在需要的地方注入。
- Dubbo是由FactoryBean入手的,过程与MyBatis的Mapper类生成过程类似。(注:3.0分支中,只有延迟加载的代理对象是使用FactoryBean功能生成的,普通代理对象是使用ReferenceConfig.get()生成的。而在master分支中,所有代理对象都是使用FactoryBean生成,底层实现也是调用的ReferenceConfig.get())
- 下面说的是3.0分支普通代理对象的生成过程,没有使用FactoryBean。
-
与服务暴露一样,服务引用也是使用事件监听的方式与spring集成,都会调用的org.apache.dubbo.config.bootstrap.DubboBootstrap#start方法,该方法上面说过,有两个关键的点,一个是exportServices():提供方开始暴露/导出所有服务,另一个是referServices():消费方开始发现/引用服务。
public DubboBootstrap start() { if (started.compareAndSet(false, true)) { startup.set(false); initialize(); //start方法内有两行关键的代码,一个是服务提供方开始暴露/导出所有服务,一个是消费方开始发现/引用服务 // 1. 导出dubbo所有服务 exportServices(); // Not only provider register if (!isOnlyRegisterProvider() || hasExportedServices()) { // 2. export MetadataService exportMetadataService(); //3. Register the local ServiceInstance if required registerServiceInstance(); } //消费者引用服务 referServices(); //省略... } return this; }
-
referServices()中会触发缓存数据的初始化、加载。
private void referServices() { //configManager.getReferences()拿到的是对xml中配置的所有<dubbo:reference xxx/> //解析后封装的ReferenceConfig对象 configManager.getReferences().forEach(rc -> { //省略... //cache为ReferenceConfigCache类型,底层是一个ConcurrentHashMap, //调用get会初始化对应的ReferenceConfig对象,放入缓存中 cache.get(rc); }); }
-
ReferenceConfigCache.get(rc),从缓存拿消费方引用的服务代理对象,如果没有触发实现类的生成、加载,然后创建代理类放入cache。
public <T> T get(ReferenceConfigBase<T> referenceConfig) { //key=org.apache.dubbo.demo.DemoService String key = generator.generateKey(referenceConfig); //type=DemoService.class Class<?> type = referenceConfig.getInterfaceClass(); //proxies结构=< DemoService.class, <"org.apache.dubbo.demo.DemoService", DemoService实现类代理对象> > proxies.computeIfAbsent(type, _t -> new ConcurrentHashMap<>()); //proxiesOfType结构=<"org.apache.dubbo.demo.DemoService", DemoService实现类代理对象> ConcurrentMap<String, Object> proxiesOfType = proxies.get(type); proxiesOfType.computeIfAbsent(key, _k -> { //由ReferenceConfig配置对象触发对应服务接口实现类的创建、加载,以及代理对象的创建。 //proxy就是在消费方引用的服务代理对象 Object proxy = referenceConfig.get(); referredReferences.put(key, referenceConfig); return proxy; }); return (T) proxiesOfType.get(key); }
-
关键的来了,ReferenceConfig类包含了生成、加载实现类,创建代理对象的主要逻辑。
public class ReferenceConfig<T> extends ReferenceConfigBase<T> { //缺省FailoverCluster private static final Cluster CLUSTER = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension(); //缺省JavassistProxyFactory private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); //根据url协议类型决定,url=registry://xxx,对应RegistryProtocol。url=dubbo://xxx,对应DubboProtocol。url=injvm://xxx,对应InjvmProtocol。 private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); //ClusterInvoker类型 private transient volatile Invoker<?> invoker; //直连提供者地址或者注册中心地址 protected final List<URL> urls = new ArrayList<URL>(); //引用的代理对象 private transient volatile T ref; //1.生成的代理对象会set到属性字段中,为null的话,开始引用初始化。 public synchronized T get() { if (ref == null) { //触发引用初始化 init(); } return ref; } //2. 初始化 public synchronized void init() { if (initialized) return; //解析各种消费方配置,放入map //省略... //map={"mapping-type":"metadata","init":"false","side":"consumer","register.ip":"192.168.2.3", // "release":"","methods":"sayHello,sayHelloAsync","qos.port":"33333","provided-by":"demo-provider", // "dubbo":"2.0.2","pid":"35116","check":"true","interface":"org.apache.dubbo.demo.DemoService", // "enable.auto.migration":"true","mapping.type":"metadata","metadata-type":"remote", // "application":"demo-consumer","sticky":"false","timestamp":"1617676272015","enable-auto-migration":"true"} //为org.apache.dubbo.demo.DemoService接口创建消费方代理对象 ref = createProxy(map); //分发ReferenceConfigInitializedEvent事件 dispatch(new ReferenceConfigInitializedEvent(this, invoker)); } //3.创建动态代理对象 private T createProxy(Map<String, String> map) { //从配置中找出是否应该引用同一JVM中的服务。默认行为为true if (shouldJvmRefer(map)) { //injvm本地引用 URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map); //InjvmProtocol.refer返回InjvmInvoker类型 invoker = REF_PROTOCOL.refer(interfaceClass, url); } else { //远程引用 //url为配置的peer-to-peer调用地址,或者注册中心地址 //假如服务集群有5台机器,你只想测试调用其中的A,B两台机器,配置的就是直连提供者AB的地址 //<dubbo:reference id="as" interface="AService" url="dubbo://1.1.1.1:20890;第二提供者地址"/> if (urls.size() == 1) { //一个服务只有一个url,有两种情况:1.只有一个注册中心;2.直连一个提供者。 //这里返回的invoker一定是ClusterInvoker子类型,默认为FailoverClusterInvoker, //注册中心的提供者或者直连url都会转为普通invoker->directory,再set到ClusterInvoker中返回 //如果url.protocol=service-discovery-registry,对应的protocol为:RegistryProtocol invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0)); } else { //一个服务多个url,可能有三种情况:1.多个注册中心;2.直连多个提供者。3.两者共存,即url配了注册中心地址和直连提供者地址,这种情况不确定可不可以,没试出来。 //如果url中有注册协议,即从注册中心订阅服务,返回的invoker为ZoneAwareClusterInvoker类型 //无注册协议时,返回FailoverClusterInvoker if (registryURL != null) { String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME); invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers)); } else { String cluster = "省略..."; invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers)); } } } //JavassistProxyFactory.getProxy return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic)); } }
到这就完成了消费者引用服务代理对象,其中Cluster和Invoker的组装过程,就是上面提到的。
-
补充下master分支中,改版的使用FactoryBean生成代理对象逻辑:
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { @Override public Object getObject() { //调用ReferenceConfig.get()生成代理对象 return get(); } @Override public Class<?> getObjectType() { //返回服务接口class return getInterfaceClass(); } }