Mina 断线重连

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

一、断线重连的方式;

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

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

第一种方式比较传统,优点是简单方便,适合网络稳定、数据量不大(1M带宽以下)的环境;不过缺点是不能对系统级的连接断开阻塞进行捕获。

第二种方式更加精细,基本上能捕获到应用、网络、系统级的断连。

二、重连目的:

在使用Mina做为客户端时,往往因为网络、服务器、应用程序出现问题而导致连接断开,而自动重连,就是解决连接断开的唯一方式。如果网线断开、服务器宕机、应用程序挂了,都是断线的原因,这个时候,通过增加一个监听器或者拦截器,就能实现重连。但是生产环境中,断线的原因可能更复杂:网络不稳定、延时、服务器负载高、服务器或者应用程序的发送或者接收缓冲区满等等问题都可能导致数据传输过程出现类似于断线的情况,这个时候,光检测Session关闭是远远不够的,这个时候就需要一种重连机制,比如读写空闲超过30秒,就进行重连。对于数据不间断、实时性高、数据量大的应用场景,更是实用。

三、实例:

第一种:监听器方式

创建一个监听器实现mina的IoServiceListener接口,里面的方法可以不用写实现

  1. <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">import org.apache.mina.core.service.IoService;
  2. import org.apache.mina.core.service.IoServiceListener;
  3. import org.apache.mina.core.session.IdleStatus;
  4. import org.apache.mina.core.session.IoSession;
  5. public class IoListener implements IoServiceListener{
  6. @Override
  7. public void serviceActivated(IoService arg0) throws Exception {
  8. // TODO Auto-generated method stub
  9. }
  10. @Override
  11. public void serviceDeactivated(IoService arg0) throws Exception {
  12. // TODO Auto-generated method stub
  13. }
  14. @Override
  15. public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception {
  16. // TODO Auto-generated method stub
  17. }
  18. @Override
  19. public void sessionCreated(IoSession arg0) throws Exception {
  20. // TODO Auto-generated method stub
  21. }
  22. @Override
  23. public void sessionDestroyed(IoSession arg0) throws Exception {
  24. // TODO Auto-generated method stub
  25. }
  26. }</span>

再创建客户端时加入监听

  1. <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">        NioSocketConnector connector = new NioSocketConnector();  //创建连接客户端
  2. connector.setConnectTimeoutMillis(30000); //设置连接超时
  3. connector.getSessionConfig().setReceiveBufferSize(10240);   // 设置接收缓冲区的大小
  4. connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小
  5. //      加入解码器
  6. TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName("GBK"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
  7. factory.setDecoderMaxLineLength(10240);
  8. factory.setEncoderMaxLineLength(10240);
  9. connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
  10. connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
  11. //添加处理器
  12. connector.setHandler(new IoHandler());
  13. // 添加重连监听
  14. connector.addListener(new IoListener() {
  15. @Override
  16. public void sessionDestroyed(IoSession arg0) throws Exception {
  17. for (;;) {
  18. try {
  19. Thread.sleep(3000);
  20. ConnectFuture future = connector.connect();
  21. future.awaitUninterruptibly();// 等待连接创建成功
  22. session = future.getSession();// 获取会话
  23. if (session.isConnected()) {
  24. logger.info("断线重连[" + connector.getDefaultRemoteAddress().getHostName() + ":" + connector.getDefaultRemoteAddress().getPort() + "]成功");
  25. break;
  26. }
  27. } catch (Exception ex) {
  28. logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
  29. }
  30. }
  31. }
  32. });
  33. for (;;) {
  34. try {
  35. ConnectFuture future = connector.connect();
  36. future.awaitUninterruptibly(); // 等待连接创建成功
  37. session = future.getSession(); // 获取会话
  38. logger.info("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
  39. break;
  40. } catch (RuntimeIoException e) {
  41. logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
  42. Thread.sleep(5000);// 连接失败后,重连间隔5s
  43. }
  44. }
  45. </span>

第一种:拦截器方式

  1. <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">        connector = new NioSocketConnector();  //创建连接客户端
  2. connector.setConnectTimeoutMillis(30000); //设置连接超时
  3. //      断线重连回调拦截器
  4. connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
  5. @Override
  6. public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
  7. for(;;){
  8. try{
  9. Thread.sleep(3000);
  10. ConnectFuture future = connector.connect();
  11. future.awaitUninterruptibly();// 等待连接创建成功
  12. session = future.getSession();// 获取会话
  13. if(session.isConnected()){
  14. logger.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功");
  15. break;
  16. }
  17. }catch(Exception ex){
  18. logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
  19. }
  20. }
  21. }
  22. });
  23. TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
  24. factory.setDecoderMaxLineLength(10240);
  25. factory.setEncoderMaxLineLength(10240);
  26. //加入解码器
  27. connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
  28. //添加处理器
  29. connector.setHandler(new IoHandler());
  30. connector.getSessionConfig().setReceiveBufferSize(10240);   // 设置接收缓冲区的大小
  31. connector.getSessionConfig().setSendBufferSize(10240);          // 设置输出缓冲区的大小
  32. connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
  33. for (;;) {
  34. try {
  35. ConnectFuture future = connector.connect();
  36. // 等待连接创建成功
  37. future.awaitUninterruptibly();
  38. // 获取会话
  39. session = future.getSession();
  40. logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
  41. break;
  42. } catch (RuntimeIoException e) {
  43. logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
  44. Thread.sleep(5000);// 连接失败后,重连间隔5s
  45. }
  46. }</span>

