【MINA】心跳机制

列上两篇好文章

http://www.cnblogs.com/pricks/p/3832882.html

http://blog.csdn.net/cruise_h/article/details/13756219

心跳要解决的问题:说白了就是监控无效的连接并断开,多次超时无反应就断开的方式处理

MINA自带了对心跳协议的支持,可以对心跳做出细致的配置,本文在次基础上实现了server端对client端的心跳检测。

在开始之前先简单介绍下keepAlive的机制:

首先,需要搞清楚TCP keepalive是干什么用的。从名字理解就能够知道,keepalive就是用来检测一个tcp connection是否还连接正常。当一个tcp connection建立好之后,如果双方都不发送数据的话,tcp协议本身是不会发送其它的任何数据的,也就是说,在一个idle的connection上,两个socket之间不产生任何的数据交换。从另一个方面讲,当一个connection建立之后,链接双方可以长时间的不发送任何数据,比如几天,几星期甚至几个月,但该connection仍然存在。

所以,这就可能出现一个问题。举例来说,server和client建立了一个connection,server负责接收client的request。当connection建立好之后,client由于某种原因机器停机了。但server端并不知道,所以server就会一直监听着这个connection,但其实这个connection已经失效了。

keepalive就是为这样的场景准备的。当把一个socket设置成了keepalive,那么这个socket空闲一段时间后,它就会向对方发送数据来确认对方仍然存在。放在上面的例子中,如果client停机了,那么server所发送的keepalive数据就不会有response,这样server就能够确认client完蛋了(至少从表面上看是这样)。

 

那我项目心跳来说一下具体怎么做

MINA本身提供了一个过滤器类: org.apache.mina.filter.keepalive . KeepAliveFilter ,该过滤器用于在IO空闲的时候发送并且反馈心跳包(keep-alive request/response)。 

说到KeepAliveFilter这个类有必要先说一说其构造函数,即实例化该类需要些什么,该类构造函数中参数有三个分别是: 
(1)KeepAvlieMessageFactory:   该实例引用用于判断接受与发送的包是否是心跳包,以及心跳请求包的实现 
(2)IdleStatus:                              该过滤器所关注的空闲状态,默认认为读取空闲。 即当读取通道空闲的时候发送心跳包 
(3)KeepAliveRequestTimeoutHandler: 心跳包请求后超时无反馈情况下的处理机制  默认为CLOSE  即关闭连接 

 

其实我们自己做下这两个接口的实现类就搞定了KeepAvlieMessageFactory、KeepAliveRequestTimeoutHandler

KeepAvlieMessageFactoryImpl中主要是定义心跳包的内容

KeepAliveRequestTimeoutHandlerImpl 主要是定义超时后的处理方式,通常是多次超时后就断开

 

看实例,我的消息类型固定为map,里面加一个cmd表示心跳的请求和响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
  * 心跳消息
  *
  * @author Administrator
  *
  */
public  class  KeepAliveMessageFactoryImpl implements KeepAliveMessageFactory {
     public  static  final  int  BEAT_REQ = 100000;
     public  static  final  int  BEAT_RES = 100001;
     private  static  final String key =  "cmd" ;
 
     private  boolean notSendRequest; // 不发送请求,只接受心跳请求
     private  Map<String, Object> req =  new  HashMap<String, Object>();
     private  Map<String, Object> res =  new  HashMap<String, Object>();
 
     public  KeepAliveMessageFactoryImpl() {
         req.put(key, BEAT_REQ);
         res.put(key, BEAT_RES);
     }
 
     public  KeepAliveMessageFactoryImpl(boolean notSendRequest) {
         req.put(key, BEAT_REQ);
         res.put(key, BEAT_RES);
         this .notSendRequest = notSendRequest;
     }
 
