`
yaojialing
  • 浏览: 252817 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

nio框架apache mina使用经验总结(转)

阅读更多

最近做的一个项目用到了开源的C/S应用的服务器框架MINA,当初做的时候资料非常少,只能自己不停的测试,总结出了一些规律经验。

从网上看的资料上看,这个服务器框架还是比较稳定和支持的并发数还是很不错的,不过没有准确的数据,而且我做完的时候也没有拿到真正的实际环境中测试过,用的时候也发现了很多优点和缺点,使用者可以自己去根据自己的使用需求去衡量是否使用该框架。

服务器是商业系统很重要的一部分,主要负责数据采集,文件分发,与端机的通信,和自动作业等任务,服务器大多是24小时运行的,因此服务器的实现必须强壮、稳定、安全,而速度虽然也是很重要,不过最重要的还是前三者。服务器框架MINA就是要为这样的服务器提供了一个网络应用框架,当然这样的服务器框架也可以自己去实现。MINA为我们封装了socket的底层通信实现,提供了日志,线程池等功能,使用起来非常简单、方便。

MINA是一个异步框架,是通过网络事件激发的,它包含两层:IO层和协议层。首先介绍IO层,要说明的是我用的版本是0.8.2,可能不同版本会稍有不同。

Client产生一个底层IO事件,比如说连接和发送数据包,IoAcceptor执行所有底层IO,将他们翻译成抽象的IO事件,接着这里可以添加(也可以部添加)一个IoFilters对IO事件进行过滤,并把翻译过的事件或过滤过的事件和关联的IoSession 发送给IoHandler。IoSession是一个有效的网络连接会话,此会话将一直保持连接,除非网络断开或用户主动断开连接(session.close()),用户可以通过IoSession获得有关该会话连接的信息和配置会话的对象和属性;IoHandler是网络事件的监听器,也就是说当有网络事件发生时会通知IoHandler,用户不用去主动接受数据。用户只要实现此接口爱干吗干吗去吧。IoFilter:Io过滤器,对Io事件进行过滤,比如添加日志过滤器和线程池过滤器。

使用说明:

import java.util.logging.Level;

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.SessionConfig;
import org.apache.mina.io.IoHandlerAdapter;
import org.apache.mina.io.IoSession;
import org.apache.mina.io.socket.SocketSessionConfig;
import org.apache.mina.util.SessionLog;

public class ServerHandler extends IoHandlerAdapter {

  public ServerHandler() {

 }

 public void dataRead(IoSession session, ByteBuffer buffer) throws Exception {

//当有数据读入时此方法被调用,数据封装在ByteBuffer中,可以用以下方法对出buffer的数据,ByteBuffer的数据读出后内存中就没有了。
//String message = "";
//  byte[] bytes = new byte[rb.remaining()];
//  int j = 0;                                                                   //  while (rb.hasRemaining()) {
 //  bytes[j++] = rb.get();
 // }
 //  message = new String(bytes); 

//接着可以进行逻辑处理

}

 public void dataWritten(IoSession session, Object mark) throws Exception {

//当数据被写入通道时此方法被调用,实际就是调用了session.write(IoSession,Object)方法
  SessionLog.log(Level.INFO,session,mark.toString());//必要时打印所写入的内容,mark的内容就是session.write(session,mark)中的第二个参数
 }

 public void exceptionCaught(IoSession session, Throwable arg1)
   throws Exception {

//当出现异常时此方法被调用,从而进行各种异常处理,该方法可以捕捉网络异常(如连接非正常关闭)和所有其他方法产生的异常,这里要注意如果客户端要保持与服务器端的连接时不要在这里马上重新连接不然会抛出CancelKeyException运行期异常直接导致程序死掉(特别是与服务器端有超过两个连接时一定会发生并且此异常无法捕获),建议的方法是启动一个单独的线程来完成与服务器端的重新连接,还有要注意的是如果网络是正常关闭的,比如说是客户端正常关闭连接,而此时服务器端是不愿意关闭的话,这个异常本方法是捕捉不了的,因此只能在session.close()方法中处理这种情况。
  session.close();
  
 }

 public void sessionClosed(IoSession session) throws Exception {

//当网络连接被关闭是此方法被调用
  SessionLog.log(Level.INFO,session,"Close a Session");//必要时打印出信息
 }

 public void sessionCreated(IoSession session) throws Exception {

//当网络连接被创建时此方法被调用(这个肯定在sessionOpened(IoSession session)方法之前被调用),这里可以对Socket设置一些网络参数
  SessionConfig cfg = session.getConfig();
  if (cfg instanceof SocketSessionConfig) {
   ((SocketSessionConfig) cfg).setSessionReceiveBufferSize(2048);
   ((SocketSessionConfig) cfg).setKeepAlive(true);
   ((SocketSessionConfig) cfg).setSoLinger(true, 0);
   ((SocketSessionConfig) cfg).setTcpNoDelay(true);
   ((SocketSessionConfig) cfg).setWriteTimeout(1000 * 5);
  }
 }

 public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception {
  // 当网络通道空闲时此方法被调用,在这里可以判断是读空闲、写空闲还是两个都空闲,以便做出正确的处理

一半的网络通讯程序都要与服务器端保持长连接,所以这里可以发一下网络测试数据以保持与服务器端的连接
 }

 public void sessionOpened(IoSession session) throws Exception {

//当网络连接被打开时此方法被调用,这里可以对session设置一些参数或者添加一些IoFilter的实现,也可以对客户端做一些认证之类的工作
  session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 60);
 }

}

//启动监听连接的服务器

import org.apache.mina.common.*;
import org.apache.mina.io.*;
import org.apache.mina.io.filter.*;
import org.apache.mina.registry.*;

public class Server{
    /** Choose your favorite port number. */
    private static final int PORT = 8080;
    
    public static void main( String[] args ) throws Exception
    {
        ServiceRegistry registry = new SimpleServiceRegistry();

//可以添加各种过滤器,比如线程池过滤器,增加一个线程池处理来自不同的连接

   IoAcceptor ioAcceptor = registry.getIoAcceptor();
   IoThreadPoolFilter ioThreadPoolFilter = new IoThreadPoolFilter();
   ioThreadPoolFilter.setMaximumPoolSize(10);
   ioThreadPoolFilter.start();
   ioAcceptor.getFilterChain().addLast("IoThreadPool",
     ioThreadPoolFilter);
       
// Bind
        Service service = new Service( "serviceName",
TransportType.SOCKET, PORT );
        registry.bind( service, new ServerHandler() );

        System.out.println( "Listening on port " + PORT );
    }
}

//如果是连接服务器的可以如下启动连接请求

import org.apache.mina.io.filter.IoThreadPoolFilter;
import org.apache.mina.io.socket.SocketConnector;
import java.net.InetSocketAddress;

public class Client{
      public static void main( String[] args ) throws Exception
    {
    private static final int CONNECT_TIMEOUT = 3; //设置超时连接时间

//可以添加各种过滤器,比如线程池过滤器,增加一个线程池处理来自不同的连接

IoThreadPoolFilter ioThreadPoolFilter = new IoThreadPoolFilter();
   ioThreadPoolFilter.setMaximumPoolSize(10);
   ioThreadPoolFilter.start();
   SocketConnector connector = new SocketConnector();

   connector.getFilterChain().addFirst("threadPool",
     ioThreadPoolFilter);

//初始化客户端的监听处理器
   ClientHandler clientHandler = new ClientHandler();  

  InetSocketAddress address = new InetSocketAddress("serverIp",serverPort);

  try {

connector.connect(address, CONNECT_TIMEOUT,
        clientHandler);      

System.out.println("connect sucessfully!");

   } catch(Exception e){

    System.err.println("Failed to connect.");

}   
}

如果一个协议非常复杂,如果只用一个Io层是非常复杂的,因为IO层没有帮助你分离‘message解析’和‘实际的业务逻辑,MINA提供了一个协议层来解决这个问题。

 

使用协议层必须实现5个接口:ProtocolHandler, ProtocolProvider, ProtocolCodecFactory, ProtocolEncoder, 和 ProtocolDecoder

第一步:实现ProtocolDecoder和ProtocolEncoder,当有IO事件时便先调用这两个类的方法

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.protocol.ProtocolDecoderOutput;
import org.apache.mina.protocol.ProtocolSession;
import org.apache.mina.protocol.ProtocolViolationException;
import org.apache.mina.protocol.codec.MessageDecoder;
import org.apache.mina.protocol.codec.MessageDecoderResult;
import java.util.*;

public class ServerDecoder implements MessageDecoder {

 public ServerTranInfoDecoder() {

 }


 public MessageDecoderResult decodable(ProtocolSession session, ByteBuffer in) {

//对接受的数据判断是否与协议相同,如果相同返回MessageDecoderResult.OK,否则返回MessageDecoderResult.NOT_OK,这里如果要从ByteBuffer读出数据,需要重新用ByteBuffer.put(ByteBuffer)放回内存中,以便decode方法使用;
  return MessageDecoderResult.OK;
  return MessageDecoderResult.NOT_OK;
 }

  public MessageDecoderResult decode(ProtocolSession session, ByteBuffer in,
   ProtocolDecoderOutput out) throws ProtocolViolationException {

//根据协议将介绍到的数据(放在ByteBuffer中)组装成相对应的实体,调用out.write(Object)方法发送给协议层进行业务逻辑的处理,如果成功返回MessageDecoderResult.OK,否则返回MessageDecoderResult.NOT_OK;

out.write(object);

  }

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.protocol.ProtocolEncoderOutput;
import org.apache.mina.protocol.ProtocolSession;
import org.apache.mina.protocol.ProtocolViolationException;
import org.apache.mina.protocol.codec.MessageEncoder;
import java.util.*;

public class ServerEncoder implements MessageEncoder{
 public static Set TYPES;
 
    public ServerEncoder(){
     
    }
   
    public Set getMessageTypes(){
     HashSet set = new HashSet();
     set.add(Send.class);  //增加要进行编码的实体类
     TYPES = Collections.unmodifiableSet( set );
        return TYPES; 
    }
 

public void encode( ProtocolSession session, Object message, ProtocolEncoderOutput out )
                                                    throws ProtocolViolationException {
    //将回应报文实体message编码层returnStr后发送到客户端  

  byte[] bytes = returnStr.getBytes();
  ByteBuffer rb = ByteBuffer.allocate(bytes.length);
  rb.put(bytes);
  rb.flip();
  out.write(rb);
   }
}

第二步:实现ProtocolCodecFactory

import org.apache.mina.protocol.codec.DemuxingProtocolCodecFactory;

public class ServerProtocolCodecFactory extends DemuxingProtocolCodecFactory {
 public ServerProtocolCodecFactory(boolean server) {
  if (server) {
   super.register(ServerDecoder.class);
   super.register(ServerEncoder.class);
  }
 }
}

第三步:实现ProtocolHandler,在有IO事件发生后,经过decode和encode的处理后就把协议实体交个这个处理器进行业务逻辑的处理,因此实现了协议解释和业务逻辑的分离,它与IoHandler非常相似,不过这里处理的是经过编码与解码后的对象实体。

import org.apache.mina.common.IdleStatus;
import org.apache.mina.protocol.ProtocolSession;
import org.apache.mina.util.SessionLog;
import org.apache.mina.protocol.handler.DemuxingProtocolHandler;

public class ServerSessionHandler extends DemuxingProtocolHandler {
 public ServerSessionHandler() {
 }

 public void sessionCreated(ProtocolSession session) throws Exception {
 }

 public void sessionOpened(ProtocolSession session) throws Exception {
    session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 60);
  }

 public void sessionClosed(ProtocolSession session) {
  }

  public void messageReceived(ProtocolSession session, Object message)
   throws Exception {

//根据解码后的message,进行业务逻辑的处理
session.close();
   }

 public void messageSent(ProtocolSession session, Object message) {
   }

  public void sessionIdle(ProtocolSession session, IdleStatus status)
   throws Exception {

//网络出现空闲时进行处理,并关掉连接
    session.close();
  }

 public void exceptionCaught(ProtocolSession session, Throwable cause) {
  cause.printStackTrace();

//处理所有Handler方法抛出的异常,和Mina架构抛出的异常,并关掉连接
    session.close();
 }
}

第四步:实现ProtocolProvider

import org.apache.mina.protocol.*;

public class ServerProtocolProvider implements ProtocolProvider {
 private static final ProtocolCodecFactory CODEC_FACTORY = new SemsProtocolCodecFactory(
   true);

 private static final ProtocolHandler HANDLER = new ServerSessionHandler();

 public ServerProtocolProvider() {

 }

 public ProtocolCodecFactory getCodecFactory() {
  return CODEC_FACTORY;
 }

 public ProtocolHandler getHandler() {
  return HANDLER;
 }
}

这样协议层便完成了,启动时跟IO层的差不多,不过我们还可以在IO层和协议层用两个线程池,如下:

public class Server {
 //服务器的监听端口号
 public static final int SERVER_PORT = 8000;

 public static void main(String[] args) {
    
    //进行服务器的相关配置
   ServiceRegistry registry = new SimpleServiceRegistry();
   IoProtocolAcceptor protocolAcceptor = (IoProtocolAcceptor) registry
     .getProtocolAcceptor(TransportType.SOCKET);
   ProtocolThreadPoolFilter protocolThreadPoolFilter = new ProtocolThreadPoolFilter();
   protocolThreadPoolFilter.setMaximumPoolSize(10);
   protocolThreadPoolFilter.start();
   protocolAcceptor.getFilterChain().addLast("IoProtocolThreadPool",
     protocolThreadPoolFilter);

   IoAcceptor ioAcceptor = protocolAcceptor.getIoAcceptor();
   IoThreadPoolFilter ioThreadPoolFilter = new IoThreadPoolFilter();
   ioThreadPoolFilter.setMaximumPoolSize(10);
   ioThreadPoolFilter.start();
   ioAcceptor.getFilterChain().addLast("IoThreadPool",
     ioThreadPoolFilter);

   Service service = new Service("TranServer", TransportType.SOCKET,
     SERVER_PORT);

   //绑定了刚刚实现的ServerProtocolProvider
   registry.bind(service, new ServerProtocolProvider());

   }

整个MINA框架经常用到的就是这些了,这样的事件触发框架和两层框架使用起来非常方便,不过这种异步框架还是有些非常明显的缺陷:

第一,MINA只会为每个Session分配一个线程,也就是只能一个一个事件按顺序执行,就算你在某个方法执行时产生了新的事件,比如收到新的数据,MINA也会先将该事件缓冲起来,所以你在执行某个方法时是不可能执行dataRead方法的,所以MINA框架是不会阻塞的,要想在一个逻辑方法中实现交互是实现不了的,因此要想出另外的实现方法。

第二,如果客户端发完一个数据给服务器就想马上得到回复,而不等整个业务逻辑执行完,也是实现不到的,因为MINA框架要将整个接收事件处理完了,再把回复信息发给客户端。

第三,如果MINA是作为服务器端等待连接的,当客户端正常关闭后业务逻辑也可继续正常执行,但是如果MINA是连接服务器的客户端,则当服务器关闭后,MINA的session也会关闭。

最后要说明的是MINA使用的线程池是用Leader/Followers Tread Pool实现的,默认最大支持2G的线程。当然MINA框架是开源的,用户可以根据自己的需要改写代码,而其MINA的功能也是不断可以扩展的。

以上是我使用MINA的经验总结,其实MINA的相关文档和例子也介绍了很多了,我这里算是一个总结吧,不过有很多地方只是我的个人见解,不一定正确,如果有不对的,希望高手可以提出。

(转自)http://chinasun84.blog.sohu.com/25466493.html

另外:apache mina 这个nio非阻塞式框架的强大实现主要有openfire等,可以看看openfire connection manager

分享到:
评论
4 楼 lliiqiang 2014-02-27  
软件中出错都是分类和层级的,所以错误减少不代表有进步,关键有结果
3 楼 lliiqiang 2014-02-27  
对于整体来说有一个错误和多个错误一样,但是进度上几个错误是不同的
2 楼 lliiqiang 2014-02-27  
最好要分离,否则相互影响的话就没有办法分离,特别是有的错误出现的时候越早越好,亡羊补牢是对待错误的正确方式,一个框架出现再找错误原因那么大的框架怎么找?系统稳定性是关键,我们使用者根本就无法预测你的错误,只能误以为自己错了,复杂系统更想不到是你错了,需要系统分析测试
1 楼 lliiqiang 2014-02-27  
关键是有jar包冲突,只能调整先后顺序,即自己框架的jar包是第一位的(自己代码可以做成jar包用来调整顺序),系统的放在后面.
结果是关键,对于强者我们可以先防御强迫消耗其力量。对于必然的事情没有分析的必要,但是大多数情况下我们无法相互说服,虽然真理只有一个但是人类在探索中发现真理,只能根据规矩来一点点探索真理

相关推荐

    常见NIO开源框架(MINA、xSocket)学习

    常见NIO开源框架(MINA、xSocket)学习 基于io包的阻塞式socket通信代码简单,在连接数很少的情况下是一个不错的选择。不过实际应用中一个socket服务器采用传统的阻塞式socket方式通信可能会是一场灾难,一路...

    关于apache Mina Server

    深入理解Apache_Mina_(1)----_Mina的几个类 深入理解Apache_Mina_(2)----_与IoFilter相关的几个...深入理解Apache_Mina_(6)----_Java_Nio_ByteBuffer与Mina_ByteBuffer的区别(类图) 相信你们也愿意去下载更有价值的东西

    mina框架使用总结

    nio的经典框架mina 在此基础上发展的netty是nio经典之中的经典

    Apache MINA开发相关jar包

    Apache MINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可靠性的网络应用程序。 apache-mina-2.0.7-bin.zip,apache-mina-2.0.7-src.zip,log4j-1.2.17.zip,slf4j-api-1.6.6.jar,slf4j-api-1.6.6-...

    Apache Mina核心jar包:mina-core-2.0.7

    Apache MINA是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。 当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版...

    Apache Mina Server 是一个网络通信应用框架

    Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高...

    Java springboot 整合mina 框架,nio通讯基础教程,mina框架基础教程.zip

    Java_NIO基础教程、MINA教程, 并附上基于springboot 整合mina的基础平台框架,可以直接使用,也可以借鉴下

    深入理解Apache Mina (6)---- Java Nio ByteBuffer与Mina ByteBuffer的区别

    NULL 博文链接:https://chinaestone.iteye.com/blog/468138

    Apache Mina网络通信应用框架实例

    Apache Mina Server 是一个网络通信应用框架 基于 TCP/IP、UDP/IP协议栈的通信框架 支持串口和虚拟机内部的管道等传输方式 Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用 Mina 提供了事件驱动、异步操作...

    niosocket及其开源框架MINA学习总结收集.pdf

    niosocket及其开源框架MINA学习总结收集.pdf

    Apache MINA使用案例源代码

    Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的...

    apache mina socket实例

    mina简单示例,Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速...

    MINA框架源码

    目前正在使用 MINA 的软件包括有:Apache Directory Project、AsyncWeb、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。

    Apache MINA使用案例

    Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的...

    apache mina

    Mina 2.0.16 简介 代码实例 Mina 是Apache开发的一个开源的网络通信框架,基于java NIO实现。 建立server与client之间的会话连接,对会话消息进行过滤; 非阻塞消息会话机制; 通过自定义的通信协议进行编解码消息。

    springboot 整合mina 源码,nio通讯基础教程,mina框架基础教程.rar

    springboot 整合mian框架tcp通讯源码, 适合初学者另附java nio及mina相关教程

    apache-mina-2.0.4架包及源码各pdf学习教程

    apache-mina-2.0.4 架包 源码 学习教程.apache mina是Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序...

    apache_mina--2.0.7

    关于apache组织的mina通信框架,可替代 java.NIO.Selector 的更好选择, 里面包括 mina 2.07版本的源码,编译好的jar文件,以及一些网络上找来的网摘资料文档 [截至2013年10月止]。 打包文件名:...

    Apache Mina Server 2.0 参考手册

    Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高...

    基于高性能NIO的MINA框架的应用

    基于高性能NIO的MINA框架的应用,剖析MINA框架,提出MINA的简单应用

Global site tag (gtag.js) - Google Analytics