第二种:加入空闲检测机制

空闲检测机制需要在创建客户端时,加入空闲超时,然后在处理器handler端的sessionIdle方法中加入一个预关闭连接的方法。让Session关闭传递到监听器或者拦截器的sessionClose方法中实现重连。

以拦截器方式为例,在创建客户端时,加入读写通道空闲检查超时机制。

  1. <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">        connector = new NioSocketConnector();  //创建连接客户端
  2. connector.setConnectTimeoutMillis(30000); //设置连接超时
  3. //      断线重连回调拦截器
  4. connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
  5. @Override
  6. public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
  7. for(;;){
  8. try{
  9. Thread.sleep(3000);
  10. ConnectFuture future = connector.connect();
  11. future.awaitUninterruptibly();// 等待连接创建成功
  12. session = future.getSession();// 获取会话
  13. if(session.isConnected()){
  14. logger.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功");
  15. break;
  16. }
  17. }catch(Exception ex){
  18. logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
  19. }
  20. }
  21. }
  22. });
  23. connector.getFilterChain().addLast("mdc", new MdcInjectionFilter());
  24. TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
  25. factory.setDecoderMaxLineLength(10240);
  26. factory.setEncoderMaxLineLength(10240);
  27. //加入解码器
  28. connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
  29. connector.getSessionConfig().setReceiveBufferSize(10240);   // 设置接收缓冲区的大小
  30. connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小
  31. connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30000);  //读写都空闲时间:30秒
  32. connector.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 40000);//读(接收通道)空闲时间:40秒
  33. connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE, 50000);//写(发送通道)空闲时间:50秒
  34. //添加处理器
  35. connector.setHandler(new IoHandler());
  36. connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
  37. for (;;) {
  38. try {
  39. ConnectFuture future = connector.connect();
  40. // 等待连接创建成功
  41. future.awaitUninterruptibly();
  42. // 获取会话
  43. session = future.getSession();
  44. logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
  45. break;
  46. } catch (RuntimeIoException e) {
  47. System.out.println("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage());
  48. logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
  49. Thread.sleep(5000);// 连接失败后,重连10次,间隔30s
  50. }
  51. }</span>

然后在数据处理器IoHandler中sessionIdle方法中加入Session会话关闭的代码,这样session关闭就能传递到拦截器或者监听器中,然后实现重连。

  1. <span style="font-family: 'Microsoft YaHei',微软雅黑,SimHei,tahoma,arial,helvetica,sans-serif;">import org.apache.mina.core.service.IoHandlerAdapter;
  2. import org.apache.mina.core.session.IdleStatus;
  3. import org.apache.mina.core.session.IoSession;
  4. public class IoHandler extends IoHandlerAdapter {
  5. //部分代码忽略...
  6. @Override
  7. public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
  8. logger.info("-客户端与服务端连接[空闲] - " + status.toString());
  9. if(session != null){
  10. session.close(true);
  11. }
  12. }
  13. //部分代码忽略...
  14. }</span>

总结-最佳实践:

以上两种方式我个人认为最好是使用第二种。在实际的生产环境,对于数据量比较少的情况下,需要加一个线程专门发送心跳信息,然后在服务器端进行回应心跳,这样就保证读写通道不出现空闲。如果数据量比较大,大到24小时都有数据,那么就不需要心跳线程,可以直接在IoHandler处理器端中messageReceived方法中定时发送心跳到服务器。由于读写监控还可以处理服务器、网络、应用等等方面的不确定因素,所以建议使用第二种方式。

