服务引用无非就是做了两件事

  • 将spring的schemas标签信息转换bean,然后通过这个bean的信息,连接、订阅zookeeper节点信息创建一个invoker

  • invoker的信息创建一个动态代理对象

时序图:

最终返回一个被调用接口的动态代理对象。

在调用代理对象的方法时,会进入InvokerInvocationHandle类的逻辑。

跟踪源码的时候,发现消费端调用invoke的时候要调用一连串的Invoker实现类,一直纠结这些Invoker是用来做什么的?

Invoker的创建应该是入口,也就是从referenceConfig类开始

然后找到RegistryProtocol.doRefer方法

  1. private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
  2. RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
  3. directory.setRegistry(registry);
  4. directory.setProtocol(protocol);
  5. URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
  6. if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
  7. && url.getParameter(Constants.REGISTER_KEY, true)) {
  8. registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
  9. Constants.CHECK_KEY, String.valueOf(false)));
  10. }
  11.  
  12. directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
  13. Constants.PROVIDERS_CATEGORY
  14. + "," + Constants.CONFIGURATORS_CATEGORY
  15. + "," + Constants.ROUTERS_CATEGORY));
  16.  
  17. return cluster.join(directory);
  18. }

也就是这一行:
cluster.join(directory);

在执行join方法的时候,会通过SPI机制找到cluster的扩展实例,默认的时候FailoverCluster
但是调试发现第一步创建的实例化对象是MockClusterWrapper类而不是FailoverCluster
查阅资料 dubbo中的mock机制 再结合源码总结如下:
在dubbo的配置文件  classpath:/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.Cluster中,failover对应的是FailoverCluster类:
但是ExtensionLoader在实例化对象时,有个比较特殊的地方,那就是在实例化完成之后,会自动套上当前的ExtensionLoader中的Wrapper类,上面的mock所对应的MockClusterWrapper就是这样的一个Wrapper:也就是实例化出来的FailoverCluster会被套上一层MockClusterWrapper,总结一下就是:
Cluster$Adaptive -> 定位到内部key为failover的对象 ->FailoverCluster->外部套上MockClusterWrapper
 
所以时序图是这样的:
 
官网集群容错介绍图:
 
根据以上时序图查看源码如下:
MockClusterInvoker.java

  1. public Result invoke(Invocation invocation) throws RpcException {
  2. Result result = null;
  3.  
  4. String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
  5. if (value.length() == 0 || value.equalsIgnoreCase("false")) {
  6. //no mock
  7. //执行到这一行的时候开始进入集群 cluster -> AbstractClusterInvoker
  8. result = this.invoker.invoke(invocation);
  9. } else if (value.startsWith("force")) {
  10. if (logger.isWarnEnabled()) {
  11. logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
  12. }
  13. //force:direct mock
  14. result = doMockInvoke(invocation, null);
  15. } else {
  16. //fail-mock
  17. try {
  18. result = this.invoker.invoke(invocation);
  19. } catch (RpcException e) {
  20. if (e.isBiz()) {
  21. throw e;
  22. } else {
  23. if (logger.isWarnEnabled()) {
  24. logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
  25. }
  26. result = doMockInvoke(invocation, e);
  27. }
  28. }
  29. }
  30. return result;
  31. }

AbstractClusterInvoker.java

  1. public Result invoke(final Invocation invocation) throws RpcException {
  2. checkWhetherDestroyed();
  3.  
  4. // binding attachments into invocation.
  5. Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
  6. if (contextAttachments != null && contextAttachments.size() != 0) {
  7. ((RpcInvocation) invocation).addAttachments(contextAttachments);
  8. }
  9.  
  10. //选择出可用的invoker集合
  11. List<Invoker<T>> invokers = list(invocation);
  12. // 初始化负载均衡策略
  13. LoadBalance loadbalance = initLoadBalance(invokers, invocation);
  14. RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
  15. return doInvoke(invocation, invokers, loadbalance);
  16. }
  17.  
  18. protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
  19. // -> AbstractDirectory.java
  20. return directory.list(invocation);
  21. }

