Mina 心跳KeepAliveFilter 源码解读
来源:互联网 发布:花岗岩水槽 知乎 编辑:程序博客网 时间:2024/04/29 17:11
以下对mina 2.0.7版本中的KeepAliveFilter源码解读,想给自行添加心跳的朋友一点提示。读完这个类后,自行扩展就不说了。都懂的。
注:不当之处,还请斧正!
public class KeepAliveFilter extends IoFilterAdapter { /** * 监听状态标识:等待对端返回。在向对端发送Ping命令后设置。注:由于此过滤器可用于Mina客户端/服务端,所以此处使用 对端 返回 */ private final AttributeKey WAITING_FOR_RESPONSE = new AttributeKey(getClass(), "waitingForResponse"); /** * 监听状态标识:忽略一次读空闲。即发出Ping命令后如果有2次读空闲时间都没有返回,再认为此连接异常并交由超时处理。对应方法中有详细说明。 */ private final AttributeKey IGNORE_READER_IDLE_ONCE = new AttributeKey(getClass(), "ignoreReaderIdleOnce"); /** * 创建和判断心跳消息的工厂,主要提供和检测对应的消息包是否为心跳包。 */ private final KeepAliveMessageFactory messageFactory; /** * 空闲状态3种:例如设定时长10秒 * READER_IDLE 读空闲 表示10秒内没有读到对端的任何消息时触发 * WRITER_IDLE 写空闲 表示10秒内没有任何消息写入到对端时触发 * BOTH_IDLE 读写空闲 表示10秒内即没有读到也没有写入到对端时触发 * */ private final IdleStatus interestedIdleStatus; /** * 超时处理器,一般自行定义并和具体业务逻辑相关。注:以下几个参数设置成volatile主要是多线程立即可见。即一个线程修改参数,其他使用的线程立即可见。 */ private volatile KeepAliveRequestTimeoutHandler requestTimeoutHandler; /** * 设置心跳频率,单位:秒。和interestedIdleStatus配合使用,例如60秒,用于设置空闲状态达到指定间隔60秒后触发session_idle事件。 */ private volatile int requestInterval; /** * 设置读空闲时间,单位:秒。 * 注:上面触发session_idle事件后,向对端发送了一条PING命令,然后设置此间隔用来监听返回结果的。 * 例20秒,如果PING命令发出后20秒后没有读到任何返回结果,则认为此次PING请求超时,并交由 timeoutHandler处理。 */ private volatile int requestTimeout; /** * 是否向下转发session_idle事件。默认为false。即不会向下传递sessionIdle事件。其他类型的事件不受影响。 * 表示:如果客户端或服务端设置了心跳监听器,那么此监听器后续的Filter处理器,和消息处理器IoHandler都不会再收到sessionIdle事件。 */ private volatile boolean forwardEvent; /** * 各种构造方法不解释。 * Creates a new instance with the default properties. * The default property values are: * <ul> * <li><tt>interestedIdleStatus</tt> - {@link IdleStatus#READER_IDLE}</li> * <li><tt>policy</tt> = {@link KeepAliveRequestTimeoutHandler#CLOSE}</li> * <li><tt>keepAliveRequestInterval</tt> - 60 (seconds)</li> * <li><tt>keepAliveRequestTimeout</tt> - 30 (seconds)</li> * </ul> */ public KeepAliveFilter(KeepAliveMessageFactory messageFactory) { this(messageFactory, IdleStatus.READER_IDLE, KeepAliveRequestTimeoutHandler.CLOSE); } /** * Creates a new instance with the default properties. * The default property values are: * <ul> * <li><tt>policy</tt> = {@link KeepAliveRequestTimeoutHandler#CLOSE}</li> * <li><tt>keepAliveRequestInterval</tt> - 60 (seconds)</li> * <li><tt>keepAliveRequestTimeout</tt> - 30 (seconds)</li> * </ul> */ public KeepAliveFilter(KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus) { this(messageFactory, interestedIdleStatus, KeepAliveRequestTimeoutHandler.CLOSE, 60, 30); } /** * Creates a new instance with the default properties. * The default property values are: * <ul> * <li><tt>interestedIdleStatus</tt> - {@link IdleStatus#READER_IDLE}</li> * <li><tt>keepAliveRequestInterval</tt> - 60 (seconds)</li> * <li><tt>keepAliveRequestTimeout</tt> - 30 (seconds)</li> * </ul> */ public KeepAliveFilter(KeepAliveMessageFactory messageFactory, KeepAliveRequestTimeoutHandler policy) { this(messageFactory, IdleStatus.READER_IDLE, policy, 60, 30); } /** * Creates a new instance with the default properties. * The default property values are: * <ul> * <li><tt>keepAliveRequestInterval</tt> - 60 (seconds)</li> * <li><tt>keepAliveRequestTimeout</tt> - 30 (seconds)</li> * </ul> */ public KeepAliveFilter(KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus, KeepAliveRequestTimeoutHandler policy) { this(messageFactory, interestedIdleStatus, policy, 60, 30); } /** * Creates a new instance. */ public KeepAliveFilter(KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus, KeepAliveRequestTimeoutHandler policy, int keepAliveRequestInterval, int keepAliveRequestTimeout) { if (messageFactory == null) { throw new IllegalArgumentException("messageFactory"); } if (interestedIdleStatus == null) { throw new IllegalArgumentException("interestedIdleStatus"); } if (policy == null) { throw new IllegalArgumentException("policy"); } this.messageFactory = messageFactory; this.interestedIdleStatus = interestedIdleStatus; requestTimeoutHandler = policy; setRequestInterval(keepAliveRequestInterval); setRequestTimeout(keepAliveRequestTimeout); } public IdleStatus getInterestedIdleStatus() { return interestedIdleStatus; } public KeepAliveRequestTimeoutHandler getRequestTimeoutHandler() { return requestTimeoutHandler; } public void setRequestTimeoutHandler(KeepAliveRequestTimeoutHandler timeoutHandler) { if (timeoutHandler == null) { throw new IllegalArgumentException("timeoutHandler"); } requestTimeoutHandler = timeoutHandler; } public int getRequestInterval() { return requestInterval; } public void setRequestInterval(int keepAliveRequestInterval) { if (keepAliveRequestInterval <= 0) { throw new IllegalArgumentException("keepAliveRequestInterval must be a positive integer: " + keepAliveRequestInterval); } requestInterval = keepAliveRequestInterval; } public int getRequestTimeout() { return requestTimeout; } public void setRequestTimeout(int keepAliveRequestTimeout) { if (keepAliveRequestTimeout <= 0) { throw new IllegalArgumentException("keepAliveRequestTimeout must be a positive integer: " + keepAliveRequestTimeout); } requestTimeout = keepAliveRequestTimeout; } public KeepAliveMessageFactory getMessageFactory() { return messageFactory; } /** * Returns <tt>true</tt> if and only if this filter forwards * a {@link IoEventType#SESSION_IDLE} event to the next filter. * By default, the value of this property is <tt>false</tt>. */ public boolean isForwardEvent() { return forwardEvent; } /** * Sets if this filter needs to forward a * {@link IoEventType#SESSION_IDLE} event to the next filter. * By default, the value of this property is <tt>false</tt>. */ public void setForwardEvent(boolean forwardEvent) { this.forwardEvent = forwardEvent; } /** * 被加到链中之前调用,每个新会话仅调用一次! */ @Override public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { if (parent.contains(this)) { throw new IllegalArgumentException("You can't add the same filter instance more than once. " + "Create another instance and add it."); } } /** * 被加到链中之后调用,每个新会话仅调用一次! */ @Override public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { resetStatus(parent.getSession()); } /** * 被移除链后调用,每个新会话仅调用一次! */ @Override public void onPostRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { resetStatus(parent.getSession()); } @Override public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception { try { /** * 由于心跳要在两端做,所以此处如果收到对端的Ping请求,也会发送Pong响应,以便对端即时接收到处理。(如立即接收到表示连接正常,只是此通道有一段时间没有数据通信而已) * */ if (messageFactory.isRequest(session, message)) { Object pongMessage = messageFactory.getResponse(session, message); if (pongMessage != null) { nextFilter.filterWrite(session, new DefaultWriteRequest(pongMessage)); } } /** * 成功接收到对端的返回结果,认为连接正常。则重置已端的状态,以便下次重新监听。即再过多少秒的空闲状态后继续触发sessionIdle事件。 * */ if (messageFactory.isResponse(session, message)) { resetStatus(session); } } finally { /** * finally强制执行下面的代码。如果不是心跳包,别等了赶紧交给后面的正式业务处理。 * */ if (!isKeepAliveMessage(session, message)) { nextFilter.messageReceived(session, message); } } } @Override public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { /** * 主要检测消息包发送成功后,如不是心跳包交由后面处理。因为Ping命令发送后也会回调此方法,为了别影响后面业务层做了个检测过滤。 * */ Object message = writeRequest.getMessage(); if (!isKeepAliveMessage(session, message)) { nextFilter.messageSent(session, writeRequest); } } /** * 前面说了半天,见着真东西了。 * 上面的参数设置后,如果达到了条件就会触发此方法。 * */ @Override public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception { if (status == interestedIdleStatus) { /** * 第一次肯定不会有,进入 * 1.发送PING命令到对端。 * 2.标记状态,设定为读空闲和读超时,来监听此次PING命令能否正常返回。谁让此通道很长时间没有任何通信了呢。 * 3.把 等待返回 的 监听标识加上。 * */ if (!session.containsAttribute(WAITING_FOR_RESPONSE)) { Object pingMessage = messageFactory.getRequest(session); if (pingMessage != null) { nextFilter.filterWrite(session, new DefaultWriteRequest(pingMessage)); // If policy is OFF, there's no need to wait for // the response. if (getRequestTimeoutHandler() != KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) { markStatus(session); /** * 此处如果为读写空闲,则再给它一次机会,即忽略其后的一次读空闲处理。第二次就不行了,呵呵。 * 注意此时监听的空闲状态已变为 读空闲,即READER_IDLE。 * */ if (interestedIdleStatus == IdleStatus.BOTH_IDLE) { session.setAttribute(IGNORE_READER_IDLE_ONCE); } } else { //如果设定的超时处理器为聋子,则效果就是每到一定的空闲状态后就向对端发送PING命令,不管那边回不回。一直发。因为它听不见返回。 resetStatus(session); } } } else {//如果超时返回,则回调超时处理。 handlePingTimeout(session); } } else if (status == IdleStatus.READER_IDLE) { /** * 如果标记了 忽略一次, 则语句不执行。否则会调用超时处理。 * */ if (session.removeAttribute(IGNORE_READER_IDLE_ONCE) == null) { if (session.containsAttribute(WAITING_FOR_RESPONSE)) { handlePingTimeout(session); } } } /** * 这很简单,是否向后转发。默认后续的逻辑就不要处理此类事件了。 * */ if (forwardEvent) { nextFilter.sessionIdle(session, status); } } private void handlePingTimeout(IoSession session) throws Exception { /** * 重置监听状态,主要是保持万一你什么都不做,我也要正常运行。 * */ resetStatus(session); KeepAliveRequestTimeoutHandler handler = getRequestTimeoutHandler(); if (handler == KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) { return; } /** * 自行处理,这个会话已经超时了,连接可能有问题。 * 此时这个连接情况为:有很长时间没有任何通信了,然后主动发了一次Ping包之后,还很长时间没有返回。你说是坏了不?自己看着办吧。 * */ handler.keepAliveRequestTimedOut(this, session); } /** * 标记状态为读空闲,时间为设定的请求超时时间。 * */ private void markStatus(IoSession session) { session.getConfig().setIdleTime(interestedIdleStatus, 0); session.getConfig().setReaderIdleTime(getRequestTimeout()); session.setAttribute(WAITING_FOR_RESPONSE); } /** * 重置状态为最初设置。然后继续按照心跳频率监听。 * */ private void resetStatus(IoSession session) { session.getConfig().setReaderIdleTime(0); session.getConfig().setWriterIdleTime(0); session.getConfig().setIdleTime(interestedIdleStatus, getRequestInterval()); session.removeAttribute(WAITING_FOR_RESPONSE); } /** * 是否为心跳包 * */ private boolean isKeepAliveMessage(IoSession session, Object message) { return messageFactory.isRequest(session, message) || messageFactory.isResponse(session, message); }}
0 0
- Mina 心跳KeepAliveFilter 源码解读
- MINA源码分析---心跳包过滤器KeepAliveFilter
- 基于MINA实现server端心跳检测(KeepAliveFilter)
- 基于MINA实现server端心跳检测(KeepAliveFilter)
- MINA源码解读(二)
- MINA源码解读(一)
- Mina 粘包断包解码 CumulativeProtocolDecoder 源码解读
- mina 服务端实现心跳
- MINA 心跳协议
- mina 服务端实现心跳
- mina 心跳机制
- MINA心跳协议
- mina 2 心跳包
- 【MINA】心跳机制
- MINA心跳协议
- mina 心跳机制
- mina心跳过滤器
- mina 心跳机制
- 手机摔坏文件怎么恢复
- centos 6.4上安装rabbitmq server
- scanf中的%*s的解疑
- SQL存储过程
- 关于access中like的用法
- Mina 心跳KeepAliveFilter 源码解读
- 农夫养牛问题
- 跑马问题
- QQ密码的MD5加密-QQ农场研究手札
- 关于strcpy函数的说明
- QQ空间登录的http包分析-QQ农场研究手札
- cocos2d-x 创建工程详解
- 安全类型转换
- class与struct的异同