此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

dubbo的心跳机制:

  • 目的:检测provider与consumer之间的connection连接是不是还连接着,如果连接断了,需要作出相应的处理。

  • 原理:

    • provider:dubbo的心跳默认是在heartbeat(默认是60s)内如果没有接收到消息,就会发送心跳消息,如果连着3次(180s)没有收到心跳响应,provider会关闭channel。

    • consumer:dubbo的心跳默认是在60s内如果没有接收到消息,就会发送心跳消息,如果连着3次(180s)没有收到心跳响应,consumer会进行重连。

来看源码调用链。先看provider端。

一、provider端心跳机制

  1.               -->openServer(URL url)
  2.                  urldubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.10.10.10&bind.port=20880&default.server=netty4&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=21999&qos.port=22222&side=provider&timestamp=1520660491836
  3.                 -->createServer(URL url)
  4.                     -->HeaderExchanger.bind(URL url, ExchangeHandler handler)
  5.                        urldubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.10.10.10&bind.port=20880&channel.readonly.sent=true&codec=dubbo&default.server=netty4&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=21999&qos.port=22222&side=provider&timestamp=1520660491836 handler:DubboProtocol.requestHandler
  6.                       -->new DecodeHandler(new HeaderExchangeHandler(handler)))
  7.                         -->NettyTransporter.bind(URL url, ChannelHandler listener)
  8.                            listener:上边的DecodeHandler实例
  9.                           -->new NettyServer(URL url, ChannelHandler handler)
  10.                             -->ChannelHandler.wrapInternal(ChannelHandler handler, URL url)
  11.                                handler:上边的DecodeHandler实例
  12.                             -->doOpen()//开启netty服务
  13.                       -->new HeaderExchangeServer(Server server)
  14.                          server:上述的NettyServer
  15.                         -->startHeatbeatTimer()

服务端在开启netty服务时, 在调用createServer时,会从url的parameters map中获取heartbeat配置,代码如下:

  1.  1      private ExchangeServer createServer(URL url) {
  2.  2 
  3.  3         ...
  4.  4 
  5.  5         url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
  6.  6        
  7.  7         ...
  8.  8 
  9.  9         ExchangeServer server;
  10. 10         try {
  11. 11             server = Exchangers.bind(url, requestHandler);
  12. 12         } catch (RemotingException e) {
  13. 13             throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
  14. 14         }
  15. 15 
  16. 16         ...
  17. 17 
  18. 18         return server;
  19. 19     }

其中:int DEFAULT_HEARTBEAT = 60 * 1000,即当用户没有配置heartbeat(心跳时间)时,默认heartbeat=60s(即60s内没有接收到任何请求,就会发送心跳信息)。那么这个heartbeat到底该怎么配?

provider端:

  1. 1     <dubbo:service ...>
  2. 2         <dubbo:parameter key="heartbeat" value="3000"/>
  3. 3     </dubbo:service>

consumer端:

  1. 1     <dubbo:reference ...>
  2. 2         <dubbo:parameter key="heartbeat" value="3000"/>
  3. 3     </dubbo:reference>

再来看调用链,当执行到这一句。

  1. 1 ChannelHandler.wrapInternal(ChannelHandler handler, URL url)

会形成一个handler调用链,调用链如下:

  1. 1 MultiMessageHandler
  2. 2 -->handler: HeartbeatHandler
  3. 3    -->handler: AllChannelHandler
  4. 4          -->url: providerUrl
  5. 5          -->executor: FixedExecutor
  6. 6          -->handler: DecodeHandler
  7. 7             -->handler: HeaderExchangeHandler
  8. 8                -->handler: ExchangeHandlerAdapterDubboProtocol.requestHandler

这也是netty接收到请求后的处理链路,注意其中有一个HeartbeatHandler。

