dubbo源码之服务引用

╰半橙微兮° 2022-12-08 14:25 229阅读 0赞

在这里插入图片描述

这个是dubbo 调用的流程图,这篇文件主要要介绍第二步中的服务订阅和第四步代理类的封装过程,这次使用的源码为2.7.1

服务引用大致流程

在这里插入图片描述

Provider将自己的服务暴露出来,注册到注册中心, Consumer从注册中心得知 Provider 的信息,然后自己封装一个调用类对Provider 实现调用。这次使用的源码为2.7.1,采用注解方式引入。dubbo入门

服务引用大致流程

通过前面文章可以看到,我们这次是采用注解方式引入。因此在初始化引入对应接口类进行属性赋值的时候,会将服务引入。
在这里插入图片描述

这一部分涉及到spring bean生命周期的内容,故在这个暂时不展开。我们直接从
ReferenceAnnotationBeanPostProcessor 类 的doGetInjectedBean 方法中展开。
在这里插入图片描述

通过调用这一步构建 buildReferencedBeanName 构建出名字依赖的名字
consumers:dubbo:service.ProviderService:1.0.0:dev,作为判断容器是否存在的依据。

ReferenceAnnotationBeanPostProcessor #buildInvocationHandler 方法

  1. private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) {
  2. //通过引用名字构建hander(这个时候的handler没有调用信息)
  3. ReferenceAnnotationBeanPostProcessor.ReferenceBeanInvocationHandler handler = (ReferenceAnnotationBeanPostProcessor.ReferenceBeanInvocationHandler)this.localReferenceBeanInvocationHandlerCache.get(referencedBeanName);
  4. if (handler == null) {
  5. handler = new ReferenceAnnotationBeanPostProcessor.ReferenceBeanInvocationHandler(referenceBean);
  6. }
  7. //由于第一次调用,容器中并不存在该引用
  8. if (this.applicationContext.containsBean(referencedBeanName)) {
  9. this.localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler);
  10. } else {
  11. //初始该应用
  12. handler.init();
  13. }
  14. return handler;
  15. }

ReferenceAnnotationBeanPostProcessor 中的内部类ReferenceBeanInvocationHandler的init()方法

this.referenceBean 的类为ReferenceBean ,其get() 方法。是其父类ReferenceConfig 的方法
在这里插入图片描述
这个里面最终调用了ReferenceConfig 中的init方法
在这里插入图片描述

在这里插入图片描述

上面是处理器的调用调用过程,具体invoker的核心逻辑在ReferenceConfig 中init方法中

ReferenceConfig 中init

  1. private void init() {
  2. //如果没有初始化
  3. if (!this.initialized) {
  4. 。。。(省略部分代码)这部分是构建map
  5. //构建代理对象
  6. this.ref = this.createProxy(map);
  7. String serviceKey = URL.buildKey(this.interfaceName, this.group, this.version);
  8. ApplicationModel.initConsumerModel(serviceKey, this.buildConsumerModel(serviceKey, attributes));
  9. }
  10. }

在这里插入图片描述

