MINA断线重连

来源:互联网 发布:编程 数学 编辑:程序博客网 时间:2024/04/28 13:37

转: http://chwshuang.iteye.com/blog/2028951


Mina 断线重连

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

 



    
 
 

 

一、断线重连的方式;

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

 
    
 

 

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

 

 
    
 

 

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

       

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

 

二、重连目的:

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

 

 

三、实例:

    第一种:监听器方式

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

Java代码  收藏代码
  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.   
  6. public class IoListener implements IoServiceListener{  
  7.     @Override  
  8.     public void serviceActivated(IoService arg0) throws Exception {  
  9.         // TODO Auto-generated method stub  
  10.     }  
  11.     @Override  
  12.     public void serviceDeactivated(IoService arg0) throws Exception {  
  13.         // TODO Auto-generated method stub  
  14.     }  
  15.     @Override  
  16.     public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception {  
  17.         // TODO Auto-generated method stub  
  18.     }  
  19.     @Override  
  20.     public void sessionCreated(IoSession arg0) throws Exception {  
  21.         // TODO Auto-generated method stub  
  22.     }  
  23.   
  24.     @Override  
  25.     public void sessionDestroyed(IoSession arg0) throws Exception {  
  26.         // TODO Auto-generated method stub  
  27.     }  
  28. }</span>  

     

 再创建客户端时加入监听

Java代码  收藏代码
  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.                 // 添加重连监听  
  15.         connector.addListener(new IoListener() {  
  16.             @Override  
  17.             public void sessionDestroyed(IoSession arg0) throws Exception {  
  18.                 for (;;) {  
  19.                     try {  
  20.                         Thread.sleep(3000);  
  21.                         ConnectFuture future = connector.connect();  
  22.                         future.awaitUninterruptibly();// 等待连接创建成功  
  23.                         session = future.getSession();// 获取会话  
  24.                         if (session.isConnected()) {  
  25.                             logger.info("断线重连[" + connector.getDefaultRemoteAddress().getHostName() + ":" + connector.getDefaultRemoteAddress().getPort() + "]成功");  
  26.                             break;  
  27.                         }  
  28.                     } catch (Exception ex) {  
  29.                         logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());  
  30.                     }  
  31.                 }  
  32.             }  
  33.         });  
  34.                 for (;;) {  
  35.             try {  
  36.                 ConnectFuture future = connector.connect();  
  37.                 future.awaitUninterruptibly(); // 等待连接创建成功    
  38.                         session = future.getSession(); // 获取会话     
  39.                 logger.info("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));  
  40.                 break;  
  41.             } catch (RuntimeIoException e) {  
  42.                 logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);  
  43.                 Thread.sleep(5000);// 连接失败后,重连间隔5s  
  44.             }  
  45.         }  
  46.   
  47. </span>  

 

 

    第一种:拦截器方式

 

Java代码  收藏代码
  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.           
  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.         //添加处理器  
  30.                 connector.setHandler(new IoHandler());  
  31.         connector.getSessionConfig().setReceiveBufferSize(10240);   // 设置接收缓冲区的大小  
  32.         connector.getSessionConfig().setSendBufferSize(10240);          // 设置输出缓冲区的大小  
  33.         connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址  
  34.         for (;;) {  
  35.             try {  
  36.                 ConnectFuture future = connector.connect();  
  37.                 // 等待连接创建成功  
  38.                 future.awaitUninterruptibly();  
  39.                 // 获取会话  
  40.                 session = future.getSession();  
  41.                 logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));  
  42.                 break;  
  43.             } catch (RuntimeIoException e) {  
  44.                 logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);  
  45.                 Thread.sleep(5000);// 连接失败后,重连间隔5s  
  46.             }  
  47.         }</span>  

 

 

 

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

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

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

 

