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

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

来看一下HeaderExchangeServer.this.getChannels():

  1.   1     public Collection<Channel> getChannels() {
  2.  2         return (Collection) getExchangeChannels();
  3.  3     }
  4.  4 
  5.  5     public Collection<ExchangeChannel> getExchangeChannels() {
  6.  6         Collection<ExchangeChannel> exchangeChannels = new ArrayList<ExchangeChannel>();
  7.  7         Collection<Channel> channels = server.getChannels();
  8.  8         if (channels != null && channels.size() > 0) {
  9.  9             for (Channel channel : channels) {
  10. 10                 exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel));
  11. 11             }
  12. 12         }
  13. 13         return exchangeChannels;
  14. 14     }

实际上就是获取NettyServer中的全部channel连接。

获取到需要心跳检测的channel后,对每一个channel进行如下判断:

  • 如果在heartbeat内没有进行读操作或者写操作,则发送心跳请求

  • 如果正常消息和心跳在heartbeatTimeout都没接收到,consumer端会进行重连,provider端会关闭channel

这里比较关键的是lastRead和lastWrite的设置。先来看一下获取:

  1. 1 Long lastRead = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_READ_TIMESTAMP);
  2. 2 Long lastWrite = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);

说明有地方在设置这两个值到channel中。

从请求和响应处理来看,无论是请求还是响应都会按照这个顺序处理一遍。

  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

其中HeartbeatHandler源码如下:

  1. 1 public class HeartbeatHandler extends AbstractChannelHandlerDelegate {
  2.  2 
  3.  3     private static final Logger logger = LoggerFactory.getLogger(HeartbeatHandler.class);
  4.  4 
  5.  5     public static String KEY_READ_TIMESTAMP = "READ_TIMESTAMP";
  6.  6 
  7.  7     public static String KEY_WRITE_TIMESTAMP = "WRITE_TIMESTAMP";
  8.  8 
  9.  9     public HeartbeatHandler(ChannelHandler handler) {
  10. 10         super(handler);
  11. 11     }
  12. 12 
  13. 13     public void connected(Channel channel) throws RemotingException {
  14. 14         setReadTimestamp(channel);
  15. 15         setWriteTimestamp(channel);
  16. 16         handler.connected(channel);
  17. 17     }
  18. 18 
  19. 19     public void disconnected(Channel channel) throws RemotingException {
  20. 20         clearReadTimestamp(channel);
  21. 21         clearWriteTimestamp(channel);
  22. 22         handler.disconnected(channel);
  23. 23     }
  24. 24 
  25. 25     public void sent(Channel channel, Object message) throws RemotingException {
  26. 26         setWriteTimestamp(channel);
  27. 27         handler.sent(channel, message);
  28. 28     }
  29. 29 
  30. 30     public void received(Channel channel, Object message) throws RemotingException {
  31. 31         setReadTimestamp(channel);
  32. 32         if (isHeartbeatRequest(message)) {
  33. 33             Request req = (Request) message;
  34. 34             if (req.isTwoWay()) {
  35. 35                 Response res = new Response(req.getId(), req.getVersion());
  36. 36                 res.setEvent(Response.HEARTBEAT_EVENT);
  37. 37                 channel.send(res);
  38. 38                 if (logger.isInfoEnabled()) {
  39. 39                     int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
  40. 40                     if (logger.isDebugEnabled()) {
  41. 41                         logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
  42. 42                                 + ", cause: The channel has no data-transmission exceeds a heartbeat period"
  43. 43                                 + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
  44. 44                     }
  45. 45                 }
  46. 46             }
  47. 47             return;
  48. 48         }
  49. 49         if (isHeartbeatResponse(message)) {
  50. 50             if (logger.isDebugEnabled()) {
  51. 51                 logger.debug(
  52. 52                         new StringBuilder(32)
  53. 53                                 .append("Receive heartbeat response in thread ")
  54. 54                                 .append(Thread.currentThread().getName())
  55. 55                                 .toString());
  56. 56             }
  57. 57             return;
  58. 58         }
  59. 59         handler.received(channel, message);
  60. 60     }
  61. 61 
  62. 62     private void setReadTimestamp(Channel channel) {
  63. 63         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
  64. 64     }
  65. 65 
  66. 66     private void setWriteTimestamp(Channel channel) {
  67. 67         channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
  68. 68     }
  69. 69 
  70. 70     private void clearReadTimestamp(Channel channel) {
  71. 71         channel.removeAttribute(KEY_READ_TIMESTAMP);
  72. 72     }
  73. 73 
  74. 74     private void clearWriteTimestamp(Channel channel) {
  75. 75         channel.removeAttribute(KEY_WRITE_TIMESTAMP);
  76. 76     }
  77. 77 
  78. 78     private boolean isHeartbeatRequest(Object message) {
  79. 79         return message instanceof Request && ((Request) message).isHeartbeat();
  80. 80     }
  81. 81 
  82. 82     private boolean isHeartbeatResponse(Object message) {
  83. 83         return message instanceof Response && ((Response) message).isHeartbeat();
  84. 84     }
  85. 85 }
  • 连接完成时:设置lastRead和lastWrite

  • 连接断开时:清空lastRead和lastWrite

  • 发送消息时:设置lastWrite

  • 接收消息时:设置lastRead

