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
原创粉丝点击