假定:主机 A, B 通过 tcp 连接发送数据,如果拔掉 A 主机的网线,B 是无法感知到的。但是如果 A 定时给 B 发送心跳,则能根据心跳的回复来判断连接的状态。

以 zookeeper 为例:zk client 会记录上一次发送数据的时间(lastSend)和上一次接收数据的时间(lastHeard),zk client 给 server 发送心跳(ping),这些心跳和其他命令一起发送给 zk server,如果 zk client 发现好长的时间没有接收到数据,认为超时,则断开与 server 的连接,并重连服务器。

// zookeeper 3.3.3
// void org.apache.zookeeper.ClientCnxn.SendThread.run()
public void run() {
    long now = System.currentTimeMillis();
    long lastHeard = now;
    long lastSend = now;

    // 这里的 zooKeeper 是客户端
    while (zooKeeper.state.isAlive()) {
        try {
            if (sockKey == null) {
                // don't re-establish connection if we are closing
                if (closing) {
                    break;
                }
           // 连接 zk server
                startConnect();
                lastSend = now;
                lastHeard = now;
            }
            int idleRecv = (int) (now - lastHeard);
            int idleSend = (int) (now - lastSend);
            int to = readTimeout - idleRecv;
            if (zooKeeper.state != States.CONNECTED) {
                to = connectTimeout - idleRecv;
            }
            // 接收数据超时,抛异常,异常会在后面的 catch 块中处理
            if (to <= 0) {
                throw new SessionTimeoutException(
                        "Client session timed out, have not heard from server in "
                        + idleRecv + "ms"
                        + " for sessionid 0x"
                        + Long.toHexString(sessionId));
            }
            if (zooKeeper.state == States.CONNECTED) {
                int timeToNextPing = readTimeout/2 - idleSend;
                // 发送 ping 命令(心跳),更新 lastSend
                if (timeToNextPing <= 0) {
                    sendPing();
                    lastSend = now;
                    enableWrite();
                } else {
                    if (timeToNextPing < to) {
                        to = timeToNextPing;
                    }
                }
            }

            selector.select(to);
            Set<SelectionKey> selected;
            synchronized (this) {
                selected = selector.selectedKeys();
            }
            // Everything below and until we get back to the select is
            // non blocking, so time is effectively a constant. That is
            // Why we just have to do this once, here
            now = System.currentTimeMillis();
            for (SelectionKey k : selected) {
                SocketChannel sc = ((SocketChannel) k.channel());
                if ((k.readyOps() & SelectionKey.OP_CONNECT) != 0) {
                    if (sc.finishConnect()) {
                        lastHeard = now;
                        lastSend = now;
                        primeConnection(k);
                    }
                } else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) {
                    if (outgoingQueue.size() > 0) {
                        // We have something to send so it's the same
                        // as if we do the send now.
                        lastSend = now;
                    }
                    if (doIO()) {
                        lastHeard = now;
                    }
                }
            }
            if (zooKeeper.state == States.CONNECTED) {
                if (outgoingQueue.size() > 0) {
                    enableWrite();
                } else {
                    disableWrite();
                }
            }
            selected.clear();
        } catch (Exception e) {
            if (closing) {
                if (LOG.isDebugEnabled()) {
                    // closing so this is expected
                    LOG.debug("An exception was thrown while closing send thread for session 0x"
                            + Long.toHexString(getSessionId())
                            + " : " + e.getMessage());
                }
                break;
            } else {
                // this is ugly, you have a better way speak up
                if (e instanceof SessionExpiredException) {
                    LOG.info(e.getMessage() + ", closing socket connection");
                } else if (e instanceof SessionTimeoutException) {
                    LOG.info(e.getMessage() + RETRY_CONN_MSG);
                } else if (e instanceof EndOfStreamException) {
                    LOG.info(e.getMessage() + RETRY_CONN_MSG);
                } else {
                    LOG.warn("Session 0x"
                            + Long.toHexString(getSessionId())
                            + " for server "
                            + ((SocketChannel)sockKey.channel())
                                .socket().getRemoteSocketAddress()
                            + ", unexpected error"
                            + RETRY_CONN_MSG,
                            e);
                }
                // 断开连接
                cleanup();
                if (zooKeeper.state.isAlive()) {
                    eventThread.queueEvent(new WatchedEvent(
                            Event.EventType.None,
                            Event.KeeperState.Disconnected,
                            null));
                }

                now = System.currentTimeMillis();
                lastHeard = now;
                lastSend = now;
            }
        }
    }
    cleanup();
    try {
        selector.close();
    } catch (IOException e) {
        LOG.warn("Ignoring exception during selector close", e);
    }
    if (zooKeeper.state.isAlive()) {
        eventThread.queueEvent(new WatchedEvent(
                Event.EventType.None,
                Event.KeeperState.Disconnected,
                null));
    }
    ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(),
                             "SendThread exitedloop.");
}

