`
yaojialing
  • 浏览: 255444 次
  • 性别: 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包用来调整顺序),系统的放在后面.
结果是关键,对于强者我们可以先防御强迫消耗其力量。对于必然的事情没有分析的必要,但是大多数情况下我们无法相互说服,虽然真理只有一个但是人类在探索中发现真理,只能根据规矩来一点点探索真理

相关推荐

    apache mina 中文参考手册

    综上所述,Apache Mina 是一个功能强大、易于使用的网络应用框架,它能够帮助开发者快速构建出高性能和高可靠性的网络应用程序。无论是对于初学者还是有经验的开发者来说,Mina 都是一个值得学习和使用的工具。

    ApacheMina入门

    - **高效性**:Mina 使用 NIO 技术,提供了高性能的 I/O 处理能力。 - **灵活性**:支持多种通信协议和编码方式,易于扩展和定制。 - **易用性**:提供了一套简洁的 API,易于理解和使用,降低了网络编程的复杂度。 ...

    mina

    3. **MINA使用手记[1] .shtml** 和 **MINA框架使用总结 .shtml**:这些可能是个人或团队在使用MINA过程中的笔记和总结,可能包含了实践中的技巧、问题解决策略以及最佳实践。 4. **Apache MINA 线程模型配置 .shtml...

    Mina 框架研究与实现

    ### Mina框架研究与实现 #### 引言 在当今高度网络化的世界中,服务器端程序面临着前所未有的挑战,特别是当需要同时处理...无论是对于初学者还是经验丰富的开发者,Mina框架都是值得深入研究和广泛应用的重要工具。

    Mina案例+使用文档.pdf

    Apache Mina(Multipurpose Infrastructure for Network Applications)是Apache基金会推出的一个用于开发高性能和高可用性网络应用程序的框架。Mina的设计理念旨在简化复杂的网络编程,使得开发者能够更加专注于...

    mina学习总结

    《Mina学习总结》 Apache Mina(全称Mini Assured Networking Abstraction)是一个轻量级、高性能的网络通信框架,广泛应用于Java平台上的网络应用开发。它为开发者提供了简单的API来处理网络通信,简化了TCP/IP和...

    mina 中文参考文档

    总结来说,Apache Mina 是一个功能丰富的网络编程框架,它通过抽象和封装底层网络通信细节,使得开发者可以更专注于业务逻辑的实现。通过上述介绍,我们能够看出 Mina 在网络通信和异步 I/O 操作方面的强大能力,...

    mina用户指南中文版(waylau).zip

    《mina用户指南中文版》是由waylau编译的一份详尽教程,旨在帮助中文使用者更好地理解和使用Apache Mina框架。Apache Mina是一个开源的网络通信应用框架,它简化了高性能、高可用性和高可扩展性的网络应用开发。这份...

    MINA开发手册和JAR包

    MINA(Java IO Network Application Framework)是一个开源的网络应用程序框架,由Apache软件基金会维护,它为构建高性能、高可用性的网络应用提供了丰富的API和工具。MINA的目标是简化网络编程,无论你是在处理TCP/...

    Mina官方教程_中文版.rar

    总结,《Mina官方教程_中文版》提供了全面且深入的指导,适合初学者和有经验的开发者了解和掌握Mina框架,提升网络编程能力。通过学习这份教程,读者将能够运用Mina构建高效的网络应用程序,满足各种业务需求。

    MINA文档

    - **MINA使用手记[1] .shtml**和**MINA框架使用总结 .shtml**:这两篇可能是作者的经验分享,可能涵盖了在实际项目中遇到的问题、解决方案以及最佳实践。 - **Apache MINA 线程模型配置 .shtml**:这部分内容可能...

    mina学习文档

    Mina不仅被看作是一个NIO框架,同时也常被称为客户端/服务端框架,或网络套接字类库,它提供了一套高级API来封装底层的IO操作。 #### 二、Mina的核心特性 1. **事件驱动的异步API**:Mina基于事件驱动模型,能够...

Global site tag (gtag.js) - Google Analytics