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)可以实现多方位的计算地图上的路线指示,路径行程等功能,比如说实现 ...
随机推荐
- 随着firefox的迭代更新:FireBug不能用了?使用火狐Try Xpath插件替代Firebug和Firepath
本篇文章讲解如何在火狐中安装和使用Try Xpath(插件替代Firebug和Firepath). 在火狐中安装Try Xpath 1. 打开火狐浏览器 FireFox57以上的版本 2. 在火狐菜单 ...
- OpenCL 查询平台和设备
▶ 查询平台和设备的代码以结果,放在这里方便以后逐渐扩充和查询(没有营养) #include <stdio.h> #include <stdlib.h> #include &l ...
- controller检查header
以前都只能拿到request再检查,其实有相应的注解. public Result updateRecentScore(@RequestBody Map map, @RequestHeader(&qu ...
- border做三角符号
用border做三角符号以及其他图形 ;; border-width:20px 10px; border-style:solid; border-color:#ff3300 #ff3300 #ffff ...
- requests bs4 爬取 资讯 图片
#!/usr/bin/env python # Version = 3.5.2 # __auth__ = '无名小妖' import requests from bs4 import Beautifu ...
- VS 类快捷键
生成类的构造函数 输入 ctrl,按两下 TAB 键 快速添加属性输入prop,按2下tab键 添加折叠输入reg,按2下tab键,快速输入#region输入class,按下2次tab建,快速输入类定 ...
- delphi const的用法
unit RadKeygen; interface uses Classes,SysUtils,Windows; function fun1():string; implementation cons ...
- OTS parsing error: invalid version tag woff和ttf文件被Filter拦截
从服务器下载的字体文件放在本地,执行无法展示iconfont,浏览器控制台报出 Failed to decode downloaded font: http://127.0.0.1:8080/mhr/ ...
- 【OpenPose-Windows】运行OpenposeDemo.exe 如何保存图像运行结果及关节点信息
跑过很多次openposeDemo了,不管是video.Webcam还是Images,都是运行结果一闪而过,然而我们所要的肯定不是只看一下运行结果就完事的,很多情况下,我们都希望能够把运行结果的图像. ...
- box2d 易错
1.动态刚体与一个与静态刚体重叠的小的感应刚体在contactBegin时,有些时候无法侦测到 2.bodyA.GetContactList()应用非动态刚体,会找不到另一非动态刚体的接触,非动态刚体 ...