Mina 断线重连的更多相关文章

  1. Mina.Net实现的断线重连

    using Mina.Filter.Codec; using Mina.Filter.Codec.TextLine; using System; using System.Collections.Ge ...

  2. 关于socket tcp 断线重连

    这个问题困扰过我几次,都没有来得及研究,今天研究一下. 首先写一个最简易的socket tcp程序,连接成功后再关闭服务器然后再用客户端各种操作看是什么情况 测试表明 (1)客户端已经连接,当服务端关 ...

  3. netty4 断线重连

    转载:http://www.tuicool.com/articles/B7RzMbY 一 实现心跳检测 原理:当服务端每隔一段时间就会向客户端发送心跳包,客户端收到心跳包后同样也会回一个心跳包给服务端 ...

  4. Netty 客户端断线重连

    client 关闭后会执行 finally 代码块,可以在这里可以进行重连操作 public class NettyClient implements Runnable { private final ...

  5. ActiveMQ的断线重连机制

    断线重连机制是ActiveMQ的高可用性具体体现之一.ActiveMQ提供failover机制去实现断线重连的高可用性,可以使得连接断开之后,不断的重试连接到一个或多个brokerURL. 默认情况下 ...

  6. ADOConnection断线重连

    问题: ADOConnection断线重连问题描述: 使用ADOConnection连接oracle数据库,开始正常,当网络断开时数据库连接失败(此时查询ADOConnection.connected ...

  7. 发现电脑上装着liteide,就用golang做一个TCP通讯测试(支持先启动client端和断线重连)

    1.参考https://www.cnblogs.com/yin5th/p/9274495.html server端 main.go package main import ( "fmt&qu ...

  8. Netty断线重连

    Netty断线重连 最近使用Netty开发一个中转服务,需要一直保持与Server端的连接,网络中断后需要可以自动重连,查询官网资料,实现方案很简单,核心思想是在channelUnregistered ...

  9. 浅谈IM软件client的断线重连、心跳和长在线

    版权声明:原创文章,未经博主同意禁止转载.欢迎点击头像上方"郭晓东的专栏"查看专栏 https://blog.csdn.net/hherima/article/details/27 ...

随机推荐

  1. 一、springcloud服务注册、发现、调用(consul/eureka)

    1.Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全 ...

  2. spring学习之一概念

    概念 1.是开源的轻量级框架 2.是一站式框架,就是说在java ee的三层结构中,每一层它都提供了不同的解决技术 web层:springMVC servoce层:spring IOC ,控制反转,通 ...

  3. local class incompatible: stream classdesc serialVersionUID = -2897844985684768944, local class serialVersionUID = 7350468743759137184

    local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2427 ...

  4. python面向对象(四)之抽象类与接口

    ​ 学过java的应该知道java有抽象类和接口的那么python呢?(以前写的关于java抽象类的笔记java抽象类与接口) python作为一个动态语言,没有强类型的检查,而是以鸭子类型的方式提现 ...

  5. AT994 【11の倍数】

    超短AC代码压行小技巧 #include<iostream> using namespace std; string s; ]; int main() { cin>>s; in ...

  6. MySQL约束笔记

    MySQL 查看约束,添加约束,删除约束 添加列,修改列,删除列 · 查看表的字段信息:desc 表名; · 查看表的所有信息:show create table 表名; 添加主键约束:alter t ...

  7. 20165203迭代和JDB测试

    1.使用C(n,m)=C(n-1,m-1)+C(n-1,m)公式进行递归编程实现求组合数C(m,n)的功能 public class C { public static void main(Strin ...

  8. dos批处理知识

    echo 命令 rem 命令 pause 命令 call 命令 start 命令 goto 命令 set 命令 编辑本段批处理符号简介 回显屏蔽 重定向1 与 重定向2 管道符号 转义符 逻辑命令符 ...

  9. Linux mint 17.3系统安装及常用开发办公软件部署

    关于为什么选择linuxmint17.3作为个人办公开发系统的选择说明: 编者按]提起Linux系统,大家可能最先想到的就是 Linux Mint 和 Ubuntu 两个版本了.近来,开源界貌似激进了 ...

  10. MemSQL Start[c]UP 2.0 - Round 1 F - Permutation 思维+线段树维护hash值

    F - Permutation 思路:对于当前的值x, 只需要知道x + k, x - k这两个值是否出现在其左右两侧,又因为每个值只有一个, 所以可以转换成,x+k, x-k在到x所在位置的时候是否 ...