AbstractDirectory.java

  1. public List<Invoker<T>> list(Invocation invocation) throws RpcException {
  2. if (destroyed) {
  3. throw new RpcException("Directory already destroyed .url: " + getUrl());
  4. }

  5. // 模板方法,由子类实现
    // -> RegistryDirectory.java 或者 StaticDirectory.java
  6. List<Invoker<T>> invokers = doList(invocation);
  7. List<Router> localRouters = this.routers; // local reference
  8. if (localRouters != null && !localRouters.isEmpty()) {
  9. for (Router router : localRouters) {
  10. try {
  11. if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
  12. //将invokers返回后,下面来到了Router,开始进入路由,现在我们到了序号6,此时到了MockInvokersSelector类,
  13. //他是Router接口的实现类,从官网的介绍图中我们也可以看到Router分为Script和Condition两种,翻译过来也就是脚本路由和条件路由
  14. invokers = router.route(invokers, getConsumerUrl(), invocation);
  15. }
  16. } catch (Throwable t) {
  17. logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
  18. }
  19. }
  20. }
  21. return invokers;
  22. }

RegistryDirectory.java

  1. public List<Invoker<T>> doList(Invocation invocation) {
  2. if (forbidden) {
  3. // 1. No service provider 2. Service providers are disabled
  4. throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,
  5. "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost()
  6. + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
  7. }
  8. List<Invoker<T>> invokers = null;
  9. Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
  10. if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
  11. String methodName = RpcUtils.getMethodName(invocation);
  12. Object[] args = RpcUtils.getArguments(invocation);
  13. if (args != null && args.length > 0 && args[0] != null
  14. && (args[0] instanceof String || args[0].getClass().isEnum())) {
  15. invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // The routing can be enumerated according to the first parameter
  16. }
  17. if (invokers == null) {
  18. invokers = localMethodInvokerMap.get(methodName);
  19. }
  20. if (invokers == null) {
  21. invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
  22. }
  23. if (invokers == null) {
  24. Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
  25. if (iterator.hasNext()) {
  26. invokers = iterator.next();
  27. }
  28. }
  29. }
  30. return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
  31. }

MockInvokersSelector.java

  1. public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
  2. URL url, final Invocation invocation) throws RpcException {
  3. if (invocation.getAttachments() == null) {
  4. return getNormalInvokers(invokers);
  5. } else {
  6. String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
  7. if (value == null) {
  8. //拿到能正常执行的invokers,并将其返回
  9. return getNormalInvokers(invokers);
  10. } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
  11. return getMockedInvokers(invokers);
  12. }
  13. }
  14. return invokers;
  15. }
  16.  
  17. private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers) {
  18. if (!hasMockProviders(invokers)) {
  19. return invokers;
  20. } else {
  21. List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());
  22. for (Invoker<T> invoker : invokers) {
  23. if (!invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
  24. sInvokers.add(invoker);
  25. }
  26. }
  27. return sInvokers;
  28. }
  29. }
上面出现的这两个关键词,其实无非就是做两件事
在Directory中找出本次集群中的全部invokers
在Router中,将上一步的全部invokers挑选出能正常执行的invokers
回到AbstractClusterInvoker.java
 
  1. ......
  2. //选择出可用的invoker集合
  3. List<Invoker<T>> invokers = list(invocation);
  4. // 初始化负载均衡策略
  5. LoadBalance loadbalance = initLoadBalance(invokers, invocation);
  6. RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
  7. return doInvoke(invocation, invokers, loadbalance);