最后,执行new HeaderExchangeServer(Server server),来看源码:

  1. 1 public class HeaderExchangeServer implements ExchangeServer {
  2.  2     /** 心跳定时器 */
  3.  3     private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1,
  4.  4             new NamedThreadFactory(
  5.  5                     "dubbo-remoting-server-heartbeat",
  6.  6                     true));
  7.  7     /** NettyServer */
  8.  8     private final Server server;
  9.  9     // heartbeat timer
  10. 10     private ScheduledFuture<?> heatbeatTimer;
  11. 11     // heartbeat timeout (ms), default value is 0 , won't execute a heartbeat.
  12. 12     private int heartbeat;
  13. 13     private int heartbeatTimeout;
  14. 14     private AtomicBoolean closed = new AtomicBoolean(false);
  15. 15 
  16. 16     public HeaderExchangeServer(Server server) {
  17. 17         if (server == null) {
  18. 18             throw new IllegalArgumentException("server == null");
  19. 19         }
  20. 20         this.server = server;
  21. 21         this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
  22. 22         this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
  23. 23         if (heartbeatTimeout < heartbeat * 2) {
  24. 24             throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
  25. 25         }
  26. 26         startHeatbeatTimer();
  27. 27     }
  28. 28 
  29. 29     private void startHeatbeatTimer() {
  30. 30         stopHeartbeatTimer();
  31. 31         if (heartbeat > 0) {
  32. 32             heatbeatTimer = scheduled.scheduleWithFixedDelay(
  33. 33                     new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
  34. 34                         public Collection<Channel> getChannels() {
  35. 35                             return Collections.unmodifiableCollection(
  36. 36                                     HeaderExchangeServer.this.getChannels());
  37. 37                         }
  38. 38                     }, heartbeat, heartbeatTimeout),
  39. 39                     heartbeat, heartbeat, TimeUnit.MILLISECONDS);
  40. 40         }
  41. 41     }
  42. 42 
  43. 43     private void stopHeartbeatTimer() {
  44. 44         try {
  45. 45             ScheduledFuture<?> timer = heatbeatTimer;
  46. 46             if (timer != null && !timer.isCancelled()) {
  47. 47                 timer.cancel(true);
  48. 48             }
  49. 49         } catch (Throwable t) {
  50. 50             logger.warn(t.getMessage(), t);
  51. 51         } finally {
  52. 52             heatbeatTimer = null;
  53. 53         }
  54. 54     }
  55. 55 }

创建HeaderExchangeServer时,初始化了heartbeat(心跳间隔时间)和heartbeatTimeout(心跳响应超时时间:即如果最终发送的心跳在这个时间内都没有返回,则做出响应的处理)。

  • heartbeat默认是0(从startHeatbeatTimer()方法可以看出只有heartbeat>0的情况下,才会发心跳,这里heartbeat如果从url的parameter map中获取不到,就是0,但是我们在前边看到dubbo会默认设置heartbeat=60s到parameter map中,所以此处的heartbeat=60s);

  • heartbeatTimeout:默认是heartbeat*3。(原因:假设一端发出一次heartbeatRequest,另一端在heartbeat内没有返回任何响应-包括正常请求响应和心跳响应,此时不能认为是连接断了,因为有可能还是网络抖动什么的导致了tcp包的重传超时等)

  • scheduled是一个含有一个线程的定时线程执行器(其中的线程名字为:"dubbo-remoting-server-heartbeat-thread-*")

之后启动心跳定时任务:

  • 首先如果原来有心跳定时任务,关闭原来的定时任务

  • 之后启动scheduled中的定时线程,从启动该线程开始,每隔heartbeat执行一次HeartBeatTask任务(第一次执行是在启动线程后heartbeat时)

