Mina 断线重连
Mina 断线重连
定义:这里讨论的Mina 断线重连是指使用mina作为客户端软件,连接其他提供Socket通讯服务的服务器端。Socket服务器可以是Mina提供的服务器,也可以是C++提供的服务器。

一、断线重连的方式;
1. 在创建Mina客户端时增加一个监听器,或者增加一个拦截器,当检测到Session关闭时,自动进行重连。

2. 在第1种方式的基础上,增加客户端的读写通道空闲检查,当发生Session关闭或者读写空闲时,进行重连。

第一种方式比较传统,优点是简单方便,适合网络稳定、数据量不大(1M带宽以下)的环境;不过缺点是不能对系统级的连接断开阻塞进行捕获。
第二种方式更加精细,基本上能捕获到应用、网络、系统级的断连。
二、重连目的:
在使用Mina做为客户端时,往往因为网络、服务器、应用程序出现问题而导致连接断开,而自动重连,就是解决连接断开的唯一方式。如果网线断开、服务器宕机、应用程序挂了,都是断线的原因,这个时候,通过增加一个监听器或者拦截器,就能实现重连。但是生产环境中,断线的原因可能更复杂:网络不稳定、延时、服务器负载高、服务器或者应用程序的发送或者接收缓冲区满等等问题都可能导致数据传输过程出现类似于断线的情况,这个时候,光检测Session关闭是远远不够的,这个时候就需要一种重连机制,比如读写空闲超过30秒,就进行重连。对于数据不间断、实时性高、数据量大的应用场景,更是实用。
三、实例:
第一种:监听器方式
创建一个监听器实现mina的IoServiceListener接口,里面的方法可以不用写实现
- <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">import org.apache.mina.core.service.IoService;
- import org.apache.mina.core.service.IoServiceListener;
- import org.apache.mina.core.session.IdleStatus;
- import org.apache.mina.core.session.IoSession;
- public class IoListener implements IoServiceListener{
- @Override
- public void serviceActivated(IoService arg0) throws Exception {
- // TODO Auto-generated method stub
- }
- @Override
- public void serviceDeactivated(IoService arg0) throws Exception {
- // TODO Auto-generated method stub
- }
- @Override
- public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception {
- // TODO Auto-generated method stub
- }
- @Override
- public void sessionCreated(IoSession arg0) throws Exception {
- // TODO Auto-generated method stub
- }
- @Override
- public void sessionDestroyed(IoSession arg0) throws Exception {
- // TODO Auto-generated method stub
- }
- }</span>
再创建客户端时加入监听
- <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;"> NioSocketConnector connector = new NioSocketConnector(); //创建连接客户端
- connector.setConnectTimeoutMillis(30000); //设置连接超时
- connector.getSessionConfig().setReceiveBufferSize(10240); // 设置接收缓冲区的大小
- connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小
- // 加入解码器
- TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName("GBK"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
- factory.setDecoderMaxLineLength(10240);
- factory.setEncoderMaxLineLength(10240);
- connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
- connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
- //添加处理器
- connector.setHandler(new IoHandler());
- // 添加重连监听
- connector.addListener(new IoListener() {
- @Override
- public void sessionDestroyed(IoSession arg0) throws Exception {
- for (;;) {
- try {
- Thread.sleep(3000);
- ConnectFuture future = connector.connect();
- future.awaitUninterruptibly();// 等待连接创建成功
- session = future.getSession();// 获取会话
- if (session.isConnected()) {
- logger.info("断线重连[" + connector.getDefaultRemoteAddress().getHostName() + ":" + connector.getDefaultRemoteAddress().getPort() + "]成功");
- break;
- }
- } catch (Exception ex) {
- logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
- }
- }
- }
- });
- for (;;) {
- try {
- ConnectFuture future = connector.connect();
- future.awaitUninterruptibly(); // 等待连接创建成功
- session = future.getSession(); // 获取会话
- logger.info("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- break;
- } catch (RuntimeIoException e) {
- logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
- Thread.sleep(5000);// 连接失败后,重连间隔5s
- }
- }
- </span>
第一种:拦截器方式
- <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;"> connector = new NioSocketConnector(); //创建连接客户端
- connector.setConnectTimeoutMillis(30000); //设置连接超时
- // 断线重连回调拦截器
- connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
- @Override
- public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
- for(;;){
- try{
- Thread.sleep(3000);
- ConnectFuture future = connector.connect();
- future.awaitUninterruptibly();// 等待连接创建成功
- session = future.getSession();// 获取会话
- if(session.isConnected()){
- logger.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功");
- break;
- }
- }catch(Exception ex){
- logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
- }
- }
- }
- });
- TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
- factory.setDecoderMaxLineLength(10240);
- factory.setEncoderMaxLineLength(10240);
- //加入解码器
- connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
- //添加处理器
- connector.setHandler(new IoHandler());
- connector.getSessionConfig().setReceiveBufferSize(10240); // 设置接收缓冲区的大小
- connector.getSessionConfig().setSendBufferSize(10240); // 设置输出缓冲区的大小
- connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
- for (;;) {
- try {
- ConnectFuture future = connector.connect();
- // 等待连接创建成功
- future.awaitUninterruptibly();
- // 获取会话
- session = future.getSession();
- logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- break;
- } catch (RuntimeIoException e) {
- logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
- Thread.sleep(5000);// 连接失败后,重连间隔5s
- }
- }</span>
第二种:加入空闲检测机制
空闲检测机制需要在创建客户端时,加入空闲超时,然后在处理器handler端的sessionIdle方法中加入一个预关闭连接的方法。让Session关闭传递到监听器或者拦截器的sessionClose方法中实现重连。
以拦截器方式为例,在创建客户端时,加入读写通道空闲检查超时机制。
- <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;"> connector = new NioSocketConnector(); //创建连接客户端
- connector.setConnectTimeoutMillis(30000); //设置连接超时
- // 断线重连回调拦截器
- connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
- @Override
- public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
- for(;;){
- try{
- Thread.sleep(3000);
- ConnectFuture future = connector.connect();
- future.awaitUninterruptibly();// 等待连接创建成功
- session = future.getSession();// 获取会话
- if(session.isConnected()){
- logger.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功");
- break;
- }
- }catch(Exception ex){
- logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
- }
- }
- }
- });
- connector.getFilterChain().addLast("mdc", new MdcInjectionFilter());
- TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
- factory.setDecoderMaxLineLength(10240);
- factory.setEncoderMaxLineLength(10240);
- //加入解码器
- connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
- connector.getSessionConfig().setReceiveBufferSize(10240); // 设置接收缓冲区的大小
- connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小
- connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30000); //读写都空闲时间:30秒
- connector.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 40000);//读(接收通道)空闲时间:40秒
- connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE, 50000);//写(发送通道)空闲时间:50秒
- //添加处理器
- connector.setHandler(new IoHandler());
- connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
- for (;;) {
- try {
- ConnectFuture future = connector.connect();
- // 等待连接创建成功
- future.awaitUninterruptibly();
- // 获取会话
- session = future.getSession();
- logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- break;
- } catch (RuntimeIoException e) {
- System.out.println("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage());
- logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
- Thread.sleep(5000);// 连接失败后,重连10次,间隔30s
- }
- }</span>
然后在数据处理器IoHandler中sessionIdle方法中加入Session会话关闭的代码,这样session关闭就能传递到拦截器或者监听器中,然后实现重连。
- <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">import org.apache.mina.core.service.IoHandlerAdapter;
- import org.apache.mina.core.session.IdleStatus;
- import org.apache.mina.core.session.IoSession;
- public class IoHandler extends IoHandlerAdapter {
- //部分代码忽略...
- @Override
- public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
- logger.info("-客户端与服务端连接[空闲] - " + status.toString());
- if(session != null){
- session.close(true);
- }
- }
- //部分代码忽略...
- }</span>
总结-最佳实践:
以上两种方式我个人认为最好是使用第二种。在实际的生产环境,对于数据量比较少的情况下,需要加一个线程专门发送心跳信息,然后在服务器端进行回应心跳,这样就保证读写通道不出现空闲。如果数据量比较大,大到24小时都有数据,那么就不需要心跳线程,可以直接在IoHandler处理器端中messageReceived方法中定时发送心跳到服务器。由于读写监控还可以处理服务器、网络、应用等等方面的不确定因素,所以建议使用第二种方式。
Mina 断线重连的更多相关文章
- Mina.Net实现的断线重连
using Mina.Filter.Codec; using Mina.Filter.Codec.TextLine; using System; using System.Collections.Ge ...
- 关于socket tcp 断线重连
这个问题困扰过我几次,都没有来得及研究,今天研究一下. 首先写一个最简易的socket tcp程序,连接成功后再关闭服务器然后再用客户端各种操作看是什么情况 测试表明 (1)客户端已经连接,当服务端关 ...
- netty4 断线重连
转载:http://www.tuicool.com/articles/B7RzMbY 一 实现心跳检测 原理:当服务端每隔一段时间就会向客户端发送心跳包,客户端收到心跳包后同样也会回一个心跳包给服务端 ...
- Netty 客户端断线重连
client 关闭后会执行 finally 代码块,可以在这里可以进行重连操作 public class NettyClient implements Runnable { private final ...
- ActiveMQ的断线重连机制
断线重连机制是ActiveMQ的高可用性具体体现之一.ActiveMQ提供failover机制去实现断线重连的高可用性,可以使得连接断开之后,不断的重试连接到一个或多个brokerURL. 默认情况下 ...
- ADOConnection断线重连
问题: ADOConnection断线重连问题描述: 使用ADOConnection连接oracle数据库,开始正常,当网络断开时数据库连接失败(此时查询ADOConnection.connected ...
- 发现电脑上装着liteide,就用golang做一个TCP通讯测试(支持先启动client端和断线重连)
1.参考https://www.cnblogs.com/yin5th/p/9274495.html server端 main.go package main import ( "fmt&qu ...
- Netty断线重连
Netty断线重连 最近使用Netty开发一个中转服务,需要一直保持与Server端的连接,网络中断后需要可以自动重连,查询官网资料,实现方案很简单,核心思想是在channelUnregistered ...
- 浅谈IM软件client的断线重连、心跳和长在线
版权声明:原创文章,未经博主同意禁止转载.欢迎点击头像上方"郭晓东的专栏"查看专栏 https://blog.csdn.net/hherima/article/details/27 ...
随机推荐
- 正则表达式基础->
描述:(grep) 正则表达式是一种字符模式,用于在查找过程中匹配指定的字符.在大多数程序里,正则表达式都被置于两个正斜杠之间,它匹配被查找的行中任何位置出现的相同模式 基础正则表达式 正则表达式 描 ...
- springboot配置fastjson后端往前端传输格式化
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.Spri ...
- 【前端开发】前端引入公共部分footer header的几种方法,及iframe自适应高度js
一.引入页面几种方法 1.IFrame引入,看看下面的代码 <iframe frameborder=0 border=0 width=300 height=300 ...
- SQLAlchemy-介绍安装
一:概述 SQLAlchemy的SQL工具包和对象关系映射是一个全面的工具集,用来处理数据库和Python. 它有几个不同的功能领域,可以单独使用或组合使用. 所示的主要组件,组件依赖关系组织成层: ...
- 数据库-mysql触发器
MySQL包含对触发器的支持.触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行. 一:创建触发器 在MySQL中,创建触发器语法 ...
- python类中的私有方法
假设有如下一个python类: class Foo(object): def __a(self): print "Bet you can't see me..." def bar( ...
- MySQL学习笔记:少用Null
在实际编程中,Null容易引起很多问题,例如在Java里NullPointerException猝不及防的空指针异常,因此需要过多的if判断,甚是麻烦. 在MySQL数据库中也要少用Null,尽量保持 ...
- Jenkins在Linux环境安装
Jenkins介绍 Jenkins是基于Java开发的一种持续集成工具,用于监控持续重复的工作,功能包括: 1.持续的软件版本发布/测试项目. 2.监控外部调用执行的工作. 安装环境 操作系统:lin ...
- who am i ?
Id:Ox9A82 Email:hucvbty@gmail.com 微博:http://weibo.com/1828621423 知乎:Ox9A82 常乐村男子职业技术学院 Syclover拖后腿成员 ...
- mysql-noinstall.zip免安装版的优化配置和精简
1.准备工作 下载mysql的最新免安装版本mysql-noinstall-5.5.25a-win32.zip,解压缩到相关目录,如:d:\\ mysql-noinstall-5.1.53-win32 ...