从上面步骤已经挑选出能正常执行的invokers了,但是假如2个做集群,但是这两个都是正常的,到底要执行哪一个呢?
 根据官网的描述
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
所以这个时候是到了FailoverClusterInvoker类,但是如果你配置的是Failfast Cluster(快速失败),Failsafe Cluster(失败安全),Failback Cluster(失败自动恢复),Forking Cluster(并行调用多个服务器,只要一个成功即返回),Broadcast Cluster(广播调用所有提供者,逐个调用,任意一台报错则报错)他也会到达相应的类
FailoverClusterInvoker.java
  1. public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
  2. List<Invoker<T>> copyinvokers = invokers;
  3. checkInvokers(copyinvokers, invocation);
  4. String methodName = RpcUtils.getMethodName(invocation);
  5. int len = getUrl().getMethodParameter(methodName, Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
  6. if (len <= 0) {
  7. len = 1;
  8. }
  9. // retry loop.
  10. RpcException le = null; // last exception.
  11. List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
  12. Set<String> providers = new HashSet<String>(len);
  13. for (int i = 0; i < len; i++) {
  14. //Reselect before retry to avoid a change of candidate `invokers`.
  15. //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
  16. if (i > 0) {
  17. checkWhetherDestroyed();
  18. copyinvokers = list(invocation);
  19. // check again
  20. checkInvokers(copyinvokers, invocation);
  21. }
  22. // 通过负载均衡算法选择一个Invoker,然后调用
  23. Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
  24. invoked.add(invoker);
  25. RpcContext.getContext().setInvokers((List) invoked);
  26. try {
  27. Result result = invoker.invoke(invocation);
  28. if (le != null && logger.isWarnEnabled()) {
  29. logger.warn("Although retry the method " + methodName
  30. + " in the service " + getInterface().getName()
  31. + " was successful by the provider " + invoker.getUrl().getAddress()
  32. + ", but there have been failed providers " + providers
  33. + " (" + providers.size() + "/" + copyinvokers.size()
  34. + ") from the registry " + directory.getUrl().getAddress()
  35. + " on the consumer " + NetUtils.getLocalHost()
  36. + " using the dubbo version " + Version.getVersion() + ". Last error is: "
  37. + le.getMessage(), le);
  38. }
  39. return result;
  40. } catch (RpcException e) {
  41. if (e.isBiz()) { // biz exception.
  42. throw e;
  43. }
  44. le = e;
  45. } catch (Throwable e) {
  46. le = new RpcException(e.getMessage(), e);
  47. } finally {
  48. providers.add(invoker.getUrl().getAddress());
  49. }
  50. }
  51. throw new RpcException(le.getCode(), "Failed to invoke the method "
  52. + methodName + " in the service " + getInterface().getName()
  53. + ". Tried " + len + " times of the providers " + providers
  54. + " (" + providers.size() + "/" + copyinvokers.size()
  55. + ") from the registry " + directory.getUrl().getAddress()
  56. + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
  57. + Version.getVersion() + ". Last error is: "
  58. + le.getMessage(), le.getCause() != null ? le.getCause() : le);
  59. }
  60.  
  61. protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
  62. if (invokers == null || invokers.isEmpty()) {
  63. return null;
  64. }
  65. String methodName = invocation == null ? "" : invocation.getMethodName();
  66.  
  67. boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName, Constants.CLUSTER_STICKY_KEY, Constants.DEFAULT_CLUSTER_STICKY);
  68. {
  69. //ignore overloaded method
  70. if (stickyInvoker != null && !invokers.contains(stickyInvoker)) {
  71. stickyInvoker = null;
  72. }
  73. //ignore concurrency problem
  74. if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))) {
  75. if (availablecheck && stickyInvoker.isAvailable()) {
  76. return stickyInvoker;
  77. }
  78. }
  79. }
  80. Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected);
  81.  
  82. if (sticky) {
  83. stickyInvoker = invoker;
  84. }
  85. return invoker;
  86. }
  87.  
  88. private Invoker<T> doSelect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
  89. if (invokers == null || invokers.isEmpty())
  90. return null;
  91. if (invokers.size() == 1)
  92. return invokers.get(0);
  93. Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
  94.  
  95. //If the `invoker` is in the `selected` or invoker is unavailable && availablecheck is true, reselect.
  96. if ((selected != null && selected.contains(invoker))
  97. || (!invoker.isAvailable() && getUrl() != null && availablecheck)) {
  98. try {
  99. Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
  100. if (rinvoker != null) {
  101. invoker = rinvoker;
  102. } else {
  103. //Check the index of current selected invoker, if it's not the last one, choose the one at index+1.
  104. int index = invokers.indexOf(invoker);
  105. try {
  106. //Avoid collision
  107. invoker = index < invokers.size() - 1 ? invokers.get(index + 1) : invokers.get(0);
  108. } catch (Exception e) {
  109. logger.warn(e.getMessage() + " may because invokers list dynamic change, ignore.", e);
  110. }
  111. }
  112. } catch (Throwable t) {
  113. logger.error("cluster reselect fail reason is :" + t.getMessage() + " if can not solve, you can set cluster.availablecheck=false in url", t);
  114. }
  115. }
  116. return invoker;
  117. }
在这个集群容错的整体架构过程中,dubbo其实也就是三件事
1.在Directory中找出本次集群中的全部invokers
2.在Router中,将上一步的全部invokers挑选出能正常执行的invokers
3.在LoadBalance中,将上一步的能正常的执行invokers中,根据配置的负载均衡策略,挑选出需要执行的invoker
 

