心跳机制 heartbeat

       心跳机制可以分为集中式和分散式,简单说集中式就是A通过心跳告诉B自己还活着,C通过B来获得或者的A,这里面的A,B,C可以是一个集群,可以认为B是个服务发现的功能。这种方式广泛应用于hadoop,NameServer来管理哪些DataNode时活的,客户端存储数据要从nameserver中获取存储的datanode的位置。这种方式有弊端就是B会存在单点故障,当然也有方式解决,譬如zookeeper来充当B的角色,实现相对复杂,但是监控等比较容易,而且A不需要关心C到底哪些活的,B(相对简单)告诉A那个可用即可。

分散式:A 和 C 通过心跳来告诉C,A是活的。这种方式简单,譬如A是server端,有两台机器,C是client端,有10台机器,那么C定时向A发送心跳,来看A是否还活着,决定向那一台机器发送数据,这种简单,但是存在弊端,就是譬如A有10台机器,C有30台机器,单位时间那么就存在300个心跳,也就是存在了信令风暴。各有利弊,要看实际需求。

       从实现机制上看有两种,第一种如果链接是TCP那么可以采用TCP自带的keepalive选项,第二种就是应用程序自己控制。

       第一中TCP的keepalive,从名字可以看出,可以用来保持tcp链接的alive,tcp的keepalive有三个参数,tcp_keepalive_time 链接闲置多久发送keepalive的包

tcp_keepalive_probes 发送几个ack 包不回复当作连接死亡

tcp_keepalive_intvl 两个ack包间隔多久

centos默认值7200秒(2个小时,要不要这么蛋疼啊!)、9次、75秒,连接就有了一个时间窗口,如果发送的ack连续不回复,那么这个时间窗口就会越来越小,当变为零时,则认为该连接不可用。这里有一篇非常详细的介绍文章: http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO ,很多网络设备,由于硬件的限制(cpu,内存),尤其时NAT(network address Translations),无法保持其上的所有连接,因此必要的时候,会在连接池上选择一些不活跃的链接踢掉,典型的算法LRU,把最久没有数据的链接踢掉,通过tcp的keepalive机制,可以让连接每隔一个小时产生一个ack包,降低被T掉的风险,当然代价就是额外的网络和cpu负担。

       第二种就是应用程序自己控制,判断方法有两种,时间差策略和简单标志。时间差标志,当前时间-上次心跳时间 > 某个时间差,则认为该连接不可用。简单标志,当发送心跳,对面没有返回,则认为对方不可用。

拿netty的长连接举例,客户端和服务器创建了长连接,我们可以把它们的连接存到一个列表,在netty的handler种,我们定义new IdleStateHandler(60, 15, 13,TimeUnit.SECONDS)然后在某个handler种定义连接http://www.oschina.net/question/139577_146101,@Override

     public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
             throws Exception {
         if (evt instanceof IdleStateEvent) {
             IdleStateEvent event = (IdleStateEvent) evt;
             if (event.state().equals(IdleState.READER_IDLE)) {
                 System.out.println( "READER_IDLE" );
                 // 超时关闭channel
                 ctx.close();
             } else if (event.state().equals(IdleState.WRITER_IDLE)) {
                 System.out.println( "WRITER_IDLE" );
             } else if (event.state().equals(IdleState.ALL_IDLE)) {
                 System.out.println( "ALL_IDLE" );
                 // 发送心跳
                 ctx.channel().write( "ping\n" );
             }
         }
         super .userEventTriggered(ctx, evt);
     }

这样来决定是否关闭channel,RocketMQ是这样做的。当下次在发送数据时,需要重新创建链接。

猜你喜欢

转载自skywhsq1987.iteye.com/blog/2100723