zk server 对 session 也会有一个跟踪,它也会关掉超时的 session,具体逻辑在

void org.apache.zookeeper.server.SessionTrackerImpl.run()

zk server 每收到一个请求,就会触发 touchSession

zookeeper 的心跳的更多相关文章

  1. zookeeper(单机/集群)安装与配置

    一.安装与单机配置 1.下载: wget http://archive.apache.org/dist/zookeeper/stable/zookeeper-3.4.6.tar.gz 如果网站下载不了 ...

  2. zookeeper dubbo 问题解决录

    问题1: 运行起来不报错,不过在Console没有zookeeper的心跳信息,也就是说没有配置上zookeeper,而出错的原因是下面蓝色这段解析不了 spring-dubbo-provider.x ...

  3. dubbo与zookeeper的关系

    Dubbo建议使用Zookeeper作为服务的注册中心. 1.   Zookeeper的作用: zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是 ...

  4. spring Boot环境下dubbo+zookeeper的一个基础讲解与示例

    一,学习背景 1.   前言 对于我们不管工作还是生活中,需要或者想去学习一些东西的时候,大致都考虑几点: a)      我们为什么需要学习这个东西? b)     这个东西是什么? c)      ...

  5. linux下配置zookeeper注册中心及运行dubbo服务

    dubbo和zookeeper的关系 简单来说打个比方:dubbo就是动物园的动物,zookeeper是动物园.如果游客想看动物的话那么就去动物园看.比如你要看老虎,那么动物园有你才能看到.换句话说我 ...

  6. Zookeeper(一)CentOS7.5搭建Zookeeper3.4.12集群与命令行操作

    一. 分布式安装部署 1.0 下载地址 官网首页: https://zookeeper.apache.org/ 下载地址: http://mirror.bit.edu.cn/apache/zookee ...

  7. 为什么zookeeper会导致磁盘IO高【转】

    由于早期的storm版本心跳信息严重依赖zookeeper,心跳风暴会导致zookeeper的事务日志频繁的写磁盘,带来的问题首当其冲的是磁盘IO会爆掉. 优化思路 将zookeeper事务的日志放入 ...

  8. zookeeper和dubbo的关系

    Dubbo建议使用Zookeeper作为服务的注册中心. 1.   Zookeeper的作用:         zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知 ...

  9. Zookeeper简单初应用

    一.Zookeeper 1.1 概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目. Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务 ...

随机推荐

  1. Indy FTP 警告:Only one TIdAntiFreeze can be active in an application

    > Should I use a AntiFreeze component on every form I have a TIdTCPClient > component?  Or is ...

  2. 为什么导入数据库要加入set names utf-8

    Repinted:http://blog.csdn.NET/class1/archive/2006/12/30/1469298.aspx 为了让你的网页能在更多的服务器上正常地显示,还是加上" ...

  3. LayaAir引擎——(四)

    LayaAir引擎 TiledMap 使用 所需要的软件: Tiled地图编辑器 版本0.16.2 LayaAir IDE 所需要的图片:图块图片(1.png) 步骤1: 文件->新文件-> ...

  4. wpf添加超链接

    前段: <TextBlock Margin="69,93,859,10"> <Hyperlink NavigateUri="http://dmsite. ...

  5. Java中的位运算符、移位运算

    一.位运算 Java中有4个位运算,它们的运算规则如下: (1)按位与 (&)  :两位全为1,结果为1,否则为0: (2)按位或  (|)   :两位有一个为1,结果为1,否则为0: (3) ...

  6. wamp无法登录phpmyadmin问题

    文章来源:PHP座谈会 地址:http://bbs.phpthinking.com/forum.php? mod=viewthread&tid=163 第一步.用navicat确认一下,自己的 ...

  7. 个人的MySql配置总结

    lower_case_table_names参数是用来设置MySQL是否让Schema和数据表大小写敏感,我测试的是在查询界面和MySQL控制台界面无法改变它的值,要在配置文件中改变(先关闭服务),一 ...

  8. PAT1019:General Palindromic Number

    1019. General Palindromic Number (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN ...

  9. 用JDBC把Excel中的数据导入到Mysql数据库中

    步骤:0.在Mysql数据库中先建好table 1.从Excel表格读数据 2.用JDBC连接Mysql数据库 3.把读出的数据导入到Mysql数据库的相应表中 其中,步骤0的table我是先在Mys ...

  10. Linux进程内存分析pmap命令

    转自: http://blog.csdn.net/u013982161/article/details/52654256 名称: pmap - report memory map of a proce ...