     @Override
     public  Object getRequest(IoSession session) {
         if  (notSendRequest) {<br>                         //可以控制单项请求,也就是只有服务端发心跳,客户端不发
             return  null ;
         else  if  (!session.containsAttribute(Response.CREDIT)) {
             // 不可信的连接,不心跳
             return  null ;
         }
         return  req;
     }
 
     @Override
     public  Object getResponse(IoSession session, Object request) {
         return  res;
     }
 
     @Override
     public  boolean isRequest(IoSession session, Object message) {
         if  (message instanceof Map) {
             Map msg = (Map) message;
             Object obj = msg. get (key);
             if  (obj !=  null  && obj instanceof Integer) {
                 if  (((Integer) obj).intValue() == BEAT_REQ) {
                     // System.out.println("发送心跳响应:。。。。。。。。"+session.getRemoteAddress());
                     return  true ;
                 }
             }
         }
         return  false ;
     }
 
     @Override
     public  boolean isResponse(IoSession session, Object message) {
         if  (message instanceof Map) {
             Map msg = (Map) message;
             Object obj = msg. get (key);
             if  (obj !=  null  && obj instanceof Integer) {
                 if  (((Integer) obj).intValue() == BEAT_RES) {
                     // System.out.println("收到心跳响应:。。。。。。。。"+session.getRemoteAddress());
                     return  true ;
                 }
             }
         }
         return  false ;
     }
 
}

 

再来看超时处理接口的实现类,超过3次超时就断开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public  class  KeepAliveRequestTimeoutHandlerImpl  implements
         KeepAliveRequestTimeoutHandler {
     private  static  Logger logger = LoggerFactory
             .getLogger(RequestTimeoutCloseHandler. class );
 
     /**
      * 超时几次关闭连接
      */
     private  int  timeoutNum = 3;
 
     public  void  setTimeoutNum( int  timeoutNum) {
         this .timeoutNum = timeoutNum;
     }
 
     public  RequestTimeoutCloseHandler() {
     }
 
     public  RequestTimeoutCloseHandler( int  timeoutNum) {
         this .timeoutNum = timeoutNum;
     }
 
     public  void  keepAliveRequestTimedOut(KeepAliveFilter filter,
             IoSession session) throws Exception {
         Integer num = (Integer) session
                 .getAttribute(KeepAliveFilter.TIMEOUT_NUM);
         if  (num ==  null  || num >= timeoutNum) {
             if  (logger.isWarnEnabled()) {
                 logger.warn(com.youxigu.dynasty2.i18n.MarkupMessages.getString( "RequestTimeoutCloseHandler_1" ), filter
                         .getRequestTimeout(), session);
             }
             
             Map<String, Object>  params  new  HashMap<String, Object>(3);
             params .put( "cmd" , 1);
             params .put( "errCode" , -9011);
             params
                     .put( "err" ,com.youxigu.dynasty2.i18n.MarkupMessages.getString( "RequestTimeoutCloseHandler_2" ));
             session.write( params );
             session.close( false );
             //session.close(true);
         } else {
             if  (logger.isWarnEnabled()) {
                 logger.warn(com.youxigu.dynasty2.i18n.MarkupMessages.getString( "RequestTimeoutCloseHandler_0" ),session,num);
             }
         }
     }
}<br><br><br><strong>加心跳过滤</strong>
1
2
3
4
//在编解码过滤器之后加心跳过滤,如果connnection在处于空闲状态没10分钟发一次心跳请求,30秒无响应算超时,累计3次超时断开连接【在超时处理里做的】
KeepAliveFilter filter =  new  KeepAliveFilter(
         new  KeepAliveMessageFactoryImpl(), IdleStatus.BOTH_IDLE,
         keepAliveHandler ==  null  new  KeepAliveRequestTimeoutHandlerImpl() <br>                                : keepAliveHandler, keepAliveRequestInterval <= 0 ? 600 : keepAliveRequestInterval, 30); <br>                chain.addLast( "ping" , filter);

  

1
<strong> </strong>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值