• 【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表示心跳的请求和响应

    /**
     * 心跳消息
     * 
     * @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) {
    //可以控制单项请求,也就是只有服务端发心跳,客户端不发 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次超时就断开

    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);
    			}
    		}
    	}
    }


    加心跳过滤
    		//在编解码过滤器之后加心跳过滤,如果connnection在处于空闲状态没10分钟发一次心跳请求,30秒无响应算超时,累计3次超时断开连接【在超时处理里做的】
    		KeepAliveFilter filter = new KeepAliveFilter(
    				new KeepAliveMessageFactoryImpl(), IdleStatus.BOTH_IDLE,
    				keepAliveHandler == null ? new KeepAliveRequestTimeoutHandlerImpl() 
    : keepAliveHandler, keepAliveRequestInterval <= 0 ? 600 : keepAliveRequestInterval, 30);
    chain.addLast("ping", filter);

      

     

      

     

  • 相关阅读:
    边走边学Nodejs (基础入门篇)
    Android应用打包安装过程具体解释
    ubuntu与centos安装软件的不同点总结
    你好,C++(12)怎样管理多个类型同样性质同样的数据?3.6 数组
    oracle暂时表空间 ORA-01652:无法通过16(在表空间XXX中)扩展 temp 字段
    iOS中sqlite3操作
    sparkSQL1.1入门之二:sparkSQL执行架构
    [NHibernate]视图处理
    [NHibernate]立即加载
    [NHibernate]延迟加载
  • 原文地址:https://www.cnblogs.com/dagangzi/p/4718660.html
Copyright © 2020-2023  润新知