之后交由AllChannelHandler进行处理。之后会一直交由HeaderExchangeHandler进行处理。其对lastRead和lastWrite也做了设置和清理:

  1.   1     public void connected(Channel channel) throws RemotingException {
  2.  2         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
  3.  3         channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis()); 4         ...
  4.  5     }
  5.  6 
  6.  7     public void disconnected(Channel channel) throws RemotingException {
  7.  8         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
  8.  9         channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
  9. 10         ...
  10. 11     }
  11. 12 
  12. 13     public void sent(Channel channel, Object message) throws RemotingException {
  13. 14         Throwable exception = null;
  14. 15         try {
  15. 16             channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
  16. 17             ...
  17. 18         } catch (Throwable t) {
  18. 19             exception = t;
  19. 20         }
  20. 21     }
  21. 22 
  22. 23     public void received(Channel channel, Object message) throws RemotingException {
  23. 24         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
  24. 25         ...
  25. 26     }
  • 连接完成时:设置lastRead和lastWrite

  • 连接断开时:也设置lastRead和lastWrite(为什么?)

  • 发送消息时:设置lastWrite

  • 接收消息时:设置lastRead

这里里有个疑问,从handler链来看,无论是请求还是响应都会按照handler链来处理一遍。那么在HeartbeatHandler中已经进行了lastWrite和lastRead的设置,为什么还要在HeaderExchangeHandler中再处理一遍?

最后,provider端认为连接断了,则会关闭channel。来看一下NettyChannel的close方法:

  1.  1     public void close() {
  2.  2         // 1 将close属性设为true
  3.  3         try {
  4.  4             super.close();
  5.  5         } catch (Exception e) {
  6.  6             logger.warn(e.getMessage(), e);
  7.  7         }
  8.  8         // 2 从全局NettyChannel缓存器中将当前的NettyChannel删掉
  9.  9         try {
  10. 10             removeChannelIfDisconnected(channel);
  11. 11         } catch (Exception e) {
  12. 12             logger.warn(e.getMessage(), e);
  13. 13         }
  14. 14         // 3 清空当前的NettyChannel中的attributes属性
  15. 15         try {
  16. 16             attributes.clear();
  17. 17         } catch (Exception e) {
  18. 18             logger.warn(e.getMessage(), e);
  19. 19         }
  20. 20         // 4 关闭netty的channel,执行netty的channel的优雅关闭
  21. 21         try {
  22. 22             if (logger.isInfoEnabled()) {
  23. 23                 logger.info("Close netty channel " + channel);
  24. 24             }
  25. 25             channel.close();
  26. 26         } catch (Exception e) {
  27. 27             logger.warn(e.getMessage(), e);
  28. 28         }
  29. 29     }

从上边代码来看,假设consumer端挂了,provider端的心跳检测机制可以进行相关的资源回收,所以provider端的心跳检测机制是有必要的。

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

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

相关文章:
【推荐】 大咖分享 | 一文解锁首届云创大会干货——下篇(文末附演讲ppt文件免费下载)
【推荐】 Kafka实践、升级和新版本(0.10)特性预研

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

  1. 9.7 dubbo心跳机制

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

  2. dubbo心跳机制 (1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. dubbo的心跳机制: 目的:检测provider与consumer之间的connection连接是不是还连 ...

  3. dubbo心跳机制 (3)

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

  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. jraiser模块加载执行简要总结

    1 在html文件中,通过require方式来加载指定的入口文件:2 然后通过正则表达式来匹配入口文件中的所有require的依赖文件:注意,此时入口文件已加载完毕,不过,还没执行而已.3 之后逐一通 ...

  2. virtualvm一次插件安装想到的

    在麒麟操作系统visualvm安装插件失败,因为使用的内网,所以在官网下载了插件到本地:因为本地安装的jdk1.6,为了享受jdk1.8,在visualvm文件中增加了对于jdk1.8的引用: exp ...

  3. gitlab init project

    Command line instructions Git global setup git config --global user.name "zxpo" git config ...

  4. nginx与二级域名的绑定 nginx安装

    nginx中文文档 http://www.nginx.cn/doc/ nginx 查看配置文件地址 http://blog.csdn.net/ljfrocky/article/details/5052 ...

  5. div 遮罩层 弹窗

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. java代码swing编程JPaswordField类

    总结:JPasswordField类是JTextField类的子类.用户在JPasswordField对象中输入的字符会被其他的字符替代 而挡住,JPasswordFiled组件主要用来输入口令 pa ...

  7. 查看,创建,删除,映射rbd镜像

    标签(空格分隔): ceph,ceph实验,pg 1. 创建镜像: [root@node3 ~]# rbd create testpool/foo --size 1024 2. 查看镜像信息: [ro ...

  8. C# IL中间代码注入实现切面编程

    背景及现状:之前分享的那篇“面向切面编程–渲染监控日志记录方案”中提供了利用RealProxy作为代理类来生成代理的面向切面的编程方法,那个方法可以实现面向切面编程进行日志记录,现在渲染主程序也是采用 ...

  9. navicat for mysql ,mysql版本是8.0的版本,连接数据库报错1251,解决办法。

    我的mysql版本是8.0的版本,因为毕竟新的mysql采用新的保密方式,所以就的似乎不能用,改密码方式: 用管理员身份打开cmd mysql -uroot -p(输入密码)            进 ...

  10. 为什么in_array(0, ['a', 'b', 'c'])返回true

    为什么in_array(0, ['a', 'b', 'c'])返回true 目录 1 类型转换 2 严格比较 3 false和null 4 数组中有true 在PHP中,数据会自动转换类型后进行比较. ...