ReferenceConfig# createProxy代理对象

  1. private T createProxy(Map<String, String> map) {
  2. URL metadataReportService;
  3. URL registryURL;
  4. //判断是否需要内部引用。通过直接走本地协议的 URL 然后进行服务的引入
  5. if (this.shouldJvmRefer(map)) {
  6. metadataReportService = (new URL("injvm", "127.0.0.1", 0, this.interfaceClass.getName())).addParameters(map);
  7. this.invoker = refprotocol.refer(this.interfaceClass, metadataReportService);
  8. if (logger.isInfoEnabled()) {
  9. logger.info("Using injvm service " + this.interfaceClass.getName());
  10. }
  11. } else {
  12. URL u;
  13. URL url;
  14. //如果不是本地,那肯定是远程了,接下来就是判断是点对点直连 provider 还是通过注册中心拿到 //provider 信息再连接 provider 了,如果配置了 url 的情况,如果配置了 url 那么不是直连的地址
  15. //,就是注册中心的地址
  16. if (this.url != null && this.url.length() > 0) {
  17. String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(this.url);
  18. if (us != null && us.length > 0) {
  19. String[] var11 = us;
  20. int var14 = us.length;
  21. for(int var16 = 0; var16 < var14; ++var16) {
  22. String u = var11[var16];
  23. URL url = URL.valueOf(u);
  24. if (StringUtils.isEmpty(url.getPath())) {
  25. url = url.setPath(this.interfaceName);
  26. }
  27. //判断是否是注册中心
  28. if ("registry".equals(url.getProtocol())) {
  29. //如果是注册中心地址将map转化为map查询字符串,并作为refer参数添加到url中
  30. this.urls.add(url.addParameterAndEncoded("refer", StringUtils.toQueryString(map)));
  31. } else {
  32. //如果是点对点会合并url,移除服务提供者一下参数
  33. this.urls.add(ClusterUtils.mergeUrl(url, map));
  34. }
  35. }
  36. }
  37. } else {
  38. //如果没有配置地址,则根据map注册url
  39. this.checkRegistry();
  40. //服务暴露同样是通过这个方法加载配置,ture 代表为是服务提供者,false 代表服务消费者
  41. List<URL> us = this.loadRegistries(false);
  42. if (CollectionUtils.isNotEmpty(us)) {
  43. for(Iterator var3 = us.iterator(); var3.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {
  44. u = (URL)var3.next();
  45. url = this.loadMonitor(u);
  46. if (url != null) {
  47. map.put("monitor", URL.encode(url.toFullString()));
  48. }
  49. }
  50. }
  51. if (this.urls.isEmpty()) {
  52. throw new IllegalStateException("No such any registry to reference " + this.interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
  53. }
  54. }
  55. //构建的url参数
  56. //registry://192.168.25.128:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-consumer&dubbo=2.0.2&pid=317360&qos.enable=false&refer=application%3Ddubbo-consumer%26default.lazy%3Dfalse%26default.sticky%3Dfalse%26default.timeout%3D30000%26dubbo%3D2.0.2%26group%3Ddev%26interface%3Dservice.ProviderService%26lazy%3Dfalse%26methods%3DgetProviderData%26pid%3D317360%26qos.enable%3Dfalse%26register.ip%3D192.168.1.101%26release%3D2.7.1%26revision%3D1.0.0%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1600589714223%26version%3D1.0.0&registry=zookeeper&release=2.7.1&timeout=4000&timestamp=1600591668151
  57. if (this.urls.size() == 1) {
  58. this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
  59. } else {
  60. List<Invoker<?>> invokers = new ArrayList();
  61. registryURL = null;
  62. Iterator var15 = this.urls.iterator();
  63. while(var15.hasNext()) {
  64. url = (URL)var15.next();
  65. invokers.add(refprotocol.refer(this.interfaceClass, url));
  66. if ("registry".equals(url.getProtocol())) {
  67. registryURL = url;
  68. }
  69. }
  70. if (registryURL != null) {
  71. u = registryURL.addParameter("cluster", "registryaware");
  72. this.invoker = cluster.join(new StaticDirectory(u, invokers));
  73. } else {
  74. this.invoker = cluster.join(new StaticDirectory(invokers));
  75. }
  76. }
  77. }
  78. if (this.shouldCheck() && !this.invoker.isAvailable()) {
  79. this.initialized = false;
  80. throw new IllegalStateException("Failed to check the status of the service " + this.interfaceName + ". No provider available for the service " + (this.group == null ? "" : this.group + "/") + this.interfaceName + (this.version == null ? "" : ":" + this.version) + " from the url " + this.invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
  81. } else {
  82. if (logger.isInfoEnabled()) {
  83. logger.info("Refer dubbo service " + this.interfaceClass.getName() + " from url " + this.invoker.getUrl());
  84. }
  85. metadataReportService = null;
  86. MetadataReportService metadataReportService;
  87. if ((metadataReportService = this.getMetadataReportService()) != null) {
  88. registryURL = new URL("consumer", (String)map.remove("register.ip"), 0, (String)map.get("interface"), map);
  89. metadataReportService.publishConsumer(registryURL);
  90. }
  91. return proxyFactory.getProxy(this.invoker);
  92. }
  93. }

这其实就是ReferenceConfig 中init整个流程了,先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过 URL 上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。

在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。

然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。

在这里插入图片描述

refprotocol.refer

在这里插入图片描述

这个时候协议是registry ,故先调用是RegistryProtocol#refer

  1. public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
  2. url = URLBuilder.from(url).setProtocol(url.getParameter("registry", "dubbo")).removeParameter("registry").build();
  3. Registry registry = this.registryFactory.getRegistry(url);
  4. if (RegistryService.class.equals(type)) {
  5. return this.proxyFactory.getInvoker(registry, type, url);
  6. } else {
  7. Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded("refer"));
  8. String group = (String)qs.get("group");
  9. //根据是否有group 调用不同cluster调用doRefer
  10. return group == null || group.length() <= 0 || Constants.COMMA_SPLIT_PATTERN.split(group).length <= 1 && !"*".equals(group) ? this.doRefer(this.cluster, registry, type, url) : this.doRefer(this.getMergeableCluster(), registry, type, url);
  11. }
  12. }

RegistryProtocol#doRefer 进行真正的引用

  1. private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
  2. RegistryDirectory<T> directory = new RegistryDirectory(type, url);
  3. //塞入注册中心
  4. directory.setRegistry(registry);
  5. directory.setProtocol(this.protocol);
  6. Map<String, String> parameters = new HashMap(directory.getUrl().getParameters());
  7. //生成服务消费者链接
  8. URL subscribeUrl = new URL("consumer", (String)parameters.remove("register.ip"), 0, type.getName(), parameters);
  9. if (!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {
  10. //想注册中心注入服务消费者,在consumers创建新节点
  11. directory.setRegisteredConsumerUrl(this.getRegisteredConsumerUrl(subscribeUrl, url));
  12. registry.register(directory.getRegisteredConsumerUrl());
  13. }
  14. directory.buildRouterChain(subscribeUrl);
  15. //在订阅注册中的providers目录、configurators 和routers目录
  16. directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
  17. Invoker invoker = cluster.join(directory);
  18. ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
  19. return invoker;
  20. }

Conusmer 是在 RegistryProtocol#refer 中向注册中心注册自己的信息,并且订阅 Provider 和配置的一些相关信息

封装之后invoker
在这里插入图片描述

DubboProtocol#refer

具体调用哪个 protocol 还是得看 URL的协议的,我们这里是 dubbo 协议
在这个getClients(url) 中有实例类型为 ExchangeClient,底层依赖 Netty 来进行网络通信,这里不再深入了

  1. public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
  2. this.optimizeSerialization(url);
  3. //拿到url创建client 在这个getClients(url) 中有实例类型为 ExchangeClient,底层依赖 Netty 来进行网络通信,这里不再深入了
  4. DubboInvoker<T> invoker = new DubboInvoker(serviceType, url, this.getClients(url), this.invokers);
  5. this.invokers.add(invoker);
  6. return invoker;
  7. }

在这里插入图片描述

这个是refprotocol.refer 这一段的流程图,我们将整个流程图贴出来大家参考一下
在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,229人围观)

还没有评论,来说两句吧...

相关阅读