来看一下HeartBeatTask的源码:

  1.  1 final class HeartBeatTask implements Runnable {
  2.  2     // channel获取器:用于获取所有需要进行心跳检测的channel
  3.  3     private ChannelProvider channelProvider;
  4.  4     private int heartbeat;
  5.  5     private int heartbeatTimeout;
  6.  6 
  7.  7     HeartBeatTask(ChannelProvider provider, int heartbeat, int heartbeatTimeout) {
  8.  8         this.channelProvider = provider;
  9.  9         this.heartbeat = heartbeat;
  10. 10         this.heartbeatTimeout = heartbeatTimeout;
  11. 11     }
  12. 12 
  13. 13     public void run() {
  14. 14         try {
  15. 15             long now = System.currentTimeMillis();
  16. 16             for (Channel channel : channelProvider.getChannels()) {
  17. 17                 if (channel.isClosed()) {
  18. 18                     continue;
  19. 19                 }
  20. 20                 try {
  21. 21                     // 获取最后一次读操作的时间
  22. 22                     Long lastRead = (Long) channel.getAttribute(
  23. 23                             HeaderExchangeHandler.KEY_READ_TIMESTAMP);
  24. 24                     // 获取最后一次写操作的时间
  25. 25                     Long lastWrite = (Long) channel.getAttribute(
  26. 26                             HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);27                     // 如果在heartbeat内没有进行读操作或者写操作,则发送心跳请求
  27. 28                     if ((lastRead != null && now - lastRead > heartbeat)
  28. 29                             || (lastWrite != null && now - lastWrite > heartbeat)) {
  29. 30                         Request req = new Request();
  30. 31                         req.setVersion("2.0.0");
  31. 32                         req.setTwoWay(true);
  32. 33                         req.setEvent(Request.HEARTBEAT_EVENT);
  33. 34                         channel.send(req);
  34. 35                         if (logger.isDebugEnabled()) {
  35. 36                             logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress()
  36. 37                                     + ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms");
  37. 38                         }
  38. 39                     }
  39. 40                     //正常消息和心跳在heartbeatTimeout都没接收到
  40. 41                     if (lastRead != null && now - lastRead > heartbeatTimeout) {
  41. 42                         logger.warn("Close channel " + channel
  42. 43                                 + ", because heartbeat read idle time out: " + heartbeatTimeout + "ms");
  43. 44                         // consumer端进行重连
  44. 45                         if (channel instanceof Client) {
  45. 46                             try {
  46. 47                                 ((Client) channel).reconnect();
  47. 48                             } catch (Exception e) {
  48. 49                                 //do nothing
  49. 50                             }
  50. 51                         } else {// provider端关闭连接
  51. 52                             channel.close();
  52. 53                         }
  53. 54                     }
  54. 55                 } catch (Throwable t) {
  55. 56                     logger.warn("Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t);
  56. 57                 }
  57. 58             }
  58. 59         } catch (Throwable t) {
  59. 60             logger.warn("Unhandled exception when heartbeat, cause: " + t.getMessage(), t);
  60. 61         }
  61. 62     }
  62. 63 
  63. 64     interface ChannelProvider {
  64. 65         Collection<Channel> getChannels();
  65. 66     }
  66. 67 }

HeartBeatTask首先获取所有的channelProvider#getChannels获取所有需要心跳检测的channel,channelProvider实例是HeaderExchangeServer中在启动线程定时执行器的时候创建的内部类。

  1. 1                     new HeartBeatTask.ChannelProvider() {
  2. 2                         public Collection<Channel> getChannels() {
  3. 3                             return Collections.unmodifiableCollection(
  4. 4                                     HeaderExchangeServer.this.getChannels());
  5. 5                         }
  6. 6                     }

免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 一个小白的测试环境docker化之路
【推荐】 移动端推广APP防作弊机制之依我见
【推荐】 大中型 UGC 平台的反垃圾(anti-spam)工作

dubbo心跳机制 (1)的更多相关文章

  1. 9.7 dubbo心跳机制

    dubbo的心跳机制: 目的:检测provider与consumer之间的connection连接是不是还连接着,如果连接断了,需要作出相应的处理. 原理: provider:dubbo的心跳默认是在 ...

  2. dubbo心跳机制 (3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 二.consumer端心跳机制                       //创建ExchangeClie ...

  3. dubbo心跳机制 (2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 来看一下HeaderExchangeServer.this.getChannels():   1     p ...

  4. dubbo之心跳机制

    在网络传输中,怎么确保通道连接的可用性是一个很重要的问题,简单的说,在网络通信中有客户端和服务端,一个负责发送请求,一个负责接收请求,在保证连接有效性的背景下,这两个物体扮演了什么角色,心跳机制能有效 ...

  5. Dubbo之心跳机制 · 房东的小黑

    在网络传输中,怎么确保通道连接的可用性是一个很重要的问题,简单的说,在网络通信中有客户端和服务端,一个负责发送请求,一个负责接收请求,在保证连接有效性的背景下,这两个物体扮演了什么角色,心跳机制能有效 ...

  6. 分析dubbo心跳检测机制

    目的: 维持provider和consumer之间的长连接 实现: dubbo心跳时间heartbeat默认是60s,超过heartbeat时间没有收到消息,就发送心跳消息(provider,cons ...

  7. rabbitmq 的心跳机制&应用

    官方文档说: If a consumer dies (its channel is closed, connection is closed, or TCP connection is lost) w ...

  8. zookeeper心跳机制流程梳理

    zookeeper心跳机制流程梳理 Processor链Chain protected void setupRequestProcessors() { RequestProcessor finalPr ...

  9. 一个Socket连接管理池(心跳机制)

    一个Socket连接管理池(心跳机制) http://cuisuqiang.iteye.com/blog/1489661

随机推荐

  1. 【luogu P3950 部落冲突】 题解

    题目连接:https://www.luogu.org/problemnew/show/P3950 1.像我这种学数据结构学傻了的 2.边权化点权 所有点权初始化0 3.对于战争 将深度较深的-1,对于 ...

  2. 【luogu P4568 [JLOI2011]飞行路线】 题解

    题目链接:https://www.luogu.org/problemnew/show/P4568 卡了一晚上,算是分层图最短路的模板.注意卡SPFA,所以我写了个SLF优化. 同时 AC400祭!~ ...

  3. Android学习笔记_54_自定义 Widget (Toast)

    1.Toast控件: 通过查看源代码,发现Toast里面实现的原理是通过服务Context.LAYOUT_INFLATER_SERVICE获取一个LayoutInflater布局管理器,从而获取一个V ...

  4. Tomcat 启动速度优化

    创建一个web项目 选择发布到 汤姆猫 的下面 deploy path: 表示发布到的文件名称 把项目添加到 tomcat 里,运行,我们可以在 tomcat里找到我们发布的项目: 现在启动时间: 现 ...

  5. 优雅的QSignleton (二) MonoSingleton单例实现

    MonoSingleton.cs namespace QFramework.Example { using System.Collections; using UnityEngine; class C ...

  6. 如何使用 SSL 证书配置端口

    创建使用自承载的 Windows Communication Foundation (WCF) 服务时WSHttpBinding类,使用传输安全,还必须使用 X.509 证书配置端口. 如果不是在创建 ...

  7. oracle中lock和latch的用途

    本文向各位阐述Oracle的Latch机制,Latch,用金山词霸翻译是门插栓,闭锁,专业术语叫锁存器,我开始接触时就不大明白为什么不写Lock,不都是锁吗?只是翻译不同而以?研究过后才知道两者有很大 ...

  8. JetBrains 授权服务器(License Server):

    JetBrains 授权服务器(License Server): https://www.imsxm.com/jetbrains-license-server.html

  9. QQ群排名优化到霸屏的策略怎么做?

    谈起QQ群排名霸屏,首先要弄清楚概念,有些刚接触QQ群的朋友可能不太了解,所谓的QQ群排名霸屏,就是指当你的客户群体搜索QQ群某个关键词时,出现在QQ群搜索结果前面的群,全部或者大部分都是我们自己的群 ...

  10. php Trait的使用

    1.php中的trait是啥? 看上去既像类又像接口,其实都不是,Trait可以看做类的部分实现,可以混入一个或多个现有的PHP类中,其作用有两个:表明类可以做什么:提供模块化实现.Trait是一种代 ...