[dubbo 源码之 ]2. 服务消费方如何启动服务

消费者在启动之后,会通过ReferenceConfig#get()来生成远程调用代理类。在get方法中,会启动一系列调用函数,我们来一个个解析。

配置同样包含2种:

XML

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application/> <dubbo:registry address="zookeeper://127.0.0.1:2181/"/> <dubbo:reference proxy="javassist" scope="remote" generic="false" check="false" async="false" group="dubbo-sxzhongf-group" version="1.0.0" interface="com.sxzhongf.deep.in.dubbo.api.service.IGreetingService"> <dubbo:method retries="3" timeout="5000" mock="false" /> <dubbo:method retries="3" timeout="5000" mock="false" /> </dubbo:reference> </beans>

Java API

public class ApiConsumerApplication { public static void main(String[] args) { // 1. 创建服务引用对象实例 ReferenceConfig<IGreetingService> referenceConfig = new ReferenceConfig<IGreetingService>(); // 2. 设置应用程序信息 referenceConfig.setApplication(new ApplicationConfig("deep-in-dubbo-first-consumer")); // 3. 设置注册中心 referenceConfig.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181/")); // 4. 设置服务接口和超时时间 referenceConfig.setInterface(IGreetingService.class); // 默认重试3次 referenceConfig.setTimeout(5000); // 5 设置服务分组和版本 referenceConfig.setGroup("dubbo-sxzhongf-group"); referenceConfig.setVersion("1.0.0"); // 6. 引用服务 IGreetingService greetingService = referenceConfig.get(); // 7. 设置隐式参数 RpcContext.getContext().setAttachment("company", "sxzhongf"); // 获取provider端传递的隐式参数(FIXME: 需要后续追踪) // System.out.println("年龄是:" + RpcContext.getContext().getAttachment("age")); //8. 调用服务 System.out.println(greetingService.sayHello("pan")); } } 1. new ReferenceConfig

在此阶段,会初始化org.apache.dubbo.config.AbstractConfig & org.apache.dubbo.config.ReferenceConfig的静态变量以及静态代码块。

2. ReferenceConfig#get

ReferenceConfig#init

通过DubboBootstrap启动dubbo。

继而初始化服务的元数据信息,URL.buildKey(interfaceName, group, version)这段用来生成唯一服务的key,所以我们之前说dubbo的唯一标识是通过全路径和group以及version来决定的。

接下来通过org.apache.dubbo.config.utils.ConfigValidationUtils#checkMock来检查我们mock是否设置正确。

设置一系列要用的参数(系统运行参数、是否为consumer、是否为泛型调用等等),检查dubbo的注册地址,默认为当前主机IP

ReferenceConfig#createProxy 创建调用代理开始

ReferenceConfig#shouldJvmRefer首先判断是否为Injvm调用

如果不为injvm,判断是否为peer to peer端对端设置,如果为p2p,那么就直连url

检查注册中心是否存在(注册中心有可能有多个)

循环检查注册中心是否有效

配置转换URL
json registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=deep-in-dubbo-first-consumer&dubbo=2.0.2&pid=9780&refer=application%3Ddeep-in-dubbo-first-consumer%26dubbo%3D2.0.2%26group%3Ddubbo-sxzhongf-group%26interface%3Dcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%26methods%3DsayHello%2CtestGeneric%26pid%3D9780%26register.ip%3D192.168.14.99%26release%3D2.7.5%26revision%3D1.0.0%26side%3Dconsumer%26sticky%3Dfalse%26timeout%3D5000%26timestamp%3D1582959441066%26version%3D1.0.0&registry=zookeeper&release=2.7.5&timestamp=1582961922459

如果只有一个注册中心,执行REF_PROTOCOL.refer(interfaceClass, urls.get(0));来将URL转为Invoker对象,因为private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();是扩展是Adaptive,因此在这里会执行Protocol$Adaptive#refer方法,又由于protocol参数值为registry,因此会只是RegistryProtocol#refer,又由于被Wrapper类装配,因此会先执行三个Wrapper类,最后才能执行到RegistryProtocol#refer -> RegistryProtocol#doRefer,在doRefer方法中会订阅服务提供者地址,最后返回Invoker对象。!

[dubbo 源码之 ]2. 服务消费方如何启动服务


[dubbo 源码之 ]2. 服务消费方如何启动服务

那么究竟是如何生成的Invoker对象呢?我们来看下具体代码:
java private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) { // 1.可以查寻RegistryDirectory & StaticDirectory 区别 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()); //2. 封装订阅所用URL 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()); } //3.build路由规则链 directory.buildRouterChain(subscribeUrl); //4.订阅服务提供者地址 directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY)); //5.封装集群策略到虚拟invoker Invoker invoker = cluster.join(directory); return invoker; }
上述代码中,步骤1根据URL生成了一个RegistryDirectory(关于Directory接口的作用,可以自行查询一些,直白一些就是将一堆Invoker对象封装成一个List,只有2种实现RegistryDirectory & StaticDirectory,从命名可看出一个是动态可变,一个不可变),代码2 封装了订阅所要使用的参数信息,代码3则是封装绑定路由规则链,代码4订阅。代码5调用 Cluster$Adaptive#join方法生成Invoker对象。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyfdp.html