dubbo服务引用与集群容错的更多相关文章

  1. Dubbo 源码分析 - 集群容错之 LoadBalance

    1.简介 LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载"均摊"到不同的机器上.避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况.通 ...

  2. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  3. Dubbo 源码分析 - 集群容错之 Router

    1. 简介 上一篇文章分析了集群容错的第一部分 -- 服务目录 Directory.服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由.上一篇文章关于服务路由相关逻辑没有 ...

  4. Dubbo 源码分析 - 集群容错之 Directory

    1. 简介 前面文章分析了服务的导出与引用过程,从本篇文章开始,我将开始分析 Dubbo 集群容错方面的源码.这部分源码包含四个部分,分别是服务目录 Directory.服务路由 Router.集群 ...

  5. Dubbo工作原理,集群容错,负载均衡

    Remoting:网络通信框架,实现了sync-over-async和request-response消息机制. RPC:一个远程过程调用的抽象,支持负载均衡.容灾和集群功能. Registry:服务 ...

  6. Dubbo负载均衡与集群容错机制

    1  Dubbo简介 Dubbo是一款高性能.轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 作为一个轻量级RPC框架,D ...

  7. dubbo源码分析- 集群容错之Cluster(一)

    1.集群容错的配置项 failover - 失败自动切换,当出现失败,重试其他服务器(缺省),通常用于读操作,但重试会带来更长的延时. failfast - 快速失效,只发起一次调用,失败立即报错.通 ...

  8. Dubbo的10种集群容错模式

    学习Dubbo源码的过程中,首先看到的是dubbo的集群容错模式,以下简单介绍10种集群容错模式 1.AvailableCluster 顾名思义,就是可用性优先,遍历所有的invokers,选择可用的 ...

  9. kubernetes实战-交付dubbo服务到k8s集群(六)使用blue ocean流水线构建dubbo-consumer服务

    我们这里的dubbo-consumer是dubbo-demo-service的消费者: 我们之前已经在jenkins配置好了流水线,只需要填写参数就行了. 由于dubbo-consumer用的gite ...

随机推荐

  1. kalilinux、parrotsecos没有声音

    Kali Linux系统默认状态下,root用户是无法使用声卡的,也就没有声音.启用的方法如下: (1)在终端执行命令:systemctl --user enable pulseaudio (2)在/ ...

  2. SDWebImage从缓存中获取图片

      if ([[SDImageCache sharedImageCache] imageFromKey:sort.imageUrl]) {         [cell.photoImageView s ...

  3. CSS Animation triggers text rendering change in Safari

    薄荷新首页上周五内测,花哥反馈在 MacBook Safari 浏览器下 鼠标移动到第一个商品的时候后面几个商品的文字会加粗.这是什么鬼??? 待我回到家打开笔记本,鼠标蹭蹭蹭的发现问题远不止如此: ...

  4. 使用Python登录腾讯MTA数据分析平台,然后获取相关数据

    思路: 第一步:使用pypeteer.launcher打开浏览器, 第二步:找到mta的登录页面,默认是使用QQ登录的,需要再触发一下切换使用帐号密码登录的按钮(通过使用iframe嵌入的腾讯单点登录 ...

  5. jQuery-关于Ajax请求async属性的说明及总结

    在jquery的ajax中如果希望实现同步或者异步,我们可以设置async(默认true,表示异步请求),下面举例说明两种请求方式的区别. 1.后台代码 public JsonResult GetDa ...

  6. swagger 在本地正常调试 发布后出现500 : {"Message":"出现错误。"}

    点击项目属性 勾上xml 解决

  7. 针对SQLServer数据库的通用访问类

    Web.config中代码 <configuration> <connectionStrings> <add name="connString" co ...

  8. [C#学习笔记]你真的理解拆箱装箱吗?

    学习一项新知识的时候,最好的方法就是去实践它. 前言 <CLR via C#>这本神书真的是太有意思了!没错我的前言就是这个. 装箱 首先来看下,下面这段代码 可以看到,每次循环迭代都会初 ...

  9. Chrome71版本使用screenfull.js全屏功能时报参数错误

    在生产环境长期使用的一个“全屏”功能突然失效了,查看Console 如下报错: Failed to execute 'requestFullscreen' on 'Element': paramete ...

  10. Android - Telephony API 1.6

    SignalStrength: 1. public int getGsmSignalStrength() : GSM Signal Strength, valid values are (0-31, ...