Java代码  收藏代码
  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.           
  24.         connector.getFilterChain().addLast("mdc"new MdcInjectionFilter());  
  25.         TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());  
  26.         factory.setDecoderMaxLineLength(10240);  
  27.         factory.setEncoderMaxLineLength(10240);  
  28.         //加入解码器  
  29.         connector.getFilterChain().addLast("codec"new ProtocolCodecFilter(factory));  
  30.   
  31.         connector.getSessionConfig().setReceiveBufferSize(10240);   // 设置接收缓冲区的大小  
  32.         connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小  
  33.           
  34.         connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30000);  //读写都空闲时间:30秒  
  35.         connector.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 40000);//读(接收通道)空闲时间:40秒  
  36.         connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE, 50000);//写(发送通道)空闲时间:50秒  
  37.           
  38.         //添加处理器  
  39.                 connector.setHandler(new IoHandler());   
  40.           
  41.         connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址  
  42.         for (;;) {  
  43.             try {  
  44.                 ConnectFuture future = connector.connect();  
  45.                 // 等待连接创建成功  
  46.                 future.awaitUninterruptibly();  
  47.                 // 获取会话  
  48.                 session = future.getSession();  
  49.                 logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));  
  50.                 break;  
  51.             } catch (RuntimeIoException e) {  
  52.                 System.out.println("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage());  
  53.                 logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);  
  54.                 Thread.sleep(5000);// 连接失败后,重连10次,间隔30s  
  55.             }  
  56.         }</span>  

 

 

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

 

Java代码  收藏代码
  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.   
  5. public class IoHandler extends IoHandlerAdapter {  
  6.     //部分代码忽略...  
  7.     @Override  
  8.     public void sessionIdle(IoSession session, IdleStatus status) throws Exception {  
  9.         logger.info("-客户端与服务端连接[空闲] - " + status.toString());  
  10.         if(session != null){  
  11.             session.close(true);  
  12.         }  
  13.     }  
  14.     //部分代码忽略...  
  15. }</span>  

 

 总结-最佳实践:

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

 

另外有几点注意事项:

第一:断线重连是针对长连接的,也就是说,连接后两端一直在发送数据。

第二:断线重连是针对客户端的,如果你在服务端使用,可能会有根据场景导致失败。因为服务端会自动发送心跳。

 

第二:断线重连如果测试设置接收超时情况,就应该一直发送数据,而服务端只接收不发送,心跳数据也不发送,到时间后才会起作用。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 老公哪里都好就是那方面不行怎么办 电饭锅盖子卡的地方掉了怎么办 唯品会退货已经收到了不退款怎么办 有人用qq冒充我和别人聊天怎么办 别人冒充我的微信名和头像怎么办 快餐店无证经营被工商局查了怎么办 买的圣才电子书只有在线版怎么办 吃了汤汤水水奶水还是少怎么办 粗肋草叶子和劲长白点怎么办 海鲜泡的时间长了有味了怎么办 富贵竹新长出来的叶子发黄怎么办 家里养的竹子的叶子发黄怎么办 发财树叶子发黄发黑怎么办打蔫 装修公司把衣柜型号弄错了怎么办 隔水炖锅里的陶瓷坏了怎么办? 色选大米带出比大怎么办? 做手足印如果漆刷到外面了怎么办 小区房子整租给民工影响邻居怎么办 种植牙植体中间的螺丝滑丝怎么办 宝宝1岁了牙齿表面脱落了怎么办 1岁3个月宝宝牙齿发黑脱落怎么办 种植牙中央螺栓断在牙根里怎么办 欧联杯冠军已经获得欧冠资格怎么办 孩子该上学了学校不收怎么办 帮室友带东西她忘给钱怎么办? 鸿合录播系统不会自动跟踪了怎么办 小米2s锁屏密码忘了怎么办 想用中国驾照去美国开车怎么办? 5座的车坐7个人怎么办 海尔电视机的设置调乱了怎么办 老公弟兄两个有个偏心的婆婆怎么办 农商银行u盾密码忘了怎么办 不熟的表弟表妹向你借钱怎么办 开货车撞到人家房子了怎么办 坐骨被摔跤后好多年没好怎么办 自动挡汽车电子手刹刹车失灵怎么办 买家拍了不包邮的宝贝付款了怎么办 包邮快递买家不要了运费怎么办 舞蹈劈叉练出肌肉劈不下去怎么办 腰间盘突出压迫神经腿疼怎么办盘 绑定了我身份证的微信被盗了怎么办